Skip to content

Commit 45fe601

Browse files
authored
NaNs over the ocean (#1200)
1 parent 1356084 commit 45fe601

File tree

7 files changed

+102
-76
lines changed

7 files changed

+102
-76
lines changed

NEWS.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ ClimaLand.jl Release Notes
33

44
main
55
-------
6+
- Output NaNs in diagnostics where the ocean is PR[#1200](https://github.com/CliMA/ClimaLand.jl/pull/1200)
67
- ![breaking change][badge-💥breaking] Make soil albedo parameterization modular
78
PR[#1184](https://github.com/CliMA/ClimaLand.jl/pull/1184)
89
- Use new spun up initial conditions from 19 year run PR[#1196](https://github.com/CliMA/ClimaLand.jl/pull/1196)

experiments/integrated/fluxnet/ozark_pft.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -338,7 +338,6 @@ short_names_1D = [
338338
"msf", # β
339339
"shf", # SHF
340340
"lhf", # LHF
341-
"ghf", # G
342341
"rn", # Rn
343342
]
344343
short_names_2D = [
@@ -354,7 +353,7 @@ hourly_diag_name_2D = short_names_2D .* "_1h_average"
354353
# diagnostic_as_vectors()[2] is a vector of a variable,
355354
# whereas diagnostic_as_vectors()[1] is a vector or time associated with that variable.
356355
# We index to only extract the period post-spinup.
357-
SIF, AR, g_stomata, GPP, canopy_T, SW_u, LW_u, ER, ET, β, SHF, LHF, G, Rn = [
356+
SIF, AR, g_stomata, GPP, canopy_T, SW_u, LW_u, ER, ET, β, SHF, LHF, Rn = [
358357
ClimaLand.Diagnostics.diagnostic_as_vectors(d_writer, diag_name)[2][(N_spinup_days * 24):end]
359358
for diag_name in hourly_diag_name
360359
]

experiments/integrated/fluxnet/run_fluxnet.jl

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -307,7 +307,6 @@ short_names_1D = [
307307
"msf", # β
308308
"shf", # SHF
309309
"lhf", # LHF
310-
"ghf", # G
311310
"rn", # Rn
312311
"swe",
313312
]
@@ -324,11 +323,10 @@ hourly_diag_name_2D = short_names_2D .* "_1h_average"
324323
# diagnostic_as_vectors()[2] is a vector of a variable,
325324
# whereas diagnostic_as_vectors()[1] is a vector or time associated with that variable.
326325
# We index to only extract the period post-spinup.
327-
SIF, AR, g_stomata, GPP, canopy_T, SW_u, LW_u, ER, ET, β, SHF, LHF, G, Rn, SWE =
328-
[
329-
ClimaLand.Diagnostics.diagnostic_as_vectors(d_writer, diag_name)[2][(N_spinup_days * 24):end]
330-
for diag_name in hourly_diag_name
331-
]
326+
SIF, AR, g_stomata, GPP, canopy_T, SW_u, LW_u, ER, ET, β, SHF, LHF, Rn, SWE = [
327+
ClimaLand.Diagnostics.diagnostic_as_vectors(d_writer, diag_name)[2][(N_spinup_days * 24):end]
328+
for diag_name in hourly_diag_name
329+
]
332330

333331
swc, soil_T, si = [
334332
ClimaLand.Diagnostics.diagnostic_as_vectors(

src/diagnostics/Diagnostics.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ import ClimaDiagnostics.Schedules:
2929
import ClimaDiagnostics
3030
import ClimaDiagnostics.Writers: HDF5Writer, NetCDFWriter, DictWriter
3131

32+
import ClimaCore.Fields: zeros, field_values
3233
import ClimaCore.Operators: column_integral_definite!
3334

3435
include("diagnostic.jl")

src/diagnostics/default_diagnostics.jl

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -170,7 +170,6 @@ function default_diagnostics(
170170
"rn",
171171
"lhf",
172172
"shf",
173-
"ghf",
174173
"salb",
175174
]
176175
elseif output_vars == :short
@@ -338,7 +337,6 @@ function default_diagnostics(
338337
"rn",
339338
"lhf",
340339
"shf",
341-
"ghf",
342340
"iwc",
343341
"snowc",
344342
]

src/diagnostics/define_diagnostics.jl

Lines changed: 0 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -813,17 +813,6 @@ function define_diagnostics!(land_model)
813813
compute_subsurface_runoff!(out, Y, p, t, land_model),
814814
)
815815

816-
# Ground heat flux
817-
add_diagnostic_variable!(
818-
short_name = "ghf",
819-
long_name = "Ground Heat Flux",
820-
standard_name = "ground_heat_flux",
821-
units = "W m^-2",
822-
comments = "Transfer of heat between the surface and deeper soil layers.",
823-
compute! = (out, Y, p, t) ->
824-
compute_ground_heat_flux!(out, Y, p, t, land_model),
825-
)
826-
827816
## Stored in Y (prognostic or state variables) ##
828817

829818
# Canopy temperature

src/diagnostics/land_compute_methods.jl

Lines changed: 95 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,17 @@ generates the function
1313
1414
function compute_soil_net_radiation!(out, Y, p, t, land_model::SoilCanopyModel)
1515
if isnothing(out)
16-
return copy(p.soil.R_n)
16+
out = zeros(axes(p.soil.R_n)) # Allocates
17+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
18+
out .= p.soil.R_n # set the land values only since this type of broadcasting respects the mask
19+
return out
1720
else
1821
out .= p.soil.R_n
1922
end
2023
end
24+
25+
Please note that if a land/sea mask is employed, the values
26+
over the ocean are set to NaN.
2127
"""
2228
macro diagnostic_compute(name, model, compute)
2329
function_name = Symbol("compute_", name, "!")
@@ -31,7 +37,10 @@ macro diagnostic_compute(name, model, compute)
3137
land_model::$model,
3238
)
3339
if isnothing(out)
34-
return copy($compute)
40+
out = zeros(axes($compute)) # Allocates
41+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
42+
out .= $compute # set the land values only since this type of broadcasting respects the mask
43+
return out
3544
else
3645
out .= $compute
3746
end
@@ -95,7 +104,9 @@ function compute_stomatal_conductance!(
95104
thermo_params = LP.thermodynamic_parameters(earth_param_set)
96105

97106
if isnothing(out)
98-
return medlyn_conductance.(
107+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
108+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
109+
@. out = medlyn_conductance(
99110
g0,
100111
Drel,
101112
medlyn_term.(
@@ -108,8 +119,7 @@ function compute_stomatal_conductance!(
108119
p.canopy.photosynthesis.An,
109120
p.drivers.c_co2,
110121
)
111-
112-
122+
return out
113123
else
114124
@. out = medlyn_conductance(
115125
g0,
@@ -137,7 +147,10 @@ function compute_canopy_transpiration!(
137147
# Convert to a mass flux by multiplying by the density of liquid
138148
# water
139149
if isnothing(out)
140-
return p.canopy.turbulent_fluxes.transpiration .* 1000
150+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
151+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
152+
@. out = p.canopy.turbulent_fluxes.transpiration * 1000
153+
return out
141154
else
142155
@. out = p.canopy.turbulent_fluxes.transpiration * 1000
143156
end
@@ -163,7 +176,10 @@ function compute_leaf_water_potential!(
163176
n_leaf = hydraulics.n_leaf
164177
n = n_stem + n_leaf
165178
if isnothing(out)
166-
return p.canopy.hydraulics.ψ.:($n)
179+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
180+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
181+
out .= p.canopy.hydraulics.ψ.:($n)
182+
return out
167183
else
168184
out .= p.canopy.hydraulics.ψ.:($n)
169185
end
@@ -196,7 +212,10 @@ function compute_moisture_stress_factor!(
196212
(; sc, pc) = canopy.photosynthesis.parameters
197213
ψ = p.canopy.hydraulics.ψ
198214
if isnothing(out)
199-
return @. moisture_stress(ψ.:($$n) * ρ_l * grav, sc, pc)
215+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
216+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
217+
@. out = moisture_stress(ψ.:($$n) * ρ_l * grav, sc, pc)
218+
return out
200219
else
201220
@. out = moisture_stress(ψ.:($$n) * ρ_l * grav, sc, pc)
202221
end
@@ -301,7 +320,10 @@ function compute_10cm_water_mass!(
301320
column_integral_definite!(∫Hdz, H)
302321

303322
if isnothing(out)
304-
return ∫Hθdz ./ ∫Hdz .* FT(0.1)
323+
out = zeros(land_model.soil.domain.space.surface) # Allocates
324+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
325+
@. out = ∫Hθdz / ∫Hdz * FT(0.1)
326+
return out
305327
else
306328
@. out = ∫Hθdz / ∫Hdz * FT(0.1)
307329
end
@@ -314,7 +336,10 @@ function compute_soil_albedo!(
314336
land_model::SoilCanopyModel{FT},
315337
) where {FT}
316338
if isnothing(out)
317-
return (p.soil.PAR_albedo .+ p.soil.NIR_albedo) ./ 2
339+
out = zeros(land_model.soil.domain.space.surface) # Allocates
340+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
341+
@. out = (p.soil.PAR_albedo + p.soil.NIR_albedo) / 2
342+
return out
318343
else
319344
@. out = (p.soil.PAR_albedo + p.soil.NIR_albedo) / 2
320345
end
@@ -334,7 +359,10 @@ function compute_heterotrophic_respiration!(
334359
land_model::Union{SoilCanopyModel{FT}, LandModel{FT}},
335360
) where {FT}
336361
if isnothing(out)
337-
return p.soilco2.top_bc .* FT(83.26)
362+
out = zeros(land_model.soil.domain.space.surface) # Allocates
363+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
364+
@. out = p.soilco2.top_bc * FT(83.26)
365+
return out
338366
else
339367
out .= p.soilco2.top_bc .* FT(83.26)
340368
end
@@ -360,11 +388,15 @@ function compute_evapotranspiration!(
360388
land_model::SoilCanopyModel{FT},
361389
) where {FT}
362390
if isnothing(out)
363-
return (
364-
p.soil.turbulent_fluxes.vapor_flux_liq .+
365-
p.soil.turbulent_fluxes.vapor_flux_ice .+
366-
p.canopy.turbulent_fluxes.transpiration
367-
) .* 1000 # density of liquid water (1000kg/m^3)
391+
out = zeros(land_model.soil.domain.space.surface) # Allocates
392+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
393+
@. out =
394+
(
395+
p.soil.turbulent_fluxes.vapor_flux_liq +
396+
p.soil.turbulent_fluxes.vapor_flux_ice +
397+
p.canopy.turbulent_fluxes.transpiration
398+
) * 1000 # density of liquid water (1000kg/m^3)
399+
return out
368400
else
369401
out .=
370402
(
@@ -383,14 +415,18 @@ function compute_evapotranspiration!(
383415
land_model::LandModel{FT},
384416
) where {FT}
385417
if isnothing(out)
386-
return @. (
387-
(1 - p.snow.snow_cover_fraction) *
388-
p.soil.turbulent_fluxes.vapor_flux_liq +
389-
(1 - p.snow.snow_cover_fraction) *
390-
p.soil.turbulent_fluxes.vapor_flux_ice +
391-
p.canopy.turbulent_fluxes.transpiration +
392-
p.snow.snow_cover_fraction * p.snow.turbulent_fluxes.vapor_flux
393-
) * 1000 # density of liquid water (1000kg/m^3)
418+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
419+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
420+
@. out =
421+
(
422+
(1 - p.snow.snow_cover_fraction) *
423+
p.soil.turbulent_fluxes.vapor_flux_liq +
424+
(1 - p.snow.snow_cover_fraction) *
425+
p.soil.turbulent_fluxes.vapor_flux_ice +
426+
p.canopy.turbulent_fluxes.transpiration +
427+
p.snow.snow_cover_fraction * p.snow.turbulent_fluxes.vapor_flux
428+
) * 1000 # density of liquid water (1000kg/m^3)
429+
return out
394430
else
395431
@. out =
396432
(
@@ -412,8 +448,11 @@ function compute_total_respiration!(
412448
land_model::Union{SoilCanopyModel{FT}, LandModel{FT}},
413449
) where {FT}
414450
if isnothing(out)
415-
return p.soilco2.top_bc .* FT(83.26) .+ # [3.664 kg CO2/ kg C] x [10^3 g CO2/ kg CO2] x [1 mol CO2/44.009 g CO2] = 83.26 mol CO2/kg C
416-
p.canopy.autotrophic_respiration.Ra
451+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
452+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
453+
@. out =
454+
p.soilco2.top_bc * FT(83.26) + p.canopy.autotrophic_respiration.Ra # [3.664 kg CO2/ kg C] x [10^3 g CO2/ kg CO2] x [1 mol CO2/44.009 g CO2] = 83.26 mol CO2/kg C
455+
return out
417456
else
418457
out .=
419458
p.soilco2.top_bc .* FT(83.26) .+ p.canopy.autotrophic_respiration.Ra
@@ -428,7 +467,10 @@ function compute_latent_heat_flux!(
428467
land_model::SoilCanopyModel{FT},
429468
) where {FT}
430469
if isnothing(out)
431-
return p.soil.turbulent_fluxes.lhf .+ p.canopy.turbulent_fluxes.lhf
470+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
471+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
472+
@. out = p.soil.turbulent_fluxes.lhf + p.canopy.turbulent_fluxes.lhf
473+
return out
432474
else
433475
out .= p.soil.turbulent_fluxes.lhf .+ p.canopy.turbulent_fluxes.lhf
434476
end
@@ -442,7 +484,10 @@ function compute_sensible_heat_flux!(
442484
land_model::SoilCanopyModel{FT},
443485
) where {FT}
444486
if isnothing(out)
445-
return p.soil.turbulent_fluxes.shf .+ p.canopy.turbulent_fluxes.shf
487+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
488+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
489+
@. out = p.soil.turbulent_fluxes.shf + p.canopy.turbulent_fluxes.shf
490+
return out
446491
else
447492
out .= p.soil.turbulent_fluxes.shf .+ p.canopy.turbulent_fluxes.shf
448493
end
@@ -456,10 +501,13 @@ function compute_latent_heat_flux!(
456501
land_model::LandModel{FT},
457502
) where {FT}
458503
if isnothing(out)
459-
return @. p.soil.turbulent_fluxes.lhf *
460-
(1 - p.snow.snow_cover_fraction) +
461-
p.canopy.turbulent_fluxes.lhf +
462-
p.snow.snow_cover_fraction * p.snow.turbulent_fluxes.lhf
504+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
505+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
506+
@. out =
507+
p.soil.turbulent_fluxes.lhf * (1 - p.snow.snow_cover_fraction) +
508+
p.canopy.turbulent_fluxes.lhf +
509+
p.snow.snow_cover_fraction * p.snow.turbulent_fluxes.lhf
510+
return out
463511
else
464512
@. out =
465513
p.soil.turbulent_fluxes.lhf * (1 - p.snow.snow_cover_fraction) +
@@ -476,10 +524,13 @@ function compute_sensible_heat_flux!(
476524
land_model::LandModel{FT},
477525
) where {FT}
478526
if isnothing(out)
479-
return @. p.soil.turbulent_fluxes.shf *
480-
(1 - p.snow.snow_cover_fraction) +
481-
p.canopy.turbulent_fluxes.shf +
482-
p.snow.snow_cover_fraction * p.snow.turbulent_fluxes.shf
527+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
528+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
529+
@. out =
530+
p.soil.turbulent_fluxes.shf * (1 - p.snow.snow_cover_fraction) +
531+
p.canopy.turbulent_fluxes.shf +
532+
p.snow.snow_cover_fraction * p.snow.turbulent_fluxes.shf
533+
return out
483534
else
484535
@. out =
485536
p.soil.turbulent_fluxes.shf * (1 - p.snow.snow_cover_fraction) +
@@ -496,31 +547,17 @@ function compute_net_radiation!(
496547
land_model::Union{SoilCanopyModel{FT}, LandModel{FT}},
497548
) where {FT}
498549
if isnothing(out)
499-
return p.drivers.LW_d .- p.LW_u .+ p.drivers.SW_d .- p.SW_u
550+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
551+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
552+
@. out = p.drivers.LW_d - p.LW_u + p.drivers.SW_d - p.SW_u
553+
return out
500554

501555
else
502556
out .= p.drivers.LW_d .- p.LW_u .+ p.drivers.SW_d .- p.SW_u
503557

504558
end
505559
end
506560

507-
function compute_ground_heat_flux!(
508-
out,
509-
Y,
510-
p,
511-
t,
512-
land_model::Union{SoilCanopyModel{FT}, LandModel{FT}},
513-
) where {FT}
514-
if isnothing(out)
515-
return p.soil.turbulent_fluxes.shf .+ p.canopy.turbulent_fluxes.shf .-
516-
p.soil.R_n
517-
else
518-
out .=
519-
p.soil.turbulent_fluxes.shf .+ p.canopy.turbulent_fluxes.shf .-
520-
p.soil.R_n
521-
end
522-
end
523-
524561
# variables stored in Y (prognostic or state variables)
525562
nan_if_no_canopy(T::FT, AI::FT) where {FT <: Real} = AI > 0 ? T : FT(NaN)
526563
function compute_canopy_temperature!(
@@ -535,7 +572,10 @@ function compute_canopy_temperature!(
535572
p.canopy.hydraulics.area_index.leaf +
536573
p.canopy.hydraulics.area_index.stem
537574
if isnothing(out)
538-
return nan_if_no_canopy.(Y.canopy.energy.T, AI)
575+
out = zeros(land_model.canopy.domain.space.surface) # Allocates
576+
fill!(field_values(out), NaN) # fill with NaNs, even over the ocean
577+
@. out = nan_if_no_canopy(Y.canopy.energy.T, AI)
578+
return out
539579
else
540580
out .= nan_if_no_canopy.(Y.canopy.energy.T, AI)
541581
end

0 commit comments

Comments
 (0)