Skip to content

Commit 39a4e0e

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 39a4e0e

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)