@@ -447,73 +447,233 @@ function overwrite_initial_conditions!(
447447 )
448448end
449449
450+ """
451+ correct_surface_pressure_for_topography!(
452+ p_sfc,
453+ file_path,
454+ face_space,
455+ Y,
456+ ᶜT,
457+ ᶜq_tot,
458+ thermo_params,
459+ regridder_kwargs;
460+ surface_altitude_var = "z_sfc",
461+ )
462+
463+ Adjusts the surface pressure field `p_sfc` to account for mismatches between
464+ ERA5 (file) surface altitude and the model orography when specifying pressure.
465+
466+ Δz = z_model_surface - z_sfc
467+
468+ and applies a hydrostatic correction at the surface using the local moist gas
469+ constant and temperature at the surface:
470+
471+ p_sfc .= p_sfc .* exp.(-Δz * g ./ (R_m_sfc .* T_sfc))
472+
473+ where:
474+ - `g` is gravitational acceleration from `thermo_params`
475+ - `R_m_sfc` is the moist-air gas constant evaluated from `ᶜq_tot` at the surface
476+ - `T_sfc` is the air temperature from `ᶜT` at the surface
477+
478+ Returns `true` if the correction is applied; returns `false` if the surface
479+ altitude field cannot be loaded.
480+
481+ Arguments
482+ - `p_sfc`: face field of surface pressure to be corrected (modified in-place)
483+ - `file_path`: path to the ERA5-derived initialization NetCDF file
484+ - `face_space`: face space of the model grid (for reading/regridding)
485+ - `Y`: prognostic state, used to obtain model surface height
486+ - `ᶜT`: center field of temperature
487+ - `ᶜq_tot`: center field of total specific humidity
488+ - `thermo_params`: thermodynamics parameter set
489+ - `regridder_kwargs`: keyword arguments forwarded to the regridder
490+ - `surface_altitude_var`: variable name for surface altitude (default `"z_sfc"`)
491+ """
492+ function correct_surface_pressure_for_topography! (
493+ p_sfc,
494+ file_path,
495+ face_space,
496+ Y,
497+ ᶜT,
498+ ᶜq_tot,
499+ thermo_params,
500+ regridder_kwargs;
501+ surface_altitude_var = " z_sfc" ,
502+ )
503+ regridder_type = :InterpolationsRegridder
504+ ᶠz_surface = Fields. level (
505+ SpaceVaryingInputs. SpaceVaryingInput (
506+ file_path,
507+ surface_altitude_var,
508+ face_space;
509+ regridder_type,
510+ regridder_kwargs = regridder_kwargs,
511+ ),
512+ Fields. half,
513+ )
514+
515+ if ᶠz_surface === nothing
516+ return false
517+ end
518+
519+ FT = eltype (thermo_params)
520+ grav = thermo_params. grav
521+
522+ ᶠz_model_surface = Fields. level (Fields. coordinate_field (Y. f). z, Fields. half)
523+ ᶠΔz = zeros (face_space)
524+ @. ᶠΔz = ᶠz_model_surface - ᶠz_surface
525+
526+ ᶠR_m = ᶠinterp .(TD. gas_constant_air .(thermo_params, TD. PhasePartition .(ᶜq_tot)))
527+ ᶠR_m_sfc = Fields. level (ᶠR_m, Fields. half)
528+
529+ ᶠT = ᶠinterp .(ᶜT)
530+ ᶠT_sfc = Fields. level (ᶠT, Fields. half)
531+
532+ @. p_sfc = p_sfc * exp (FT (- 1 ) * ᶠΔz * grav / (ᶠR_m_sfc * ᶠT_sfc))
533+
534+ @info " Adjusted surface pressure to account for ERA5/model surface-height differences."
535+ return true
536+ end
537+
450538# WeatherModel function using the shared implementation
539+ """
540+ overwrite_initial_conditions!(initial_condition::WeatherModel, Y, thermo_params; use_full_pressure=false)
541+
542+ Overwrite the prognostic state `Y` with ERA5-derived initial conditions on the model grid.
543+
544+
545+ - Derives the model's target vertical levels from `Y.c` (on CPU).
546+ - Obtains the ERA5-derived IC NetCDF path via `weather_model_data_path` (with
547+ any caller-provided kwargs forwarded there), then constructs `SpaceVaryingInput`
548+ fields to regrid ERA5 variables onto the model's center and face spaces.
549+ - Populates the thermodynamic state, density, velocity components, total energy,
550+ and moisture. If EDMF subdomains exist, initializes those as well.
551+
552+ Pressure initialization (`use_full_pressure`):
553+ - If `use_full_pressure == true` and the IC file contains a 3D pressure field
554+ `p_3d(lon,lat,z)`, then pressure is taken directly from `p_3d` and regridded.
555+ - Otherwise, pressure is obtained by hydrostatic integration starting from the
556+ surface pressure `p(lon,lat)` (broadcast in `z` in the IC file), using the
557+ regridded temperature `t` and specific humidity `q`. If the dataset provides
558+ surface altitude `z_sfc` (derived from ERA5 surface geopotential), the surface
559+ pressure is first corrected for model-versus-ERA5 topographic differences
560+ in order to adjust the reported ERA5 surface pressure.
561+
562+ Expected variables in the IC file:
563+ - 3D: `u`, `v`, `w`, `t`, `q` (and optionally `p_3d`, cloud water variables)
564+ - 2D broadcast in `z`: `p` (surface pressure), `skt` (skin temperature), and
565+ optionally `z_sfc` (surface altitude)
566+
567+ Notes:
568+ - When generating 3D ICs (via `to_z_levels(...; interp3d=true)` in
569+ `weather_model_data_path`), the file can include `p_3d` and a `z_physical`
570+ field on the target grid, enabling the full-pressure path described above.
571+ """
451572function overwrite_initial_conditions! (
452573 initial_condition:: WeatherModel ,
453574 Y,
454- thermo_params,
575+ thermo_params;
576+ use_full_pressure:: Bool = false ,
455577)
578+ regridder_type = :InterpolationsRegridder
579+ interpolation_method = Intp. Linear ()
456580 extrapolation_bc = (Intp. Periodic (), Intp. Flat (), Intp. Flat ())
457581
458- # Extract face coordinates and compute center midpoints
459- # Compute target levels on CPU to avoid GPU reductions
460- z_arr_cpu = Array (Fields. field2array (Fields. coordinate_field (Y. c). z))
461- icol = argmin (z_arr_cpu[1 , :])
462- target_levels = z_arr_cpu[:, icol]
582+ # target_levels defines vertical z for 1D interp,
583+ # if upstream processed ERA5 init file is missing, it is generated with to_z_levels_1d.
584+ z_arr = Array (Fields. field2array (Fields. coordinate_field (Y. c). z))
585+ z_top = round (maximum (z_arr))
586+ target_levels = collect (0.0 : 300.0 : z_top)
587+
588+ @info " Calling weather_model_data_path" (
589+ start_date = initial_condition. start_date,
590+ era5_dir = initial_condition. era5_initial_condition_dir,
591+ )
463592
464593 file_path = weather_model_data_path (
465594 initial_condition. start_date,
466595 target_levels,
467- initial_condition. era5_initial_condition_dir,
596+ initial_condition. era5_initial_condition_dir;
468597 )
469598
470- regridder_kwargs = (; extrapolation_bc)
599+ regridder_kwargs = (; extrapolation_bc, interpolation_method)
600+
471601 isfile (file_path) || error (" $(file_path) is not a file" )
472602 @info " Overwriting initial conditions with data from file $(file_path) "
473603
474604 center_space = Fields. axes (Y. c)
475605 face_space = Fields. axes (Y. f)
476606
477- # Using surface pressure, air temperature and specific humidity
478- # from the dataset, compute air pressure.
479- p_sfc = Fields. level (
480- SpaceVaryingInputs. SpaceVaryingInput (
481- file_path,
482- " p" ,
483- face_space,
484- regridder_kwargs = regridder_kwargs,
485- ),
486- Fields. half,
487- )
488607 ᶜT = SpaceVaryingInputs. SpaceVaryingInput (
489608 file_path,
490609 " t" ,
491- center_space,
610+ center_space;
611+ regridder_type,
492612 regridder_kwargs = regridder_kwargs,
493613 )
494614 ᶜq_tot = SpaceVaryingInputs. SpaceVaryingInput (
495615 file_path,
496616 " q" ,
497- center_space,
617+ center_space;
618+ regridder_type,
498619 regridder_kwargs = regridder_kwargs,
499620 )
500621
501- # With the known temperature (ᶜT) and moisture (ᶜq_tot) profile,
502- # recompute the pressure levels assuming hydrostatic balance is maintained.
503- # Uses the ClimaCore `column_integral_indefinite!` function to solve
504- # ∂(ln𝑝)/∂z = -g/(Rₘ(q)T), where
505- # p is the local pressure
506- # g is the gravitational constant
507- # q is the specific humidity
508- # Rₘ is the gas constant for moist air
509- # T is the air temperature
510- # p is then updated with the integral result, given p_sfc,
511- # following which the thermodynamic state is constructed.
512- ᶜ∂lnp∂z = @. - thermo_params. grav /
513- (TD. gas_constant_air (thermo_params, TD. PhasePartition (ᶜq_tot)) * ᶜT)
514- ᶠlnp_over_psfc = zeros (face_space)
515- Operators. column_integral_indefinite! (ᶠlnp_over_psfc, ᶜ∂lnp∂z)
516- ᶠp = p_sfc .* exp .(ᶠlnp_over_psfc)
622+ use_p3d = use_full_pressure && NC. NCDataset (file_path) do ds
623+ haskey (ds, " p_3d" )
624+ end
625+ ᶠp = if use_p3d
626+ SpaceVaryingInputs. SpaceVaryingInput (
627+ file_path,
628+ " p_3d" ,
629+ face_space;
630+ regridder_type,
631+ regridder_kwargs = regridder_kwargs,
632+ )
633+ else
634+ if use_full_pressure
635+ @warn " Requested full pressure initialization, but variable `p_3d` is missing in $(file_path) . Falling back to hydrostatic integration from surface pressure."
636+ end
637+ # Using surface pressure, air temperature and specific humidity
638+ # from the dataset, compute air pressure by hydrostatic integration.
639+ p_sfc = Fields. level (
640+ SpaceVaryingInputs. SpaceVaryingInput (
641+ file_path,
642+ " p" ,
643+ face_space;
644+ regridder_type,
645+ regridder_kwargs = regridder_kwargs,
646+ ),
647+ Fields. half,
648+ )
649+ # Apply hydrostatic surface-pressure correction only if surface altitude is available
650+ surface_altitude_var = " z_sfc"
651+ has_surface_altitude = NC. NCDataset (file_path) do ds
652+ haskey (ds, surface_altitude_var)
653+ end
654+ if has_surface_altitude
655+ correct_surface_pressure_for_topography! (
656+ p_sfc,
657+ file_path,
658+ face_space,
659+ Y,
660+ ᶜT,
661+ ᶜq_tot,
662+ thermo_params,
663+ regridder_kwargs;
664+ surface_altitude_var = surface_altitude_var,
665+ )
666+ else
667+ @warn " Skipping topographic correction because variable `$surface_altitude_var ` is missing from $(file_path) ."
668+ end
669+ # With the known temperature (ᶜT) and moisture (ᶜq_tot) profile,
670+ # recompute the pressure levels assuming hydrostatic balance is maintained.
671+ ᶜ∂lnp∂z = @. - thermo_params. grav /
672+ (TD. gas_constant_air (thermo_params, TD. PhasePartition (ᶜq_tot)) * ᶜT)
673+ ᶠlnp_over_psfc = zeros (face_space)
674+ Operators. column_integral_indefinite! (ᶠlnp_over_psfc, ᶜ∂lnp∂z)
675+ p_sfc .* exp .(ᶠlnp_over_psfc)
676+ end
517677 ᶜts = TD. PhaseEquil_pTq .(thermo_params, ᶜinterp .(ᶠp), ᶜT, ᶜq_tot)
518678
519679 # Assign prognostic variables from equilibrium moisture models
@@ -525,19 +685,22 @@ function overwrite_initial_conditions!(
525685 SpaceVaryingInputs. SpaceVaryingInput (
526686 file_path,
527687 " u" ,
528- center_space,
688+ center_space;
689+ regridder_type,
529690 regridder_kwargs = regridder_kwargs,
530691 ),
531692 SpaceVaryingInputs. SpaceVaryingInput (
532693 file_path,
533694 " v" ,
534- center_space,
695+ center_space;
696+ regridder_type,
535697 regridder_kwargs = regridder_kwargs,
536698 ),
537699 SpaceVaryingInputs. SpaceVaryingInput (
538700 file_path,
539701 " w" ,
540- center_space,
702+ center_space;
703+ regridder_type,
541704 regridder_kwargs = regridder_kwargs,
542705 ),
543706 )
@@ -569,14 +732,16 @@ function overwrite_initial_conditions!(
569732 SpaceVaryingInputs. SpaceVaryingInput (
570733 file_path,
571734 " cswc" ,
572- center_space,
735+ center_space;
736+ regridder_type,
573737 regridder_kwargs = regridder_kwargs,
574738 ) .* Y. c. ρ
575739 Y. c. ρq_rai .=
576740 SpaceVaryingInputs. SpaceVaryingInput (
577741 file_path,
578742 " crwc" ,
579- center_space,
743+ center_space;
744+ regridder_type,
580745 regridder_kwargs = regridder_kwargs,
581746 ) .* Y. c. ρ
582747 end
0 commit comments