Skip to content

Commit ca2ad88

Browse files
authored
reduce allocations in snow and neural snow (#980)
1 parent f081127 commit ca2ad88

File tree

5 files changed

+90
-71
lines changed

5 files changed

+90
-71
lines changed

ext/neural_snow/NeuralSnow.jl

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import ClimaLand.Snow:
99
density_prog_names,
1010
update_density!,
1111
update_density_prog!,
12-
snow_depth
12+
snow_depth!
1313
import ClimaLand.Parameters as LP
1414
using Thermodynamics
1515

@@ -114,14 +114,34 @@ ClimaLand.Snow.density_prog_names(m::NeuralDepthModel) =
114114
#Extend/Define the appropriate functions needed for this parameterization:
115115

116116
"""
117-
snow_depth(m::NeuralDepthModel, Y, p, params)
117+
snow_depth!(z_snow, m::NeuralDepthModel{FT}, Y, p, params) where {FT}
118118
119-
An extension of the `snow_depth` function to the NeuralDepthModel density parameterization, which includes the prognostic
119+
An extension of the `snow_depth!` function to the NeuralDepthModel density parameterization, which includes the prognostic
120120
depth variable and thus does not need to derive snow depth from SWE and density.
121-
This is sufficient to enable dynamics of the auxillary variable `ρ_snow` without extension of update_density!, and avoids
122-
redundant computations in the computation of runoff.
121+
122+
This function clips the snow depth to be between 0 and SWE.
123123
"""
124-
ClimaLand.Snow.snow_depth(m::NeuralDepthModel, Y, p, params) = Y.snow.Z
124+
function snow_depth!(z_snow, m::NeuralDepthModel{FT}, Y, p, params) where {FT}
125+
z_snow .= min(Y.snow.Z, Y.snow.S) # z cannot be larger than SWE
126+
z_snow .= max(z_snow, eps(FT)) # z must be positive
127+
return nothing
128+
end
129+
130+
"""
131+
update_density!(ρ_snow, density::NeuralDepthModel, Y, p, params::SnowParameters,)
132+
133+
Updates the snow density in place given the current model state. Default for all model types,
134+
can be extended for alternative density parameterizations.
135+
"""
136+
function update_density!(
137+
ρ_snow,
138+
density::NeuralDepthModel,
139+
Y,
140+
p,
141+
params::SnowParameters,
142+
)
143+
@. ρ_snow = snow_bulk_density(Y.snow.S, p.snow.z_snow, params)
144+
end
125145

126146

127147
"""
@@ -144,13 +164,13 @@ function eval_nn(
144164
end
145165

146166
"""
147-
dzdt(density::NeuralDepthModel, model::SnowModel{FT}, Y, p, t) where {FT}
148-
Returns the change in snow depth (rate) given the current model state and the `NeuralDepthModel`
149-
density paramterization, passing the approximate average of the forcings over the last 24 hours instead of
150-
the instantaneous value.
167+
update_dzdt!(density::NeuralDepthModel, model::SnowModel, Y, p, t)
168+
169+
Updates the dY.snow.Z field in places with the predicted change in snow depth (rate) given the model state `Y` and the `NeuralDepthModel`
170+
density paramterization.
151171
"""
152-
function dzdt(density::NeuralDepthModel, Y)
153-
return eval_nn.(
172+
function update_dzdt!(dzdt, density::NeuralDepthModel, Y)
173+
dzdt .= eval_nn(
154174
Ref(density.z_model),
155175
Y.snow.Z,
156176
Y.snow.S, # When snow-cover-fraction variable is implemented, make sure this value changes to the right input
@@ -164,6 +184,7 @@ end
164184

165185
"""
166186
clip_dZdt(S::FT, Z::FT, dSdt::FT, dZdt::FT, Δt::FT)::FT
187+
167188
A helper function which clips the tendency of Z such that
168189
its behavior is consistent with that of S: if all snow melts
169190
within a timestep, we clip the tendency of S so that it does
@@ -187,6 +208,7 @@ end
187208

188209
"""
189210
update_density_prog!(density::NeuralDepthModel, model::SnowModel, Y, p)
211+
190212
Updates all prognostic variables associated with density/depth given the current model state and the `NeuralDepthModel`
191213
density paramterization.
192214
"""
@@ -197,15 +219,16 @@ function update_density_prog!(
197219
Y,
198220
p,
199221
)
222+
update_dzdt!(dY.snow.Z, density, Y)
200223

201-
dY.snow.Z .=
202-
clip_dZdt.(
203-
Y.snow.S,
204-
Y.snow.Z,
205-
dY.snow.S, #assumes dY.snow.S is updated (and clipped) before dY.snow.Z
206-
dzdt(density, Y), # Note that the `dzdt` call below allocates a field. Return to this in the future.
207-
model.parameters.Δt,
208-
)
224+
# Now we clip the tendency so that Z stays within approximately physical bounds.
225+
@. dY.snow.Z = clip_dZdt(
226+
Y.snow.S,
227+
Y.snow.Z,
228+
dY.snow.S, #assumes dY.snow.S is updated (and clipped) before dY.snow.Z
229+
dY.snow.Z,
230+
model.parameters.Δt,
231+
)
209232

210233
@. dY.snow.P_avg = density.α * (abs(p.drivers.P_snow) - Y.snow.P_avg)
211234
@. dY.snow.T_avg =

src/integrated/soil_snow_model.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -252,8 +252,7 @@ function update_soil_snow_ground_heat_flux!(
252252
κ_soil = ClimaLand.Domains.top_center_to_surface(p.soil.κ)
253253

254254
# Depth of snow and soil layers interacting thermally at interface
255-
# Note that the `snow_depth` call below allocates a field. Return to this in the future.
256-
Δz_snow = Snow.snow_depth(snow_params.density, Y, p, snow_params) # Snow depth
255+
Δz_snow = p.snow.z_snow # Snow depth
257256
Δz_soil = p.effective_soil_sfc_depth
258257
(; ρc_ds, earth_param_set) = soil_params
259258

src/standalone/Snow/Snow.jl

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -249,7 +249,8 @@ Returns the auxiliary variable names for the snow model. These
249249
include the specific humidity at the surface of the snow `(`q_sfc`, unitless),
250250
the mass fraction in liquid water (`q_l`, unitless),
251251
the thermal conductivity (`κ`, W/m/K),
252-
the bulk temperature (`T`, K), the surface temperature (`T_sfc`, K), the bulk snow density (`ρ_snow`, kg/m^3)
252+
the bulk temperature (`T`, K), the surface temperature (`T_sfc`, K), the snow depth (`z_snow`, m),
253+
the bulk snow density (`ρ_snow`, kg/m^3)
253254
the SHF, LHF, and vapor flux (`turbulent_fluxes.shf`, etc),
254255
the net radiation (`R_n, J/m^2/s)`, the energy flux in liquid water runoff
255256
(`energy_runoff`, J/m^2/s), the water volume in runoff (`water_runoff`, m/s), and the total energy and water fluxes applied to the snowpack.
@@ -265,6 +266,7 @@ auxiliary_vars(::SnowModel) = (
265266
,
266267
:T,
267268
:T_sfc,
269+
:z_snow,
268270
:ρ_snow,
269271
:turbulent_fluxes,
270272
:R_n,
@@ -284,6 +286,7 @@ auxiliary_types(::SnowModel{FT}) where {FT} = (
284286
FT,
285287
FT,
286288
FT,
289+
FT,
287290
NamedTuple{(:lhf, :shf, :vapor_flux, :r_ae), Tuple{FT, FT, FT, FT}},
288291
FT,
289292
FT,
@@ -311,6 +314,7 @@ auxiliary_domain_names(::SnowModel) = (
311314
:surface,
312315
:surface,
313316
:surface,
317+
:surface,
314318
)
315319

316320

@@ -319,8 +323,8 @@ ClimaLand.name(::SnowModel) = :snow
319323
function ClimaLand.make_update_aux(model::SnowModel{FT}) where {FT}
320324
function update_aux!(p, Y, t)
321325
parameters = model.parameters
322-
323-
update_density!(parameters.density, parameters, Y, p)
326+
snow_depth!(p.snow.z_snow, model.parameters.density, Y, p, parameters)
327+
update_density!(p.snow.ρ_snow, parameters.density, Y, p, parameters)
324328

325329
@. p.snow.κ = snow_thermal_conductivity(p.snow.ρ_snow, parameters)
326330

@@ -338,16 +342,14 @@ function ClimaLand.make_update_aux(model::SnowModel{FT}) where {FT}
338342
p.drivers.thermal_state,
339343
parameters,
340344
)
341-
342-
p.snow.water_runoff .=
343-
compute_water_runoff.(
344-
Y.snow.S,
345-
p.snow.q_l,
346-
p.snow.T,
347-
p.snow.ρ_snow,
348-
snow_depth(model.parameters.density, Y, p, parameters), # Note that the `snow_depth` call below allocates a field. Return to this in the future.
349-
parameters,
350-
)
345+
@. p.snow.water_runoff = compute_water_runoff(
346+
Y.snow.S,
347+
p.snow.q_l,
348+
p.snow.T,
349+
p.snow.ρ_snow,
350+
p.snow.z_snow,
351+
parameters,
352+
)
351353

352354
@. p.snow.energy_runoff =
353355
p.snow.water_runoff * volumetric_internal_energy_liq(FT, parameters)

src/standalone/Snow/snow_parameterizations.jl

Lines changed: 20 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
export snow_surface_temperature,
2-
snow_depth,
2+
snow_depth!,
33
specific_heat_capacity,
44
snow_thermal_conductivity,
55
snow_bulk_temperature,
@@ -24,19 +24,6 @@ function snow_cover_fraction(x::FT; α = FT(1e-3))::FT where {FT}
2424
return heaviside(x - α)
2525
end
2626

27-
"""
28-
snow_depth(model::AbstractDensityModel{FT}, Y, p, params) where {FT}
29-
30-
Returns the snow depth given SWE, snow density ρ_snow, and
31-
the density of liquid water ρ_l.
32-
This can be extended for additional types of parameterizations.
33-
"""
34-
function snow_depth(density::AbstractDensityModel{FT}, Y, p, params) where {FT}
35-
ρ_l = FT(LP.ρ_cloud_liq(params.earth_param_set))
36-
return @. ρ_l * Y.snow.S / p.snow.ρ_snow
37-
end
38-
39-
4027
"""
4128
ClimaLand.surface_height(
4229
model::SnowModel{FT},
@@ -393,41 +380,43 @@ function energy_from_T_and_swe(S::FT, T::FT, parameters) where {FT}
393380

394381
end
395382

383+
396384
"""
397-
update_density!(density::AbstractDensityModel, params::SnowParameters, Y, p)
398-
Updates the snow density given the current model state. Default for all model types,
399-
can be extended for alternative density paramterizations.
385+
snow_depth!(model::ConstantDensityModel, Y, p, params)
386+
387+
Returns the snow depth given SWE, snow density ρ_snow, and
388+
the density of liquid water ρ_l for a constant density model.
400389
"""
401-
function update_density!(
402-
density::AbstractDensityModel,
403-
params::SnowParameters,
404-
Y,
405-
p,
406-
)
407-
p.snow.ρ_snow .=
408-
snow_bulk_density.(Y.snow.S, snow_depth(density, Y, p, params), params)
390+
function snow_depth!(z_snow, density::ConstantDensityModel, Y, p, params)
391+
ρ_l = LP.ρ_cloud_liq(params.earth_param_set)
392+
@. z_snow = ρ_l * Y.snow.S / density.ρ_snow
393+
return nothing
409394
end
410395

411396
"""
412-
update_density!(density::ConstantDensityModel, params::SnowParameters, Y, p)
413-
Extends the update_density! function for the ConstantDensityModel type.
397+
update_density!(ρ_snow, density::ConstantDensityModel, Y, p, params::SnowParameters)
398+
399+
Extends the update_density! function for the ConstantDensityModel type; updates the snow density in place.
414400
"""
415401
function update_density!(
402+
ρ_snow,
416403
density::ConstantDensityModel,
417-
params::SnowParameters,
418404
Y,
419405
p,
406+
params::SnowParameters,
420407
)
421-
p.snow.ρ_snow .= density.ρ_snow
408+
ρ_snow .= density.ρ_snow
422409
end
423410

424411
"""
425412
update_density_prog!(density::AbstractDensityModel{FT}, model::SnowModel{FT}, Y, p) where {FT}
413+
426414
Updates all prognostic variables associated with density/depth given the current model state.
427-
This is the default method for all density model types, which can be extended for alternative paramterizations.
415+
This is the default method for the constant density model,
416+
which has no prognostic variables.
428417
"""
429418
function update_density_prog!(
430-
density::AbstractDensityModel,
419+
density::ConstantDensityModel,
431420
model::SnowModel,
432421
dY,
433422
Y,

test/standalone/Snow/snow.jl

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,7 @@ import ClimaLand.Parameters as LP
5858
,
5959
:T,
6060
:T_sfc,
61+
:z_snow,
6162
:ρ_snow,
6263
:turbulent_fluxes,
6364
:R_n,
@@ -139,12 +140,17 @@ import ClimaLand.Parameters as LP
139140
@test turb_fluxes_copy.lhf == p.snow.turbulent_fluxes.lhf
140141
@test turb_fluxes_copy.vapor_flux == p.snow.turbulent_fluxes.vapor_flux
141142
old_ρ = deepcopy(p.snow.ρ_snow)
142-
Snow.update_density!(model.parameters.density, model.parameters, Y, p)
143+
Snow.update_density!(
144+
p.snow.ρ_snow,
145+
model.parameters.density,
146+
Y,
147+
p,
148+
model.parameters,
149+
)
143150
@test p.snow.ρ_snow == old_ρ
144-
old_z = similar(Y.snow.S)
145-
old_z .= FT(0.5)
146-
z = snow_depth(model.parameters.density, Y, p, parameters)
147-
@test z == old_z
151+
old_z = deepcopy(p.snow.z_snow)
152+
snow_depth!(p.snow.z_snow, model.parameters.density, Y, p, parameters)
153+
@test p.snow.z_snow == old_z
148154

149155
# Now compute tendencies and make sure they operate correctly.
150156
dY = similar(Y)

0 commit comments

Comments
 (0)