Skip to content

Commit db76fd3

Browse files
committed
Create unique overwrite for WeatherModel.
1 parent be82aa3 commit db76fd3

File tree

3 files changed

+165
-169
lines changed

3 files changed

+165
-169
lines changed

config/model_configs/diagnostic_edmfx_era5_initial_condition.yml

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ z_elem: 63
44
dz_bottom: 30.0
55
dt: 120secs
66
t_end: 12hours
7-
start_date: "20250627-0000"
7+
# start_date: "20250627-0000"
8+
start_date: "20250831"
9+
era5_initial_condition_dir: "/net/sampo/data1/wxquest_data/initial_conditions"
810
initial_condition: "WeatherModel"
911
discrete_hydrostatic_balance: false # TODO: change to true once we fix discrete hydrostatic balance to work with orography
1012
topo_smoothing: true
@@ -36,7 +38,7 @@ toml: [toml/diagnostic_edmfx_era5_ic.toml]
3638
use_itime: true
3739
output_default_diagnostics: false
3840
diagnostics:
39-
- short_name: [ts, ta, thetaa, ha, pfull, rhoa, ua, va, wa, hur, hus, cl, clw, cli, hussfc, evspsbl, pr]
41+
- short_name: [ts, ta, thetaa, ha, pfull, rhoa, ua, va, wa, hur, hus, cl, clw, cli, hussfc, evspsbl]
4042
period: 60mins
41-
- short_name: [rsut, rlut, ts, pr, mslp]
43+
- short_name: [rsut, rlut, tas, pr, mslp]
4244
period: 60mins

src/initial_conditions/initial_conditions.jl

Lines changed: 140 additions & 123 deletions
Original file line numberDiff line numberDiff line change
@@ -215,9 +215,7 @@ function (initial_condition::Union{MoistFromFile, WeatherModel})(params)
215215
grav = CAP.grav(params)
216216
thermo_params = CAP.thermodynamics_params(params)
217217

218-
# Use safe defaults to avoid transient NaNs before overwrite
219-
T = FT(CAP.T_0(params))
220-
p = FT(CAP.MSLP(params))
218+
T, p = FT(NaN), FT(NaN) # placeholder values
221219

