ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SurfaceTemperature.H
Go to the documentation of this file.
1 #ifndef ERF_SURFACE_TEMPERATURE_H_
2 #define ERF_SURFACE_TEMPERATURE_H_
3 
4 #include <iomanip>
5 #include <sstream>
6 
7 #include <AMReX_Math.H>
8 #include <AMReX_GpuAtomic.H>
9 #include <AMReX_ParallelDescriptor.H>
10 #include <AMReX_Print.H>
11 
12 #include <ERF_Constants.H>
13 #include <ERF_EOS.H>
14 
16 
17 /**
18  * Pressure at a boundary face from the adjacent atmospheric cell state.
19  *
20  * The state pressure is diagnosed at the cell centre. Adding rho*g*delta_z
21  * gives the pressure at the boundary face, where delta_z is the signed
22  * distance from the face to the cell centre.
23  */
24 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
27  const amrex::Real rho_theta,
28  const amrex::Real qv,
29  const amrex::Real delta_z)
30 {
31  return getPgivenRTh(rho_theta, qv) + rho * CONST_GRAV * delta_z;
32 }
33 
34 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
37  const amrex::Real rho_theta,
38  const amrex::Real qv,
39  const amrex::Real delta_z)
40 {
41  return pressure_at_boundary_from_cell(rho, rho_theta, qv, delta_z);
42 }
43 
44 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
45 bool
46 valid_pressure (const amrex::Real pressure)
47 {
48  return amrex::Math::isfinite(pressure) && pressure > amrex::Real(0.0);
49 }
50 
51 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
52 bool
53 valid_temperature (const amrex::Real temperature)
54 {
55  return amrex::Math::isfinite(temperature) && temperature > amrex::Real(0.0);
56 }
57 
58 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
59 bool
61  const amrex::Real pressure,
62  const amrex::Real rdOcp,
63  amrex::Real& temperature)
64 {
65  if (!valid_pressure(pressure) || !valid_temperature(theta) ||
66  !amrex::Math::isfinite(rdOcp) || rdOcp <= amrex::Real(0.0)) {
67  return false;
68  }
69  const amrex::Real result = getTgivenPandTh(pressure, theta, rdOcp);
70  if (!valid_temperature(result)) {
71  return false;
72  }
73  temperature = result;
74  return true;
75 }
76 
77 AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE
78 bool
79 temperature_to_theta (const amrex::Real temperature,
80  const amrex::Real pressure,
81  const amrex::Real rdOcp,
83 {
84  if (!valid_pressure(pressure) || !valid_temperature(temperature) ||
85  !amrex::Math::isfinite(rdOcp) || rdOcp <= amrex::Real(0.0)) {
86  return false;
87  }
88  const amrex::Real result = getThgivenTandP(temperature, pressure, rdOcp);
89  if (!valid_temperature(result)) {
90  return false;
91  }
92  theta = result;
93  return true;
94 }
95 
96 /**
97  * Why a surface conversion could not be made, in the order the kernels test for it.
98  *
99  * Every kernel that converts between the atmospheric state and a surface temperature or
100  * humidity guards the same sequence of ways that state can be unusable. Which guard
101  * tripped is the difference between "the surface layer gave up" and knowing that the
102  * state handed to it was already bad -- so the guards are named rather than merged into
103  * a single flag.
104  */
105 enum struct ConversionError : int {
106  none = 0,
107  bad_density,
109  bad_rho_qv,
110  bad_qv,
113  bad_pressure,
115  bad_q_air,
116  bad_q_surf
117 };
118 
119 /**
120  * The first failing cell on this rank, the state that produced it, and how many cells
121  * failed.
122  *
123  * The count is the part that is easy to leave out and expensive to do without: one bad
124  * column and a whole plane of NaN state are different problems with different causes,
125  * and nothing else in the report tells them apart. Cells the fill is not authoritative
126  * for are counted separately -- they do not abort the run, but a halo full of failures
127  * beside one bad owned cell says where to look.
128  */
130 {
131  int code = static_cast<int>(ConversionError::none);
132  int count = 0;
133  int halo_count = 0;
134  int i = 0, j = 0, k = 0;
142 };
143 
144 /**
145  * Record one cell's failure.
146  *
147  * Every failing cell adds to a count; the first authoritative one to arrive also claims
148  * the description, so what gets reported is a failure that matters rather than whichever
149  * thread happened to finish last. Pass the values the caller has reached by this point:
150  * the report prints the ones that mean something for the code it is given.
151  *
152  * @param[in,out] fail This rank's record
153  * @param[in] fatal Whether this is a cell the fill is authoritative for
154  * @param[in] code Which guard tripped
155  * @param[in] i,j,k The cell whose state was read
156  */
157 AMREX_GPU_DEVICE AMREX_FORCE_INLINE
158 void
160  const bool fatal,
161  const ConversionError code,
162  const int i, const int j, const int k,
163  const amrex::Real rho = amrex::Real(0.0),
164  const amrex::Real rho_theta = amrex::Real(0.0),
165  const amrex::Real rho_qv = amrex::Real(0.0),
166  const amrex::Real t_surf = amrex::Real(0.0),
167  const amrex::Real pressure = amrex::Real(0.0),
168  const amrex::Real delta_z = amrex::Real(0.0),
169  const amrex::Real q_air = amrex::Real(0.0))
170 {
171  if (!fatal) {
172  amrex::Gpu::Atomic::Add(&fail->halo_count, 1);
173  return;
174  }
175 
176  amrex::Gpu::Atomic::Add(&fail->count, 1);
177 
178  // The first failure to arrive owns the description; the rest only add to the count.
179  constexpr int unclaimed = static_cast<int>(ConversionError::none);
180  if (amrex::Gpu::Atomic::CAS(&fail->code, unclaimed, static_cast<int>(code)) != unclaimed) {
181  return;
182  }
183 
184  fail->i = i; fail->j = j; fail->k = k;
185  fail->rho = rho;
186  fail->rho_theta = rho_theta;
187  fail->rho_qv = rho_qv;
188  fail->t_surf = t_surf;
189  fail->pressure = pressure;
190  fail->delta_z = delta_z;
191  fail->q_air = q_air;
192 }
193 
194 AMREX_FORCE_INLINE
195 const char*
197 {
198  switch (code) {
200  return "the density or (rho theta) of the state cell is not usable";
202  return "the state carries no water vapor component to convert";
204  return "the (rho qv) of the state cell is not usable";
206  return "the water vapor mixing ratio derived from the state cell is not usable";
208  return "the surface potential temperature could not be converted to absolute temperature";
210  return "the surface temperature could not be converted to potential temperature";
212  return "the surface pressure derived from the state cell is not usable";
214  return "the skin temperature is not usable";
216  return "the reference-height water vapor mixing ratio is not usable";
218  return "the surface mixing ratio came out non-finite";
219  default:
220  return "the conversion failed for a reason that was not recorded";
221  }
222 }
223 
224 /**
225  * Describe what this rank recorded, then abort if any rank recorded anything.
226  *
227  * Collective: every rank must call this. Each rank that failed describes its own first
228  * failure before the abort, so a failure confined to one rank still names the cell and
229  * the numbers rather than only the check that tripped.
230  *
231  * @param[in] who Name of the fill, used to open the message
232  * @param[in] lev Level being filled
233  * @param[in] fail This rank's record
234  */
235 inline void
237  const int lev,
238  ConversionFailure const& fail)
239 {
240  int any_failed = (fail.code != static_cast<int>(ConversionError::none)) ? 1 : 0;
241  amrex::ParallelDescriptor::ReduceIntMax(any_failed);
242  if (any_failed == 0) { return; }
243 
244  if (fail.code != static_cast<int>(ConversionError::none)) {
245  const auto code = static_cast<ConversionError>(fail.code);
246 
247  const bool is_conversion = (code == ConversionError::bad_theta_to_temperature ||
249  const bool has_state = (code != ConversionError::no_moisture_comp);
250  const bool has_vapor = (is_conversion ||
251  code == ConversionError::bad_rho_qv ||
252  code == ConversionError::bad_qv ||
253  code == ConversionError::bad_q_air ||
255  const bool has_surface = (is_conversion ||
259  const bool has_q_air = (code == ConversionError::bad_q_air ||
261 
262  // Assemble the whole report before writing it, so that ranks that failed do not
263  // interleave their lines with one another
264  std::ostringstream msg;
265  msg << who << ": surface conversion failed at level " << lev
266  << " [rank " << amrex::ParallelDescriptor::MyProc() << "]\n"
267  << " " << fail.count << " of the cells this fill owns failed";
268  if (fail.halo_count > 0) {
269  msg << " (and " << fail.halo_count << " halo cells, which are not fatal)";
270  }
271  msg << "\n first failure at (" << fail.i << "," << fail.j << "," << fail.k << "): "
272  << conversion_error_text(code) << "\n";
273 
274  if (has_state) {
275  msg << std::setprecision(10) << " rho = " << fail.rho
276  << " (rho theta) = " << fail.rho_theta;
277  if (has_vapor) { msg << " (rho qv) = " << fail.rho_qv; }
278  if (has_q_air) { msg << " q_air = " << fail.q_air; }
279  if (has_surface) {
280  msg << "\n surface temperature = " << fail.t_surf
281  << " pressure = " << fail.pressure
282  << " delta_z = " << fail.delta_z;
283  }
284  msg << "\n";
285  }
286 
287  amrex::AllPrint() << msg.str() << std::flush;
288  }
289 
290  // Let every rank finish writing before one of them tears the job down
291  amrex::ParallelDescriptor::Barrier();
292  amrex::Abort(std::string(who) + ": could not convert the surface state (see above)");
293 }
294 
295 } // namespace erf_surface_temperature
296 
297 #endif
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:68
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getThgivenTandP(const amrex::Real T, const amrex::Real P, const amrex::Real rdOcp)
Definition: ERF_EOS.H:18
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_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenPandTh(const amrex::Real P, const amrex::Real th, const amrex::Real rdOcp)
Definition: ERF_EOS.H:32
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ rho
Definition: ERF_Kessler.H:25
@ theta
Definition: ERF_Kessler.H:26
@ qv
Definition: ERF_Kessler.H:31
Definition: ERF_SurfaceTemperature.H:15
AMREX_FORCE_INLINE const char * conversion_error_text(const ConversionError code)
Definition: ERF_SurfaceTemperature.H:196
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real pressure_at_surface(const amrex::Real rho, const amrex::Real rho_theta, const amrex::Real qv, const amrex::Real delta_z)
Definition: ERF_SurfaceTemperature.H:36
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool temperature_to_theta(const amrex::Real temperature, const amrex::Real pressure, const amrex::Real rdOcp, amrex::Real &theta)
Definition: ERF_SurfaceTemperature.H:79
AMREX_GPU_DEVICE AMREX_FORCE_INLINE void record_conversion_failure(ConversionFailure *fail, const bool fatal, const ConversionError code, const int i, const int j, const int k, const amrex::Real rho=amrex::Real(0.0), const amrex::Real rho_theta=amrex::Real(0.0), const amrex::Real rho_qv=amrex::Real(0.0), const amrex::Real t_surf=amrex::Real(0.0), const amrex::Real pressure=amrex::Real(0.0), const amrex::Real delta_z=amrex::Real(0.0), const amrex::Real q_air=amrex::Real(0.0))
Definition: ERF_SurfaceTemperature.H:159
ConversionError
Definition: ERF_SurfaceTemperature.H:105
void abort_on_conversion_failure(const char *who, const int lev, ConversionFailure const &fail)
Definition: ERF_SurfaceTemperature.H:236
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real pressure_at_boundary_from_cell(const amrex::Real rho, const amrex::Real rho_theta, const amrex::Real qv, const amrex::Real delta_z)
Definition: ERF_SurfaceTemperature.H:26
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool valid_pressure(const amrex::Real pressure)
Definition: ERF_SurfaceTemperature.H:46
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool theta_to_temperature(const amrex::Real theta, const amrex::Real pressure, const amrex::Real rdOcp, amrex::Real &temperature)
Definition: ERF_SurfaceTemperature.H:60
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool valid_temperature(const amrex::Real temperature)
Definition: ERF_SurfaceTemperature.H:53
Definition: ERF_SurfaceTemperature.H:130
int count
Definition: ERF_SurfaceTemperature.H:132
amrex::Real rho_qv
Definition: ERF_SurfaceTemperature.H:137
amrex::Real rho
Definition: ERF_SurfaceTemperature.H:135
int i
Definition: ERF_SurfaceTemperature.H:134
int code
Definition: ERF_SurfaceTemperature.H:131
amrex::Real pressure
Definition: ERF_SurfaceTemperature.H:139
amrex::Real rho_theta
Definition: ERF_SurfaceTemperature.H:136
int k
Definition: ERF_SurfaceTemperature.H:134
int j
Definition: ERF_SurfaceTemperature.H:134
amrex::Real t_surf
Definition: ERF_SurfaceTemperature.H:138
amrex::Real delta_z
Definition: ERF_SurfaceTemperature.H:140
int halo_count
Definition: ERF_SurfaceTemperature.H:133
amrex::Real q_air
Definition: ERF_SurfaceTemperature.H:141