@@ -386,73 +386,233 @@ function overwrite_initial_conditions!(
386386 )
387387end
388388
389+ """
390+ correct_surface_pressure_for_topography!(
391+ p_sfc,
392+ file_path,
393+ face_space,
394+ Y,
395+ ᶜT,
396+ ᶜq_tot,
397+ thermo_params,
398+ regridder_kwargs;
399+ surface_altitude_var = "z_sfc",
400+ )
401+
402+ Adjusts the surface pressure field `p_sfc` to account for mismatches between
403+ ERA5 (file) surface altitude and the model orography when specifying pressure.
404+
405+ Δz = z_model_surface - z_sfc
406+
407+ and applies a hydrostatic correction at the surface using the local moist gas
408+ constant and temperature at the surface:
409+
410+ p_sfc .= p_sfc .* exp.(-Δz * g ./ (R_m_sfc .* T_sfc))
411+
412+ where:
413+ - `g` is gravitational acceleration from `thermo_params`
414+ - `R_m_sfc` is the moist-air gas constant evaluated from `ᶜq_tot` at the surface
415+ - `T_sfc` is the air temperature from `ᶜT` at the surface
416+
417+ Returns `true` if the correction is applied; returns `false` if the surface
418+ altitude field cannot be loaded.
419+
420+ Arguments
421+ - `p_sfc`: face field of surface pressure to be corrected (modified in-place)
422+ - `file_path`: path to the ERA5-derived initialization NetCDF file
423+ - `face_space`: face space of the model grid (for reading/regridding)
424+ - `Y`: prognostic state, used to obtain model surface height
425+ - `ᶜT`: center field of temperature
426+ - `ᶜq_tot`: center field of total specific humidity
427+ - `thermo_params`: thermodynamics parameter set
428+ - `regridder_kwargs`: keyword arguments forwarded to the regridder
429+ - `surface_altitude_var`: variable name for surface altitude (default `"z_sfc"`)
430+ """
431+ function correct_surface_pressure_for_topography! (
432+ p_sfc,
433+ file_path,
434+ face_space,
435+ Y,
436+ ᶜT,
437+ ᶜq_tot,
438+ thermo_params,
439+ regridder_kwargs;
440+ surface_altitude_var = " z_sfc" ,
441+ )
442+ regridder_type = :InterpolationsRegridder
443+ ᶠz_surface = Fields. level (
444+ SpaceVaryingInputs. SpaceVaryingInput (
445+ file_path,
446+ surface_altitude_var,
447+ face_space;
448+ regridder_type,
449+ regridder_kwargs = regridder_kwargs,
450+ ),
451+ Fields. half,
452+ )
453+
454+ if ᶠz_surface === nothing
455+ return false
456+ end
457+
458+ FT = eltype (thermo_params)
459+ grav = thermo_params. grav
460+
461+ ᶠz_model_surface = Fields. level (Fields. coordinate_field (Y. f). z, Fields. half)
462+ ᶠΔz = zeros (face_space)
463+ @. ᶠΔz = ᶠz_model_surface - ᶠz_surface
464+
465+ ᶠR_m = ᶠinterp .(TD. gas_constant_air .(thermo_params, TD. PhasePartition .(ᶜq_tot)))
466+ ᶠR_m_sfc = Fields. level (ᶠR_m, Fields. half)
467+
468+ ᶠT = ᶠinterp .(ᶜT)
469+ ᶠT_sfc = Fields. level (ᶠT, Fields. half)
470+
471+ @. p_sfc = p_sfc * exp (FT (- 1 ) * ᶠΔz * grav / (ᶠR_m_sfc * ᶠT_sfc))
472+
473+ @info " Adjusted surface pressure to account for ERA5/model surface-height differences."
474+ return true
475+ end
476+
389477# WeatherModel function using the shared implementation
478+ """
479+ overwrite_initial_conditions!(initial_condition::WeatherModel, Y, thermo_params; use_full_pressure=false)
480+
481+ Overwrite the prognostic state `Y` with ERA5-derived initial conditions on the model grid.
482+
483+
484+ - Derives the model's target vertical levels from `Y.c` (on CPU).
485+ - Obtains the ERA5-derived IC NetCDF path via `weather_model_data_path` (with
486+ any caller-provided kwargs forwarded there), then constructs `SpaceVaryingInput`
487+ fields to regrid ERA5 variables onto the model's center and face spaces.
488+ - Populates the thermodynamic state, density, velocity components, total energy,
489+ and moisture. If EDMF subdomains exist, initializes those as well.
490+
491+ Pressure initialization (`use_full_pressure`):
492+ - If `use_full_pressure == true` and the IC file contains a 3D pressure field
493+ `p_3d(lon,lat,z)`, then pressure is taken directly from `p_3d` and regridded.
494+ - Otherwise, pressure is obtained by hydrostatic integration starting from the
495+ surface pressure `p(lon,lat)` (broadcast in `z` in the IC file), using the
496+ regridded temperature `t` and specific humidity `q`. If the dataset provides
497+ surface altitude `z_sfc` (derived from ERA5 surface geopotential), the surface
498+ pressure is first corrected for model-versus-ERA5 topographic differences
499+ in order to adjust the reported ERA5 surface pressure.
500+
501+ Expected variables in the IC file:
502+ - 3D: `u`, `v`, `w`, `t`, `q` (and optionally `p_3d`, cloud water variables)
503+ - 2D broadcast in `z`: `p` (surface pressure), `skt` (skin temperature), and
504+ optionally `z_sfc` (surface altitude)
505+
506+ Notes:
507+ - When generating 3D ICs (via `to_z_levels(...; interp3d=true)` in
508+ `weather_model_data_path`), the file can include `p_3d` and a `z_physical`
509+ field on the target grid, enabling the full-pressure path described above.
510+ """
390511function overwrite_initial_conditions! (
391512 initial_condition:: WeatherModel ,
392513 Y,
393- thermo_params,
514+ thermo_params;
515+ use_full_pressure:: Bool = false ,
394516)
517+ regridder_type = :InterpolationsRegridder
518+ interpolation_method = Intp. Linear ()
395519 extrapolation_bc = (Intp. Periodic (), Intp. Flat (), Intp. Flat ())
396520
397- # Extract face coordinates and compute center midpoints
398- # Compute target levels on CPU to avoid GPU reductions
399- z_arr_cpu = Array (Fields. field2array (Fields. coordinate_field (Y. c). z))
400- icol = argmin (z_arr_cpu[1 , :])
401- target_levels = z_arr_cpu[:, icol]
521+ # target_levels defines vertical z for 1D interp,
522+ # if upstream processed ERA5 init file is missing, it is generated with to_z_levels_1d.
523+ z_arr = Array (Fields. field2array (Fields. coordinate_field (Y. c). z))
524+ z_top = round (maximum (z_arr))
525+ target_levels = collect (0.0 : 300.0 : z_top)
526+
527+ @info " Calling weather_model_data_path" (
528+ start_date = initial_condition. start_date,
529+ era5_dir = initial_condition. era5_initial_condition_dir,
530+ )
402531
403532 file_path = weather_model_data_path (
404533 initial_condition. start_date,
405534 target_levels,
406- initial_condition. era5_initial_condition_dir,
535+ initial_condition. era5_initial_condition_dir;
407536 )
408537
409- regridder_kwargs = (; extrapolation_bc)
538+ regridder_kwargs = (; extrapolation_bc, interpolation_method)
539+
410540 isfile (file_path) || error (" $(file_path) is not a file" )
411541 @info " Overwriting initial conditions with data from file $(file_path) "
412542
413543 center_space = Fields. axes (Y. c)
414544 face_space = Fields. axes (Y. f)
415545
416- # Using surface pressure, air temperature and specific humidity
417- # from the dataset, compute air pressure.
418- p_sfc = Fields. level (
419- SpaceVaryingInputs. SpaceVaryingInput (
420- file_path,
421- " p" ,
422- face_space,
423- regridder_kwargs = regridder_kwargs,
424- ),
425- Fields. half,
426- )
427546 ᶜT = SpaceVaryingInputs. SpaceVaryingInput (
428547 file_path,
429548 " t" ,
430- center_space,
549+ center_space;
550+ regridder_type,
431551 regridder_kwargs = regridder_kwargs,
432552 )
433553 ᶜq_tot = SpaceVaryingInputs. SpaceVaryingInput (
434554 file_path,
435555 " q" ,
436- center_space,
556+ center_space;
557+ regridder_type,
437558 regridder_kwargs = regridder_kwargs,
438559 )
439560
440- # With the known temperature (ᶜT) and moisture (ᶜq_tot) profile,
441- # recompute the pressure levels assuming hydrostatic balance is maintained.
442- # Uses the ClimaCore `column_integral_indefinite!` function to solve
443- # ∂(ln𝑝)/∂z = -g/(Rₘ(q)T), where
444- # p is the local pressure
445- # g is the gravitational constant
446- # q is the specific humidity
447- # Rₘ is the gas constant for moist air
448- # T is the air temperature
449- # p is then updated with the integral result, given p_sfc,
450- # following which the thermodynamic state is constructed.
451- ᶜ∂lnp∂z = @. - thermo_params. grav /
452- (TD. gas_constant_air (thermo_params, TD. PhasePartition (ᶜq_tot)) * ᶜT)
453- ᶠlnp_over_psfc = zeros (face_space)
454- Operators. column_integral_indefinite! (ᶠlnp_over_psfc, ᶜ∂lnp∂z)
455- ᶠp = p_sfc .* exp .(ᶠlnp_over_psfc)
561+ use_p3d = use_full_pressure && NC. NCDataset (file_path) do ds
562+ haskey (ds, " p_3d" )
563+ end
564+ ᶠp = if use_p3d
565+ SpaceVaryingInputs. SpaceVaryingInput (
566+ file_path,
567+ " p_3d" ,
568+ face_space;
569+ regridder_type,
570+ regridder_kwargs = regridder_kwargs,
571+ )
572+ else
573+ if use_full_pressure
574+ @warn " Requested full pressure initialization, but variable `p_3d` is missing in $(file_path) . Falling back to hydrostatic integration from surface pressure."
575+ end
576+ # Using surface pressure, air temperature and specific humidity
577+ # from the dataset, compute air pressure by hydrostatic integration.
578+ p_sfc = Fields. level (
579+ SpaceVaryingInputs. SpaceVaryingInput (
580+ file_path,
581+ " p" ,
582+ face_space;
583+ regridder_type,
584+ regridder_kwargs = regridder_kwargs,
585+ ),
586+ Fields. half,
587+ )
588+ # Apply hydrostatic surface-pressure correction only if surface altitude is available
589+ surface_altitude_var = " z_sfc"
590+ has_surface_altitude = NC. NCDataset (file_path) do ds
591+ haskey (ds, surface_altitude_var)
592+ end
593+ if has_surface_altitude
594+ correct_surface_pressure_for_topography! (
595+ p_sfc,
596+ file_path,
597+ face_space,
598+ Y,
599+ ᶜT,
600+ ᶜq_tot,
601+ thermo_params,
602+ regridder_kwargs;
603+ surface_altitude_var = surface_altitude_var,
604+ )
605+ else
606+ @warn " Skipping topographic correction because variable `$surface_altitude_var ` is missing from $(file_path) ."
607+ end
608+ # With the known temperature (ᶜT) and moisture (ᶜq_tot) profile,
609+ # recompute the pressure levels assuming hydrostatic balance is maintained.
610+ ᶜ∂lnp∂z = @. - thermo_params. grav /
611+ (TD. gas_constant_air (thermo_params, TD. PhasePartition (ᶜq_tot)) * ᶜT)
612+ ᶠlnp_over_psfc = zeros (face_space)
613+ Operators. column_integral_indefinite! (ᶠlnp_over_psfc, ᶜ∂lnp∂z)
614+ p_sfc .* exp .(ᶠlnp_over_psfc)
615+ end
456616 ᶜts = TD. PhaseEquil_pTq .(thermo_params, ᶜinterp .(ᶠp), ᶜT, ᶜq_tot)
457617
458618 # Assign prognostic variables from equilibrium moisture models
@@ -464,19 +624,22 @@ function overwrite_initial_conditions!(
464624 SpaceVaryingInputs. SpaceVaryingInput (
465625 file_path,
466626 " u" ,
467- center_space,
627+ center_space;
628+ regridder_type,
468629 regridder_kwargs = regridder_kwargs,
469630 ),
470631 SpaceVaryingInputs. SpaceVaryingInput (
471632 file_path,
472633 " v" ,
473- center_space,
634+ center_space;
635+ regridder_type,
474636 regridder_kwargs = regridder_kwargs,
475637 ),
476638 SpaceVaryingInputs. SpaceVaryingInput (
477639 file_path,
478640 " w" ,
479- center_space,
641+ center_space;
642+ regridder_type,
480643 regridder_kwargs = regridder_kwargs,
481644 ),
482645 )
@@ -508,14 +671,16 @@ function overwrite_initial_conditions!(
508671 SpaceVaryingInputs. SpaceVaryingInput (
509672 file_path,
510673 " cswc" ,
511- center_space,
674+ center_space;
675+ regridder_type,
512676 regridder_kwargs = regridder_kwargs,
513677 ) .* Y. c. ρ
514678 Y. c. ρq_rai .=
515679 SpaceVaryingInputs. SpaceVaryingInput (
516680 file_path,
517681 " crwc" ,
518- center_space,
682+ center_space;
683+ regridder_type,
519684 regridder_kwargs = regridder_kwargs,
520685 ) .* Y. c. ρ
521686 end
0 commit comments