222220
return LocalState(;
223221
params,
@@ -382,28 +380,135 @@ function overwrite_initial_conditions!(
382380
extrapolation_bc = (Intp.Periodic(), Intp.Flat(), Intp.Flat())
383381

384382
# Extract face coordinates and compute center midpoints
385-
z_coords = Fields.axes(Y.c).grid.vertical_grid.topology.mesh.faces
386383

387-
# Compute center coordinates as midpoints between faces
388-
face_z_values = [z.z for z in z_coords]
389-
center_z_values = [
390-
(face_z_values[i] + face_z_values[i + 1]) / 2 for
391-
i in 1:(length(face_z_values) - 1)
392-
]
393-
394-
target_levels = Array(center_z_values)
384+
target_levels = Fields.field2array(Fields.coordinate_field(Y.c).z)[:, argmin(Fields.field2array(Fields.coordinate_field(Y.c).z))[2]]
395385

396386
file_path = weather_model_data_path(
397387
initial_condition.start_date,
398388
target_levels,
399389
initial_condition.era5_initial_condition_dir,
400390
)
401-
return _overwrite_initial_conditions_from_file!(
391+
392+
regridder_kwargs = (; extrapolation_bc)
393+
isfile(file_path) || error("$(file_path) is not a file")
394+
@info "Overwriting initial conditions with data from file $(file_path)"
395+
396+
center_space = Fields.axes(Y.c)
397+
face_space = Fields.axes(Y.f)
398+
399+
# Using surface pressure, air temperature and specific humidity
400+
# from the dataset, compute air pressure.
401+
p_sfc = Fields.level(
402+
SpaceVaryingInputs.SpaceVaryingInput(
403+
file_path,
404+
"p",
405+
face_space,
406+
regridder_kwargs = regridder_kwargs,
407+
),
408+
Fields.half,
409+
)
410+
ᶜT = SpaceVaryingInputs.SpaceVaryingInput(
402411
file_path,
403-
extrapolation_bc,
404-
Y,
405-
thermo_params,
412+
"t",
413+
center_space,
414+
regridder_kwargs = regridder_kwargs,
415+
)
416+
ᶜq_tot = SpaceVaryingInputs.SpaceVaryingInput(
417+
file_path,
418+
"q",
419+
center_space,
420+
regridder_kwargs = regridder_kwargs,
406421
)
422+
423+
# With the known temperature (ᶜT) and moisture (ᶜq_tot) profile,
424+
# recompute the pressure levels assuming hydrostatic balance is maintained.
425+
# Uses the ClimaCore `column_integral_indefinite!` function to solve
426+
# ∂(ln𝑝)/∂z = -g/(Rₘ(q)T), where
427+
# p is the local pressure
428+
# g is the gravitational constant
429+
# q is the specific humidity
430+
# Rₘ is the gas constant for moist air
431+
# T is the air temperature
432+
# p is then updated with the integral result, given p_sfc,
433+
# following which the thermodynamic state is constructed.
434+
ᶜ∂lnp∂z = @. -thermo_params.grav /
435+
(TD.gas_constant_air(thermo_params, TD.PhasePartition(ᶜq_tot)) * ᶜT)
436+
ᶠlnp_over_psfc = zeros(face_space)
437+
Operators.column_integral_indefinite!(ᶠlnp_over_psfc, ᶜ∂lnp∂z)
438+
ᶠp = p_sfc .* exp.(ᶠlnp_over_psfc)
439+
ᶜts = TD.PhaseEquil_pTq.(thermo_params, ᶜinterp.(ᶠp), ᶜT, ᶜq_tot)
440+
441+
# Assign prognostic variables from equilibrium moisture models
442+
Y.c.ρ .= TD.air_density.(thermo_params, ᶜts)
443+
# Velocity is first assigned on cell-centers and then interpolated onto
444+
# cell faces.
445+
vel =
446+
Geometry.UVWVector.(
447+
SpaceVaryingInputs.SpaceVaryingInput(
448+
file_path,
449+
"u",
450+
center_space,
451+
regridder_kwargs = regridder_kwargs,
452+
),
453+
SpaceVaryingInputs.SpaceVaryingInput(
454+
file_path,
455+
"v",
456+
center_space,
457+
regridder_kwargs = regridder_kwargs,
458+
),
459+
SpaceVaryingInputs.SpaceVaryingInput(
460+
file_path,
461+
"w",
462+
center_space,
463+
regridder_kwargs = regridder_kwargs,
464+
),
465+
)
466+
Y.c.uₕ .= C12.(Geometry.UVVector.(vel))
467+
Y.f.u₃ .= ᶠinterp.(C3.(Geometry.WVector.(vel)))
468+
e_kin = similar(ᶜT)
469+
e_kin .= compute_kinetic(Y.c.uₕ, Y.f.u₃)
470+
e_pot = Fields.coordinate_field(Y.c).z .* thermo_params.grav
471+
Y.c.ρe_tot .= TD.total_energy.(thermo_params, ᶜts, e_kin, e_pot) .* Y.c.ρ
472+
# Initialize prognostic EDMF 0M subdomains if present
473+
if hasproperty(Y.c, :sgsʲs)
474+
ᶜmse = TD.specific_enthalpy.(thermo_params, ᶜts) .+ e_pot
475+
for name in propertynames(Y.c.sgsʲs)
476+
s = getproperty(Y.c.sgsʲs, name)
477+
hasproperty(s, :ρa) && fill!(s.ρa, 0)
478+
hasproperty(s, :mse) && (s.mse .= ᶜmse)
479+
hasproperty(s, :q_tot) && (s.q_tot .= ᶜq_tot)
480+
end
481+
end
482+
if hasproperty(Y.c, :ρq_tot)
483+
Y.c.ρq_tot .= ᶜq_tot .* Y.c.ρ
484+
else
485+
error(
486+
"`dry` configurations are incompatible with the interpolated initial conditions.",
487+
)
488+
end
489+
if hasproperty(Y.c, :ρq_sno) && hasproperty(Y.c, :ρq_rai)
490+
Y.c.ρq_sno .=
491+
SpaceVaryingInputs.SpaceVaryingInput(
492+
file_path,
493+
"cswc",
494+
center_space,
495+
regridder_kwargs = regridder_kwargs,
496+
) .* Y.c.ρ
497+
Y.c.ρq_rai .=
498+
SpaceVaryingInputs.SpaceVaryingInput(
499+
file_path,
500+
"crwc",
501+
center_space,
502+
regridder_kwargs = regridder_kwargs,
503+
) .* Y.c.ρ
504+
end
505+
506+
if hasproperty(Y.c, :sgs⁰) && hasproperty(Y.c.sgs⁰, :ρatke)
507+
# NOTE: This is not the most consistent, but it is better than NaNs
508+
fill!(Y.c.sgs⁰.ρatke, 0)
509+
end
510+
511+
return nothing
407512
end
408513

409514
"""
@@ -474,9 +579,10 @@ function _overwrite_initial_conditions_from_file!(
474579
ᶠlnp_over_psfc = zeros(face_space)
475580
Operators.column_integral_indefinite!(ᶠlnp_over_psfc, ᶜ∂lnp∂z)
476581
ᶠp = p_sfc .* exp.(ᶠlnp_over_psfc)
477-
# Compute center pressure for thermodynamic state construction
478-
ᶜp = ᶜinterp.(ᶠp)
582+
ᶜts = TD.PhaseEquil_pTq.(thermo_params, ᶜinterp.(ᶠp), ᶜT, ᶜq_tot)
479583

584+
# Assign prognostic variables from equilibrium moisture models
585+
Y.c.ρ .= TD.air_density.(thermo_params, ᶜts)
480586
# Velocity is first assigned on cell-centers and then interpolated onto
481587
# cell faces.
482588
vel =
@@ -502,125 +608,36 @@ function _overwrite_initial_conditions_from_file!(
502608
)
503609
Y.c.uₕ .= C12.(Geometry.UVVector.(vel))
504610
Y.f.u₃ .= ᶠinterp.(C3.(Geometry.WVector.(vel)))
505-
506-
# Load cloud/precip water fields if present (fallback to zeros)
507-
has_liq_ice = hasproperty(Y.c, :ρq_liq) && hasproperty(Y.c, :ρq_ice)
508-
has_precip = hasproperty(Y.c, :ρq_rai) && hasproperty(Y.c, :ρq_sno)
509-
# TEMP: diagnose 1M condensed/precip species at runtime (initialize to zero here)
510-
diagnose_1m = true
511-
512-
ᶜq_liq = zeros(center_space)
513-
ᶜq_ice = zeros(center_space)
514-
ᶜq_rai = zeros(center_space)
515-
ᶜq_sno = zeros(center_space)
516-
517-
if has_liq_ice && !diagnose_1m
518-
try
519-
ᶜq_liq = SpaceVaryingInputs.SpaceVaryingInput(
520-
file_path,
521-
"clwc",
522-
center_space,
523-
regridder_kwargs = regridder_kwargs,
524-
)
525-
catch
526-
@info "Variable clwc not found in $(file_path); initializing liquid cloud to zero"
527-
end
528-
try
529-
ᶜq_ice = SpaceVaryingInputs.SpaceVaryingInput(
530-
file_path,
531-
"ciwc",
532-
center_space,
533-
regridder_kwargs = regridder_kwargs,
534-
)
535-
catch
536-
@info "Variable ciwc not found in $(file_path); initializing ice cloud to zero"
537-
end
538-
end
539-
if has_precip && !diagnose_1m
540-
try
541-
ᶜq_rai = SpaceVaryingInputs.SpaceVaryingInput(
542-
file_path,
543-
"crwc",
544-
center_space,
545-
regridder_kwargs = regridder_kwargs,
546-
)
547-
catch
548-
@info "Variable crwc not found in $(file_path); initializing rain water to zero"
549-
end
550-
try
551-
ᶜq_sno = SpaceVaryingInputs.SpaceVaryingInput(
552-
file_path,
553-
"cswc",
554-
center_space,
555-
regridder_kwargs = regridder_kwargs,
556-
)
557-
catch
558-
@info "Variable cswc not found in $(file_path); initializing snow water to zero"
559-
end
560-
end
561-
562-
# Build thermodynamic state consistently with moisture partition
563-
is_nonequil = hasproperty(Y.c, :ρq_liq) || hasproperty(Y.c, :ρq_ice)
564-
# Use vapor from ERA5 q as first component; add cloud+precip to condensed partitions (zeros if not provided)
565-
ᶜq_vap = max.(ᶜq_tot, zero.(ᶜq_tot))
566-
ᶜq_liq .= ifelse.(isfinite.(ᶜq_liq), ᶜq_liq, zero.(ᶜq_liq))
567-
ᶜq_liq .= max.(ᶜq_liq, zero.(ᶜq_liq))
568-
ᶜq_ice .= ifelse.(isfinite.(ᶜq_ice), ᶜq_ice, zero.(ᶜq_ice))
569-
ᶜq_ice .= max.(ᶜq_ice, zero.(ᶜq_ice))
570-
ᶜq_rai .= ifelse.(isfinite.(ᶜq_rai), ᶜq_rai, zero.(ᶜq_rai))
571-
ᶜq_rai .= max.(ᶜq_rai, zero.(ᶜq_rai))
572-
ᶜq_sno .= ifelse.(isfinite.(ᶜq_sno), ᶜq_sno, zero.(ᶜq_sno))
573-
ᶜq_sno .= max.(ᶜq_sno, zero.(ᶜq_sno))
574-
ᶜpartition = TD.PhasePartition.(ᶜq_vap, ᶜq_liq .+ ᶜq_rai, ᶜq_ice .+ ᶜq_sno)
575-
if is_nonequil
576-
ᶜts = TD.PhaseNonEquil_pTq.(thermo_params, ᶜp, ᶜT, ᶜpartition)
577-
else
578-
ᶜts = TD.PhaseEquil_pTq.(thermo_params, ᶜp, ᶜT, ᶜq_tot)
579-
end
580-
581-
# Density
582-
Y.c.ρ .= TD.air_density.(thermo_params, ᶜts)
583-
584-
# Kinetic and potential energy
585611
e_kin = similar(ᶜT)
586612
e_kin .= compute_kinetic(Y.c.uₕ, Y.f.u₃)
587613
e_pot = Fields.coordinate_field(Y.c).z .* thermo_params.grav
588-
589-
# Total energy
590614
Y.c.ρe_tot .= TD.total_energy.(thermo_params, ᶜts, e_kin, e_pot) .* Y.c.ρ
591-
592-
# Moisture mass densities
593615
if hasproperty(Y.c, :ρq_tot)
594-
# Keep ρq_tot equal to ERA5 vapor (do not add precipitating species)
595616
Y.c.ρq_tot .= ᶜq_tot .* Y.c.ρ
596617
else
597618
error(
598619
"`dry` configurations are incompatible with the interpolated initial conditions.",
599620
)
600621
end
601-
602-
if hasproperty(Y.c, :ρq_rai)
603-
Y.c.ρq_rai .= ᶜq_rai .* Y.c.ρ
604-
Y.c.ρq_sno .= ᶜq_sno .* Y.c.ρ
605-
end
606-
if hasproperty(Y.c, :ρq_liq)
607-
Y.c.ρq_liq .= ᶜq_liq .* Y.c.ρ
608-
Y.c.ρq_ice .= ᶜq_ice .* Y.c.ρ
609-
end
610-
611-
# Set prognostic EDMF draft subdomains to consistent values
612-
if hasproperty(Y.c, :sgsʲs) # if prognostic EDMF
613-
ᶜmse = TD.specific_enthalpy.(thermo_params, ᶜts) .+ e_pot
614-
for name in propertynames(Y.c.sgsʲs)
615-
s = getproperty(Y.c.sgsʲs, name)
616-
hasproperty(s, :ρa) && fill!(s.ρa, 0)
617-
hasproperty(s, :mse) && (s.mse .= ᶜmse)
618-
hasproperty(s, :q_tot) && (s.q_tot .= ᶜq_vap)
619-
end
622+
if hasproperty(Y.c, :ρq_sno) && hasproperty(Y.c, :ρq_rai)
623+
Y.c.ρq_sno .=
624+
SpaceVaryingInputs.SpaceVaryingInput(
625+
file_path,
626+
"cswc",
627+
center_space,
628+
regridder_kwargs = regridder_kwargs,
629+
) .* Y.c.ρ
630+
Y.c.ρq_rai .=
631+
SpaceVaryingInputs.SpaceVaryingInput(
632+
file_path,
633+
"crwc",
634+
center_space,
635+
regridder_kwargs = regridder_kwargs,
636+
) .* Y.c.ρ
620637
end
621638

622-
# NOTE: This is not the most consistent, but it is better than NaNs
623639
if hasproperty(Y.c, :sgs⁰) && hasproperty(Y.c.sgs⁰, :ρatke)
640+
# NOTE: This is not the most consistent, but it is better than NaNs
624641
fill!(Y.c.sgs⁰.ρatke, 0)
625642
end
626643

0 commit comments

Comments
 (0)