ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SeaLevelPressure.H
Go to the documentation of this file.
1 #ifndef ERF_SEA_LEVEL_PRESSURE_H_
2 #define ERF_SEA_LEVEL_PRESSURE_H_
3 
4 #include <AMReX_GpuQualifiers.H>
5 #include <AMReX_MultiFab.H>
6 
7 #include "ERF_Constants.H"
8 #include "ERF_EOS.H"
9 
10 /**
11  * NMC/NGM sea-level-pressure reduction with the Shuell correction.
12  *
13  * The algorithm and empirical parameters are adapted from NOAA-EMC UPP,
14  * sorc/ncep_post.fd/NGMSLP.f, commit
15  * 1ee7b175c340a566287a987870d925e4708fe19b.
16  *
17  * This ERF implementation uses ERF's native thermodynamic constants and
18  * reconstructs ground-interface pressure from the lowest cell-centered
19  * atmospheric state. It is therefore an adaptation of the UPP algorithm,
20  * not a bit-for-bit reproduction of UPP output.
21  */
22 
24 {
25 
26 // Parameters inherited from NOAA-EMC UPP's NMC/NGM reduction. They are not
27 // ERF thermodynamic constants and intentionally remain local to this module.
28 constexpr amrex::Real lapse_rate = amrex::Real(0.0065); // [K m^-1]
31 constexpr amrex::Real sea_level_height = amrex::Real(0.0); // [m]
33 
34 /** Convert ERF dry-air water-vapor mixing ratio to specific humidity.
35  *
36  * @param[in] vapor_mixing_ratio Water vapor mass per unit dry-air mass [kg/kg].
37  * @return Specific humidity used by the NMC/NGM reduction [kg/kg].
38  * @pre The mixing ratio is finite and nonnegative.
39  */
40 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
43 {
45 }
46 
47 /** Compute virtual temperature using ERF's native gas constants.
48  *
49  * @param[in] temperature Physical temperature [K].
50  * @param[in] specific_humidity Specific humidity [kg/kg].
51  * @return Virtual temperature [K].
52  */
53 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
55  amrex::Real specific_humidity) noexcept
56 {
57  // UPP uses rounded 0.608; ERF uses the equivalent coefficient from its
58  // shared R_d and R_v so the diagnostic remains EOS-consistent.
59  return temperature * (amrex::Real(1.0) + epsv * specific_humidity);
60 }
61 
62 /** Apply the NMC/NGM Shuell correction to the sea-level virtual temperature.
63  *
64  * The strict/non-strict comparisons reproduce NGMSLP.f exactly. The quadratic
65  * temperature form is algebraically equivalent to UPP's tau-space form.
66  *
67  * @param[in] virtual_temperature_surface Ground-interface virtual temperature [K].
68  * @param[in] virtual_temperature_sea_level Uncorrected sea-level virtual temperature [K].
69  * @return Corrected sea-level virtual temperature [K].
70  */
71 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
72 amrex::Real apply_shuell_correction (amrex::Real virtual_temperature_surface,
73  amrex::Real virtual_temperature_sea_level) noexcept
74 {
75  if (virtual_temperature_sea_level > shuell_critical_temperature &&
76  virtual_temperature_surface <= shuell_critical_temperature) {
78  } else if (virtual_temperature_sea_level > shuell_critical_temperature &&
79  virtual_temperature_surface > shuell_critical_temperature) {
82  (virtual_temperature_surface - shuell_critical_temperature) *
83  (virtual_temperature_surface - shuell_critical_temperature);
84  }
85  return virtual_temperature_sea_level;
86 }
87 
88 /** Reconstruct pressure at the lower terrain interface from the lowest cell.
89  *
90  * @param[in] pressure_lowest_level Lowest cell-center pressure [Pa].
91  * @param[in] virtual_temperature_lowest_level Lowest cell-center virtual temperature [K].
92  * @param[in] virtual_temperature_surface Ground-interface virtual temperature [K].
93  * @param[in] height_lowest_level Lowest cell-center physical height [m].
94  * @param[in] height_surface Ground-interface physical height [m].
95  * @return Ground-interface pressure [Pa].
96  */
97 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
99  amrex::Real pressure_lowest_level,
100  amrex::Real virtual_temperature_lowest_level,
101  amrex::Real virtual_temperature_surface,
102  amrex::Real height_lowest_level,
103  amrex::Real height_surface) noexcept
104 {
105  const amrex::Real mean_tau = (R_d / CONST_GRAV) *
106  (virtual_temperature_lowest_level + virtual_temperature_surface) /
107  amrex::Real(2.0);
108  return pressure_lowest_level * std::exp(
109  (height_lowest_level - height_surface) / mean_tau);
110 }
111 
112 /** Reconstruct ground-interface pressure and reduce it to physical z=0.
113  *
114  * @param[in] surface_pressure Ground-interface pressure [Pa].
115  * @param[in] temperature_lowest_level Lowest cell-center temperature [K].
116  * @param[in] specific_humidity Lowest-level specific humidity [kg/kg].
117  * @param[in] height_lowest_level Lowest cell-center physical height [m].
118  * @param[in] height_surface Ground-interface physical height [m].
119  * @return Pressure reduced to the physical z=0 datum [Pa].
120  */
121 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
123  amrex::Real surface_pressure,
124  amrex::Real temperature_lowest_level,
125  amrex::Real specific_humidity,
126  amrex::Real height_lowest_level,
127  amrex::Real height_surface) noexcept
128 {
129  const amrex::Real virtual_temperature_lowest =
130  virtual_temperature(temperature_lowest_level, specific_humidity);
131  const amrex::Real virtual_temperature_surface =
132  virtual_temperature_lowest + lapse_rate *
133  (height_lowest_level - height_surface);
134  const amrex::Real virtual_temperature_sea_level =
135  virtual_temperature_lowest + lapse_rate *
136  (height_lowest_level - sea_level_height);
137  const amrex::Real corrected_virtual_temperature_sea_level =
138  apply_shuell_correction(virtual_temperature_surface,
139  virtual_temperature_sea_level);
140  const amrex::Real mean_tau_surface_sea_level =
141  (R_d / CONST_GRAV) *
142  (virtual_temperature_surface + corrected_virtual_temperature_sea_level) /
143  amrex::Real(2.0);
144 
145  if (amrex::Math::abs(height_surface - sea_level_height) <=
147  return surface_pressure;
148  }
149  return surface_pressure * std::exp(
150  (height_surface - sea_level_height) / mean_tau_surface_sea_level);
151 }
152 
153 /** Compute the complete NMC/NGM reduction from an ERF conserved state.
154  *
155  * ERF stores dry density, dry density times potential temperature, and (when
156  * active) dry-density times vapor mixing ratio. The pressure and temperature
157  * are diagnosed through ERF's EOS, then pressure is reconstructed at the
158  * ground interface before the method-specific sea-level reduction.
159  *
160  * @param[in] dry_density Lowest-level dry-air density [kg/m^3].
161  * @param[in] dry_density_times_potential_temperature Lowest-level rho_d theta [kg K/m^3].
162  * @param[in] vapor_mixing_ratio Lowest-level vapor mixing ratio [kg/kg dry air].
163  * @param[in] height_lowest_level Lowest cell-center physical height [m].
164  * @param[in] height_surface Ground-interface physical height [m].
165  * @return Pressure reduced to physical z=0 [Pa].
166  * @pre State and heights are finite and physically valid; use zero mixing ratio for dry runs.
167  */
168 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
170  amrex::Real dry_density,
171  amrex::Real dry_density_times_potential_temperature,
173  amrex::Real height_lowest_level,
174  amrex::Real height_surface) noexcept
175 {
176  const amrex::Real specific_humidity =
178  const amrex::Real pressure_lowest =
179  getPgivenRTh(dry_density_times_potential_temperature,
181  const amrex::Real temperature_lowest =
182  getTgivenRandRTh(dry_density,
183  dry_density_times_potential_temperature,
185  const amrex::Real virtual_temperature_lowest =
186  virtual_temperature(temperature_lowest, specific_humidity);
187  const amrex::Real virtual_temperature_surface =
188  virtual_temperature_lowest + lapse_rate *
189  (height_lowest_level - height_surface);
190  const amrex::Real pressure_surface = reconstruct_surface_pressure(
191  pressure_lowest, virtual_temperature_lowest, virtual_temperature_surface,
192  height_lowest_level, height_surface);
193 
195  pressure_surface, temperature_lowest, specific_humidity,
196  height_lowest_level, height_surface);
197 }
198 
199 /** Fill one selected 2D field from each column's lowest ERF state.
200  *
201  * @param[out] destination 2D destination MultiFab; only destination_component is written.
202  * @param[in] destination_component Component to fill in destination.
203  * @param[in] conserved_state Cell-centered ERF conserved state MultiFab.
204  * @param[in] physical_nodal_height Absolute physical nodal heights [m].
205  * @param[in] vapor_component Conserved vapor component, or -1 for a dry run.
206  * @param[in] klo Lowest valid source-state vertical index for this AMR level.
207  */
208 void fill_sea_level_pressure (amrex::MultiFab& destination,
209  int destination_component,
210  const amrex::MultiFab& conserved_state,
211  const amrex::MultiFab& physical_nodal_height,
212  int vapor_component,
213  int klo);
214 
215 } // namespace sea_level_pressure_diagnostics
216 
217 #endif
constexpr amrex::Real epsv
Definition: ERF_Constants.H:53
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:64
constexpr amrex::Real R_d
Definition: ERF_Constants.H:47
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getPgivenRTh(const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:81
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_FORCE_INLINE AMREX_GPU_HOST_DEVICE Real vapor_mixing_ratio(const Real p_b, const Real T_b, const Real RH, const bool use_empirical, int which_zone)
Definition: ERF_HSEUtils.H:462
Definition: ERF_SeaLevelPressure.cpp:12
void fill_sea_level_pressure(MultiFab &destination, int destination_component, const MultiFab &conserved_state, const MultiFab &physical_nodal_height, int vapor_component, int klo)
Definition: ERF_SeaLevelPressure.cpp:14
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real apply_shuell_correction(amrex::Real virtual_temperature_surface, amrex::Real virtual_temperature_sea_level) noexcept
Definition: ERF_SeaLevelPressure.H:72
constexpr amrex::Real sea_level_height
Definition: ERF_SeaLevelPressure.H:31
constexpr amrex::Real shuell_quadratic_coefficient
Definition: ERF_SeaLevelPressure.H:30
constexpr amrex::Real shuell_critical_temperature
Definition: ERF_SeaLevelPressure.H:29
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real virtual_temperature(amrex::Real temperature, amrex::Real specific_humidity) noexcept
Definition: ERF_SeaLevelPressure.H:54
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real reduce_surface_pressure_to_sea_level(amrex::Real surface_pressure, amrex::Real temperature_lowest_level, amrex::Real specific_humidity, amrex::Real height_lowest_level, amrex::Real height_surface) noexcept
Definition: ERF_SeaLevelPressure.H:122
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real specific_humidity_from_dry_mixing_ratio(amrex::Real vapor_mixing_ratio) noexcept
Definition: ERF_SeaLevelPressure.H:41
constexpr amrex::Real surface_height_deadband
Definition: ERF_SeaLevelPressure.H:32
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real reconstruct_surface_pressure(amrex::Real pressure_lowest_level, amrex::Real virtual_temperature_lowest_level, amrex::Real virtual_temperature_surface, amrex::Real height_lowest_level, amrex::Real height_surface) noexcept
Definition: ERF_SeaLevelPressure.H:98
constexpr amrex::Real lapse_rate
Definition: ERF_SeaLevelPressure.H:28
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real compute_from_erf_lowest_level_state(amrex::Real dry_density, amrex::Real dry_density_times_potential_temperature, amrex::Real vapor_mixing_ratio, amrex::Real height_lowest_level, amrex::Real height_surface) noexcept
Definition: ERF_SeaLevelPressure.H:169