@@ -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
407512end
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