Skip to content

Commit 49206ad

Browse files
committed
add state NaN checker
Adds a function that quantitatively checks how many NaNs are present in the state, and displays this information to the user. This can be used to inspect sol.u[end] after a simulation has run, to see if any NaNs were produced at the end of the simulation. If no NaNs are found, this information is logged in an info statement. If NaNs are found, this information is logged in a warn statement.
1 parent c44b057 commit 49206ad

File tree

2 files changed

+76
-1
lines changed

2 files changed

+76
-1
lines changed

src/shared_utilities/utils.jl

Lines changed: 32 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import SciMLBase
33
import ClimaDiagnostics.Schedules: EveryCalendarDtSchedule
44
import Dates
55

6-
export FTfromY
6+
export FTfromY, count_nans_state
77

88
"""
99
heaviside(x::FT)::FT where {FT}
@@ -492,3 +492,34 @@ function isdivisible(
492492
# have any common divisor)
493493
return isinteger(Dates.Day(1) / dt_small)
494494
end
495+
496+
"""
497+
count_nans_state(sol)
498+
499+
Count the number of NaNs in the state variables. This function is useful for
500+
debugging simulations to determine quantitatively if a simulation is stable.
501+
502+
If this function is called on a FieldVector, it will recursively call itself
503+
on each Field in the FieldVector. If it is called on a Field, it will count
504+
the number of NaNs in the Field and produce a warning if any are found.
505+
506+
Input: `state` - e.g. the FieldVector given by `sol.u[end]` after calling `solve`
507+
"""
508+
function count_nans_state(state::ClimaCore.Fields.FieldVector)
509+
for pn in propertynames(state)
510+
state_new = getproperty(state, pn)
511+
@info "Checking NaNs in $pn"
512+
count_nans_state(state_new)
513+
end
514+
return nothing
515+
end
516+
517+
function count_nans_state(state::ClimaCore.Fields.Field)
518+
num_nans = count(isnan.(Array(parent(state))))
519+
if num_nans > 0
520+
@warn "$num_nans NaNs found"
521+
else
522+
@info "No NaNs found"
523+
end
524+
return nothing
525+
end

test/shared_utilities/utilities.jl

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -256,3 +256,47 @@ end
256256
@test Y.subfields.subfield2 == Y_copy.subfields.subfield2
257257
end
258258
end
259+
260+
@testset "count_nans_state, FT = $FT" begin
261+
# Test on a 3D spherical domain
262+
domain = ClimaLand.Domains.SphericalShell(;
263+
radius = FT(2),
264+
depth = FT(1.0),
265+
nelements = (10, 5),
266+
npolynomial = 3,
267+
)
268+
269+
# Construct some fields
270+
space = domain.space.subsurface
271+
var1 = Fields.zeros(space)
272+
var2 = Fields.zeros(space)
273+
var3 = Fields.zeros(space)
274+
fieldvec = Fields.FieldVector(var2 = var2, var3 = var3)
275+
276+
# Construct a FieldVector containing the fields and a nested FieldVector
277+
Y = Fields.FieldVector(var1 = var1, fieldvec = fieldvec)
278+
279+
# Count and log the number of NaNs in the state
280+
@test_logs (:info, "Checking NaNs in var1") (:info, "No NaNs found") (
281+
:info,
282+
"Checking NaNs in fieldvec",
283+
) (:info, "Checking NaNs in var2") (:info, "No NaNs found") (
284+
:info,
285+
"Checking NaNs in var3",
286+
) (:info, "No NaNs found") ClimaLand.count_nans_state(Y)
287+
288+
# Add some NaNs to the fields
289+
parent(var1)[1] = NaN
290+
parent(var2)[1] = NaN
291+
parent(var2)[2] = NaN
292+
293+
# Count and log the number of NaNs in the state
294+
@test_logs (:info, "Checking NaNs in var1") (:warn, "1 NaNs found") (
295+
:info,
296+
"Checking NaNs in fieldvec",
297+
) (:info, "Checking NaNs in var2") (:warn, "2 NaNs found") (
298+
:info,
299+
"Checking NaNs in var3",
300+
) (:info, "No NaNs found") ClimaLand.count_nans_state(Y)
301+
302+
end

0 commit comments

Comments
 (0)