ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_SurfaceLayer.H
Go to the documentation of this file.
1 #ifndef ERF_SURFACELAYER_H
2 #define ERF_SURFACELAYER_H
3 
4 #include <fstream>
5 #include <sstream>
6 #include <string>
7 
8 #include "AMReX_Geometry.H"
9 #include "AMReX_ParmParse.H"
10 #include "AMReX_FArrayBox.H"
11 #include "AMReX_MultiFab.H"
12 #include "AMReX_iMultiFab.H"
13 #include "AMReX_MFInterpolater.H"
14 
15 #include "ERF_IndexDefines.H"
16 #include "ERF_Constants.H"
17 #include "ERF_MOSTAverage.H"
18 #include "ERF_PlanarBoundary.H"
19 #include "ERF_MOSTStress.H"
20 #include "ERF_MOSTUtils.H"
21 #include "ERF_EBMOSTStress.H"
23 #include "ERF_TerrainMetrics.H"
24 #include "ERF_PBLHeight.H"
25 #include "ERF_MicrophysicsUtils.H"
26 #include "ERF_EB.H"
27 
28 namespace erf_surface_layer {
29 
30 inline bool
31 planar_sources_supported_for_terrain (TerrainType terrain_type,
32  bool use_sst,
33  bool use_tsk,
34  bool use_coupled_sst,
35  bool has_lsm_tsurf,
36  bool has_lsm_fluxes,
37  bool has_custom_roughness)
38 {
39  return terrain_type != TerrainType::EB ||
40  !(use_sst || use_tsk || use_coupled_sst || has_lsm_tsurf ||
41  has_lsm_fluxes || has_custom_roughness);
42 }
43 
44 } // namespace erf_surface_layer
45 
46 
47 /** Abstraction layer for different surface layer schemes (e.g. MOST, Cd)
48  *
49  * van der Laan, P., Kelly, M. C., & Sørensen, N. N. (2017). A new k-epsilon
50  * model consistent with Monin-Obukhov similarity theory. Wind Energy,
51  * 20(3), 479–amrex::Real(489.) https://doi.org/amrex::Real(10.1002)/we.2017
52  *
53  * Consistent with Dyer (1974) formulation from page 57, Chapter 2, Modeling
54  * the vertical ABL structure in Modelling of Atmospheric Flow Fields,
55  * Demetri P Lalas and Corrado F Ratto, January 1996,
56  * https://doi.org/amrex::Real(10.1142)/amrex::Real(2975.)
57  */
59 {
60 
61 public:
62  /**
63  * Construct the surface-layer interface.
64  *
65  * @param[in] face orientation of face (for wall geometries)
66  * @param[in] geom geometry for all AMR levels
67  * @param[in,out] use_rot_surface_flux whether to use rotated surface fluxes
68  * @param[in] a_pp_prefix ParmParse prefix used by MOST averages
69  * @param[in] Qv_prim primitive water-vapor fields by level
70  * @param[in] z_phys_nd nodal physical-height fields by level
71  * @param[in] zlevels_stag nominal staggered z levels by level
72  * @param[in] a_mesh_type mesh type
73  * @param[in] a_terrain_type terrain representation
74  * @param[in] a_turb_choice turbulence-model options
75  * @param[in] a_rdOcp configured Rd/cp exponent for T-theta conversions
76  * @param[in] start_low_time first available low-boundary-data time
77  * @param[in] final_low_time final available low-boundary-data time
78  * @param[in] low_time_interval low-boundary-data time interval
79  * @param[in] eb_vec optional embedded-boundary geometry data
80  */
81  explicit SurfaceLayer (amrex::Orientation face,
82  const amrex::Vector<amrex::Geometry>& geom,
83  bool& use_rot_surface_flux,
84  std::string a_pp_prefix,
85  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& Qv_prim,
86  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& z_phys_nd,
87  const amrex::Vector<amrex::Vector<amrex::Real>>& zlevels_stag,
88  const MeshType& a_mesh_type,
89  const TerrainType& a_terrain_type,
90  const TurbChoice& a_turb_choice,
91  amrex::Real a_rdOcp,
92  double start_low_time,
93  double final_low_time,
94  double low_time_interval = 0.0,
95  const amrex::Vector<const eb_*>& eb_vec = {})
96  : m_face(face),
97  m_pp_prefix(a_pp_prefix),
98  m_geom(geom),
99  m_rotate(use_rot_surface_flux),
100  m_start_low_time(start_low_time),
101  m_final_low_time(final_low_time),
102  m_low_time_interval(low_time_interval),
103  m_eb_vec(eb_vec),
104  m_terrain_type(a_terrain_type),
105  m_rdOcp(a_rdOcp),
106  m_ma(face, geom, (z_phys_nd[0] != nullptr), a_pp_prefix, a_mesh_type, a_terrain_type,
107  zlevels_stag, eb_vec)
108  {
109  // We have a moisture model if Qv_prim is a valid pointer
110  use_moisture = (Qv_prim[0].get());
111 
112  // Keep standalone SurfaceLayer instances compatible with the historical
113  // single-face behavior. ERF replaces this with the complete active-face
114  // set after constructing all SurfaceLayer objects.
115  m_surface_layer_faces[static_cast<int>(face)] = 1;
116 
117  // Get roughness
118  amrex::ParmParse pp(a_pp_prefix);
119  pp.queryAdd("most.z0", z0_const);
120 
121  // Specify how to compute the flux
122  if (use_rot_surface_flux) {
124  } else {
125  std::string flux_string_in;
126  std::string flux_string{"moeng"};
127  auto read_flux = pp.queryAdd("surface_layer.flux_type", flux_string_in);
128  if (read_flux) {
129  flux_string = amrex::toLower(flux_string_in);
130  }
131  if (flux_string == "moeng") {
133  } else if (flux_string == "rico") {
135  } else if (flux_string == "bulk_coeff") {
137  } else if (flux_string == "custom") {
139  } else {
140  amrex::Abort("Undefined MOST flux type!");
141  }
142  }
143 
148  m_face.coordDir() == 2 && m_face.isLow(),
149  "BULK_COEFF, CUSTOM, and RICO surface-layer fluxes are supported only on the z-low face.");
150  }
151 
152  // Include w* to handle free convection (Beljaars 1995, QJRMS)
153  pp.queryAdd("most.include_wstar", m_include_wstar);
154 
155  std::string pblh_string_in;
156  std::string pblh_string{"none"};
157  auto read_pblh = pp.queryAdd("most.pblh_calc", pblh_string_in);
158  if (read_pblh) {
159  pblh_string = amrex::toLower(pblh_string_in);
160  }
161  if (pblh_string == "none") {
163  } else if (pblh_string == "mynn25") {
165  } else if (pblh_string == "mynnedmf") {
167  } else if (pblh_string == "ysu") {
169  } else if (pblh_string == "mrf") {
171  } else {
172  amrex::Abort("Undefined PBLH calc type!");
173  }
174 
177  m_face.coordDir() == 2 && m_face.isLow(),
178  "MOST PBL-height calculation and wstar correction are supported only on the z-low face.");
179  }
180 
181  // The w* correction is computed from the PBL height, so it needs a scheme that
182  // actually diagnoses one. With pblh_calc = "none" the pblh MultiFab keeps the
183  // bogus_large_value it was initialized with and calc_wstar turns that into a
184  // convective velocity scale of ~1e50, which destroys the surface fluxes.
186  amrex::Abort("erf.most.include_wstar requires a PBL height: set "
187  "erf.most.pblh_calc (MYNN25 is the only scheme implemented)");
188  }
189 
190  // Get surface temperature. surf_temp and surf_moist are declared with negative
191  // sentinels (see below) so that "did the user set this" is a property of the
192  // value rather than of the queryAdd return value, which only reports whether the
193  // key existed before the call and so stops being meaningful once anything has
194  // parsed the key. Both most.surf_temp and most.surf_moist are also parsed by
195  // ERF_InputSoundingData.H, so the two sites would poison each other otherwise.
196  pp.queryAdd("most.surf_temp", surf_temp);
197  const bool erf_st = (surf_temp > amrex::Real(0));
198  if (erf_st) { default_land_surf_temp = surf_temp; }
199 
200  // Get surface moisture
201  bool erf_sq = false;
202  if (use_moisture) {
203  pp.queryAdd("most.surf_moist", surf_moist);
204  erf_sq = (surf_moist >= amrex::Real(0));
205  }
206  if (erf_sq) { default_land_surf_moist = surf_moist; }
207 
208  // Custom type user must specify the fluxes
213  pp.get("most.ustar", custom_ustar);
214  pp.get("most.tstar", custom_tstar);
215  pp.get("most.qstar", custom_qstar);
216  pp.queryAdd("most.rhosurf", custom_rhosurf);
217  if (custom_qstar != 0) {
219  "Specified custom MOST qv flux without moisture model!");
220  }
221  amrex::Print() << "Using specified ustar, tstar, qstar for MOST = "
222  << custom_ustar << " " << custom_tstar << " "
223  << custom_qstar << std::endl;
224 
225  // Bulk transfer coefficient (must specify coeffs and surface values)
226  } else if (flux_type == FluxCalcType::BULK_COEFF) {
227  pp.get("most.Cd", m_Cd);
228  pp.get("most.Ch", m_Ch);
229  pp.get("most.Cq", m_Cq);
230  pp.get("most.surf_temp", default_land_surf_temp);
231  pp.get("most.surf_moist", default_land_surf_moist);
232  amrex::Print() << "Using specified Cd, Ch, Cq for MOST = "
233  << m_Cd << " " << m_Ch << " "
234  << m_Cq << std::endl;
235 
236  // Specify surface temperature/moisture or surface flux
237  } else {
238  if (erf_st) {
240  pp.queryAdd("most.surf_heating_rate", surf_heating_rate); // [K/h]
241 
242  // Modify rate to be in units of K / s rather than K / hr
243  surf_heating_rate /= amrex::Real(3600.0); // [K/s]
244 
245  if (pp.query("most.surf_temp_flux", surf_temp_flux)) {
246  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
247  }
248  } else {
249  pp.queryAdd("most.surf_temp_flux", surf_temp_flux);
250 
251  if (pp.query("most.surf_heating_rate", surf_heating_rate)) {
252  amrex::Abort("Can only specify one of surf_temp_flux or surf_heating_rate");
253  }
254  if (std::abs(surf_temp_flux) >
257  } else {
259  }
260  }
261 
262  if (erf_sq) {
264  } else {
265  pp.queryAdd("most.surf_moist_flux", surf_moist_flux);
266  if (std::abs(surf_moist_flux) >
269  } else {
271  }
272  }
273  }
274 
279  m_face.coordDir() == 2 && m_face.isLow(),
280  "HEAT_FLUX and ADIABATIC surface-layer fluxes are supported only on the z-low face.");
281  }
282 
284  {
285  pp.queryAdd("most.rico.theta_z0", rico_theta_z0);
286  pp.queryAdd("most.rico.qsat_z0", rico_qsat_z0);
287  }
288 
289  // Make sure the inputs file doesn't try to use most.roughness_type
290  std::string bogus_input;
291  if (pp.queryAdd("most.roughness_type", bogus_input) > 0) {
292  amrex::Abort("most.roughness_type is deprecated; use "
293  "most.roughness_type_land and/or most.roughness_type_sea");
294  }
295 
296  // Specify how to compute the surface flux over land (if there is any)
297  std::string rough_land_string_in;
298  std::string rough_land_string{"constant"};
299  auto read_rough_land =
300  pp.queryAdd("most.roughness_type_land", rough_land_string_in);
301  if (read_rough_land) {
302  rough_land_string = amrex::toLower(rough_land_string_in);
303  }
304  if (rough_land_string == "constant") {
306  } else {
307  amrex::Abort("Undefined MOST roughness type for land!");
308  }
309 
310  // Allow for smooth-flow limit?
311  pp.queryAdd("most.smooth_flow_viscosity", smooth_flow_visc);
312  amrex::Print() << "The smooth-flow limit will be included for variable roughness models over sea: " << smooth_flow_visc << "\n";
313 
314  // Specify how to compute the surface flux over sea (if there is any)
315  std::string rough_sea_string_in;
316  std::string rough_sea_string{"charnock"};
317  auto read_rough_sea = pp.queryAdd("most.roughness_type_sea", rough_sea_string_in);
318  if (read_rough_sea) {
319  rough_sea_string = amrex::toLower(rough_sea_string_in);
320  }
321  if (rough_sea_string == "charnock") {
323  pp.queryAdd("most.charnock_constant", cnk_a);
324  if (cnk_a > 0) {
325  amrex::Print() << "If there is water, Charnock relation with C_a="
326  << cnk_a << " will be used" << std::endl;
327  } else {
328  amrex::Print() << "If there is water, Charnock relation with variable "
329  "Charnock parameter (COARE3.0) will be used" << std::endl;
330  }
331  } else if (rough_sea_string == "coare3.0") {
333  amrex::Print() << "If there is water, Charnock relation with variable "
334  "Charnock parameter (COARE3.0) will be used" << std::endl;
335  cnk_a = -1;
336  } else if (rough_sea_string == "donelan") {
338  } else if (rough_sea_string == "modified_charnock") {
340  pp.queryAdd("most.modified_charnock_depth", depth);
341  if (depth < amrex::Real(10.0) || depth > amrex::Real(100.0) ) {
342  amrex::Print() << "Specified depth of " << depth
343  << " is outside the valid range of [10 100], resetting to bounds now."
344  << std::endl;
345  }
346  // Limiter based upon the fit range in Jiménez & Dudhia
347  depth = amrex::min(amrex::max(depth,amrex::Real(10.0)),amrex::Real(100.0));
348  } else if (rough_sea_string == "wave_coupled") {
350  } else if (rough_sea_string == "constant") {
352  } else {
353  amrex::Abort("Undefined MOST roughness type for sea!");
354  }
355 
356  // use skin temperature instead of sea-surface temperature
357  // (wrfinput data may have lower resolution SST data)
358  pp.queryAdd("most.ignore_sst", m_ignore_sst);
359 
360  // If we're using the RANS k model, then we need to update the dirichlet
361  // wall value of k (written into the first cell above the wall and held
362  // there through the step) based on the instantaneous u* and θ*; the turbulence modeling
363  // choices can vary per level but for now, assume that if specified then
364  // all levels are using the same RANS model.
365  m_update_k_rans = (a_turb_choice.rans_type == RANSType::kEqn &&
366  a_turb_choice.dirichlet_k == true);
367  if (m_update_k_rans) {
369  m_face.coordDir() == 2 && m_face.isLow(),
370  "RANS surface-layer k updates are supported only on the z-low face.");
371  }
372  if (m_update_k_rans) {
373  inv_Cmu2 = one / (a_turb_choice.Cmu0 * a_turb_choice.Cmu0);
374  theta_ref = a_turb_choice.theta_ref;
375  }
376 
377  } // constructor
378 
379  /**
380  * Allocate and initialize surface-layer data for one AMR level.
381  *
382  * @param[in] lev level index
383  * @param[in] nlevs number of AMR levels
384  * @param[in] mfv conserved and velocity MultiFabs for this level
385  * @param[in] Theta_prim primitive potential-temperature field
386  * @param[in] Qv_prim primitive water-vapor field
387  * @param[in] Qr_prim primitive rain-water field
388  * @param[in] z_phys_nd nodal physical-height field
389  * @param[in] Hwave wave-height field
390  * @param[in] Lwave wavelength field
391  * @param[in] eddyDiffs eddy-diffusivity field
392  * @param[in] lsm_data land-surface-model data fields
393  * @param[in] lsm_data_name names for lsm_data entries
394  * @param[in] lsm_flux land-surface-model flux fields
395  * @param[in] lsm_flux_name names for lsm_flux entries
396  * @param[in] sst_lev sea-surface-temperature data by time
397  * @param[in] tsk_lev skin-temperature data by time
398  * @param[in] lmask_lev land-mask data by time
399  */
400  void make_SurfaceLayer_at_level (const int& lev,
401  int nlevs,
402  const amrex::Vector<amrex::MultiFab*>& mfv,
403  std::unique_ptr<amrex::MultiFab>& Theta_prim,
404  std::unique_ptr<amrex::MultiFab>& Qv_prim,
405  std::unique_ptr<amrex::MultiFab>& Qr_prim,
406  std::unique_ptr<amrex::MultiFab>& z_phys_nd,
407  amrex::MultiFab* Hwave,
408  amrex::MultiFab* Lwave,
409  amrex::MultiFab* eddyDiffs,
410  amrex::Vector<amrex::MultiFab*> lsm_data,
411  amrex::Vector<std::string> lsm_data_name,
412  amrex::Vector<amrex::MultiFab*> lsm_flux,
413  amrex::Vector<std::string> lsm_flux_name,
414  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& sst_lev,
415  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& tsk_lev,
416  amrex::Vector<std::unique_ptr<amrex::iMultiFab>>& lmask_lev)
417  {
418  // Update MOST Average
420  Theta_prim, Qv_prim, Qr_prim,
421  z_phys_nd);
422 
423  // Get CC vars
424  amrex::MultiFab& mf = *(mfv[0]);
425 
426  amrex::ParmParse pp(m_pp_prefix);
427 
428  // Do we have a time-varying surface roughness that needs to be saved?
429  if (lev == 0) {
430  const int nghost = 0; // ghost cells not included
431  int lmask_min = lmask_min_reduce(*lmask_lev[0].get(), nghost);
432  amrex::ParallelDescriptor::ReduceIntMin(lmask_min);
433 
434  m_var_z0 = (lmask_min < 1) & (rough_type_sea != RoughCalcType::CONSTANT);
435  if (m_var_z0) {
437  m_face.coordDir() == 2 && m_face.isLow(),
438  "Variable sea roughness surface-layer fluxes are supported only on the z-low face.");
439 
440  std::string rough_sea_string{"charnock"};
441  pp.queryAdd("most.roughness_type_sea", rough_sea_string);
442  amrex::Print() << "Variable sea roughness (type " << rough_sea_string
443  << ")" << std::endl;
444  }
445  }
446 
447  if (m_eddyDiffs_lev.size() < lev+1) {
448  m_Hwave_lev.resize(nlevs);
449  m_Lwave_lev.resize(nlevs);
450  m_eddyDiffs_lev.resize(nlevs);
451 
452  m_lsm_data_lev.resize(nlevs);
453  m_lsm_flux_lev.resize(nlevs);
454 
455  m_sst_lev.resize(nlevs);
456  m_tsk_lev.resize(nlevs);
457  m_lmask_lev.resize(nlevs);
458 
459  m_coupled_sst_lev.resize(nlevs, nullptr);
460  m_coupled_sst_valid_lev.resize(nlevs, nullptr);
461 
462  // Size the MOST params for all levels
463  z_0.resize(nlevs);
464  u_star.resize(nlevs);
465  w_star.resize(nlevs);
466  t_star.resize(nlevs);
467  q_star.resize(nlevs);
468  t_surf.resize(nlevs);
469  q_surf.resize(nlevs);
470  surface_diagnostic_source.resize(nlevs);
471  olen.resize(nlevs);
472  pblh.resize(nlevs);
473  m_planar_bndry.resize(nlevs);
474  }
475 
476  // Get pointers to SST,TSK and LANDMASK data
477  int nt_tot_sst = sst_lev.size();
478  m_sst_lev[lev].resize(nt_tot_sst);
479  for (int nt(0); nt < nt_tot_sst; ++nt) {
480  m_sst_lev[lev][nt] = sst_lev[nt].get();
481  }
482  int nt_tot_tsk = static_cast<int>(tsk_lev.size());
483  m_tsk_lev[lev].resize(nt_tot_tsk);
484  for (int nt(0); nt < nt_tot_tsk; ++nt) {
485  m_tsk_lev[lev][nt] = tsk_lev[nt].get();
486  }
487  int nt_tot_lmask = static_cast<int>(lmask_lev.size());
488  m_lmask_lev[lev].resize(nt_tot_lmask);
489  for (int nt(0); nt < nt_tot_lmask; ++nt) {
490  m_lmask_lev[lev][nt] = lmask_lev[nt].get();
491  }
492 
493  // Get pointers to wave data
494  m_Hwave_lev[lev] = Hwave;
495  m_Lwave_lev[lev] = Lwave;
496  m_eddyDiffs_lev[lev] = eddyDiffs;
497 
498  // Text-file driven surface forcing modes. The file always contains at
499  // least time(day) and absolute sst(K); the value is normalized to the
500  // canonical SurfaceLayer theta field at the z-low surface.
501  pp.queryAdd("most.use_sfc_fluxes", m_use_sfc_fluxes);
502  pp.queryAdd("most.use_sfc_sst", m_use_sfc_sst);
504  amrex::Abort("Only one of most.use_sfc_fluxes and most.use_sfc_sst may be enabled");
505  }
507  if (m_terrain_type == TerrainType::EB) {
508  amrex::Abort("Text-file surface forcing is not supported with EB terrain");
509  }
511  m_face.coordDir() == 2 && m_face.isLow(),
512  "most.use_sfc_fluxes and most.use_sfc_sst are supported only on the z-low face.");
513 
514  // load sensible and latent heat fluxes from sfc to prescribe
515  std::string sfc_file = "";
516  pp.queryAdd("most.sfc_file", sfc_file);
517  if (sfc_file.empty()) {
518  amrex::Abort("most.sfc_file must be set when using text-file surface forcing");
519  }
520 
521  // sfc contains: time(day) sst(K) H(W/m2) LE(W/m2) TAU(m2/s2)
522  sfc = read_cols(sfc_file, 1);
523 
524  const int min_cols = m_use_sfc_fluxes ? 5 : 2;
525  if (static_cast<int>(sfc.size()) < min_cols) {
526  amrex::Abort("Surface forcing file does not contain the required number of columns");
527  }
528 
529  // shift time column in days to be relative to current elapsed time
530  const amrex::Real start_day = sfc[0][0];
531  for (int i = 0; i < static_cast<int>(sfc[0].size()); ++i) {
532  sfc[0][i] = 86400.0 * (sfc[0][i] - start_day);
533  }
534 
535  if (m_use_sfc_sst) {
537  amrex::Abort("most.use_sfc_sst cannot be combined with prescribed heat flux or surf_heating_rate");
538  }
540  amrex::Abort("most.use_sfc_sst cannot be combined with prescribed moisture flux");
541  }
543  amrex::Print() << "Using MOST with prescribed SST from most.sfc_file '" << sfc_file << "' over sea" << std::endl;
544  }
545 
546  if (m_use_sfc_fluxes) {
548  amrex::Print() << "Using MOST with prescribed time-varying surface fluxes from '" << sfc_file << "'" << std::endl;
549  }
550  }
551 
552  // Get pointers to LSM data and Fluxes
553  int ndata = static_cast<int>(lsm_data.size());
554  int nflux = static_cast<int>(lsm_flux.size());
555  m_lsm_data_name.resize(ndata);
556  m_lsm_data_lev[lev].resize(ndata);
557  m_lsm_flux_name.resize(nflux);
558  m_lsm_flux_lev[lev].resize(nflux);
559  for (int n(0); n < ndata; ++n) {
560  m_lsm_data_name[n] = lsm_data_name[n];
561  m_lsm_data_lev[lev][n] = lsm_data[n];
562  const std::string lc_name = amrex::toLower(lsm_data_name[n]);
563  if (lc_name == "theta" || lc_name == "t_surf") {
564  m_has_lsm_tsurf = true;
565  m_lsm_tsurf_indx = n;
566  }
567  }
569  amrex::Abort("most.use_sfc_sst cannot be combined with an ocean LSM t_surf input");
570  }
571  int n_valid_lsm_flux = 0;
572  bool has_soil_t_flux = false;
573  for (int n(0); n < nflux; ++n) {
574  m_lsm_flux_name[n] = lsm_flux_name[n];
575  m_lsm_flux_lev[lev][n] = lsm_flux[n];
576  if (m_lsm_flux_lev[lev][n]) { ++n_valid_lsm_flux; }
577  if (amrex::toLower(m_lsm_flux_name[n]) == "soil_t_flux") {
578  has_soil_t_flux = true;
579  }
580  }
581  AMREX_ALWAYS_ASSERT((n_valid_lsm_flux==0 || n_valid_lsm_flux>=4 ||
582  (n_valid_lsm_flux==1 && has_soil_t_flux)));
583  if (n_valid_lsm_flux>=4) { m_has_lsm_fluxes = true; }
584 
585  const bool use_sst = (!m_sst_lev[lev].empty() && m_sst_lev[lev][0]);
586  const bool use_tsk = (!m_tsk_lev[lev].empty() && m_tsk_lev[lev][0]);
587 
588  // Check if there is a user-specified roughness file to be read
589  std::string fname;
590  bool read_z0 = false;
591  if ( (flux_type == FluxCalcType::MOENG) ||
593  int count = pp.countval("most.roughness_file_name");
594  if (count > 1) {
595  AMREX_ALWAYS_ASSERT(count >= lev+1);
596  pp.query("most.roughness_file_name", fname, lev);
597  read_z0 = true;
598  } else if (count == 1) {
599  if (lev == 0) {
600  pp.queryAdd("most.roughness_file_name", fname);
601  } else {
602  // we will interpolate from the coarsest level
603  fname = "";
604  }
605  read_z0 = true;
606  }
607  // else use z0_const
608  }
609  if (read_z0) {
611  m_face.coordDir() == 2 && m_face.isLow(),
612  "Custom MOST roughness is supported only on the z-low face.");
613  }
614 
615  // LSM flux arrays are planar and compute_sfc_params_from_lsm_fluxes only
616  // writes the k=0 slab, while EB MOST consumes surface parameters at
617  // arbitrary cut-cell k; no valid planar-to-cut-cell mapping exists yet.
619  m_terrain_type, use_sst, use_tsk, m_use_coupled_sst,
620  m_has_lsm_tsurf, m_has_lsm_fluxes, read_z0)) {
621  amrex::Abort(
622  "EB SurfaceLayer does not support planar SST/TSK, coupled SST, LSM surface "
623  "temperature or fluxes, or custom/file-driven roughness; no mapping exists "
624  "from those planar inputs to arbitrary EB cut cells.");
625  }
626 
627  // Attributes for MFs and FABs
628  //--------------------------------------------------------
629  // Create a 2D ba for planar terrain, 3D for EB terrain
630  const int dir = m_face.coordDir();
631  int sm_index;
632  if (m_face.isLow()) {
633  sm_index = m_geom[lev].Domain().smallEnd(dir);
634  } else {
635  sm_index = m_geom[lev].Domain().bigEnd(dir);
636  }
637 
638  amrex::BoxArray ba = mf.boxArray();
639  amrex::BoxArray ba_flux;
640  amrex::IntVect ng{1,1,0};
641 
642  // The lateral-wall implementation uses the two-dimensional land-mask
643  // layout to identify grids on the requested face. Lateral walls are
644  // intentionally restricted to grids spanning the complete z domain;
645  // z-decomposed and partial-height grids are not supported.
646  if (dir != 2) {
647  const int dom_lo_z = m_geom[lev].Domain().smallEnd(2);
648  const int dom_hi_z = m_geom[lev].Domain().bigEnd(2);
649  for (int ibox = 0; ibox < ba.size(); ++ibox) {
651  ba[ibox].smallEnd(2) == dom_lo_z && ba[ibox].bigEnd(2) == dom_hi_z,
652  "Surface layer boundaries on x/y faces require Cartesian grids that "
653  "span the full level z domain; partial-height refined grids and grids "
654  "decomposed in z are not supported. Set erf.max_grid_size_z accordingly.");
655  }
656  }
657 
658  if (m_terrain_type == TerrainType::EB) {
659  // Use full 3D BoxArray for EB terrain
660  ba_flux = ba;
661  ng = amrex::IntVect{1,1,1}; // Include z ghost cells
662  } else {
663  // Collapse to 2D for planar terrain
664  amrex::BoxList bl2d = ba.boxList();
665  for (auto& b : bl2d) {
666  b.setRange(dir,sm_index);
667  }
668  ba_flux = amrex::BoxArray(std::move(bl2d));
669  // Lateral faces need state-width ghosts in the tangential z
670  // direction. A z face only needs the original one-cell x/y halo;
671  // kernels using the wider state/mask boxes clip to the FAB bounds.
672  if (dir == 2) {
673  ng = amrex::IntVect{1,1,0};
674  } else {
675  ng = mf.nGrowVect();
676  ng[dir] = 0;
677  }
678  }
679 
680  const amrex::DistributionMapping& dm = mf.DistributionMap();
681  const int ncomp = 1;
682 
683  // Surface copies of the planar boxes (see fill_planar_boundary)
684  // PlanarBoundary handles duplicate boxes created by a z-split. The x/y
685  // layouts use selective lateral exchange instead.
686  if (m_terrain_type != TerrainType::EB && dir == 2) {
687  const int ksurface = m_face.isLow()
688  ? m_geom[lev].Domain().smallEnd(2)
689  : m_geom[lev].Domain().bigEnd(2);
690  m_planar_bndry[lev].define(ba, ba_flux, dm, ksurface, m_face.isLow());
691  }
692 
693  // Z0 heights FAB
694  //--------------------------------------------------------
695  z_0[lev].define(ba_flux, dm, ncomp, ng);
696  z_0[lev].setVal(z0_const);
697  if (read_z0) {
698  read_custom_roughness(lev, fname);
699  }
700 
701  // 2D MFs for U*, T*, T_surf
702  //--------------------------------------------------------
703  u_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
704  u_star[lev]->setVal(bogus_large_value);
705 
706  w_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
707  w_star[lev]->setVal(bogus_large_value);
708 
709  t_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
710  t_star[lev]->setVal(zero); // default to neutral
711 
712  q_star[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
713  q_star[lev]->setVal(zero); // default to dry
714 
715  olen[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
716  olen[lev]->setVal(bogus_large_value);
717 
718  pblh[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
719  pblh[lev]->setVal(bogus_large_value);
720 
721  t_surf[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
722  t_surf[lev]->setVal(default_land_surf_temp);
723 
724  q_surf[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
725  q_surf[lev]->setVal(default_land_surf_moist);
726 
727  const amrex::iMultiFab& surface_mask = *m_lmask_lev[lev][0];
729  surface_mask.boxArray().size() == mf.boxArray().size(),
730  "Surface-layer mask and state must have the same number of boxes.");
732  surface_mask.DistributionMap() == mf.DistributionMap(),
733  "Surface-layer mask and state must have identical ownership.");
735  surface_mask.boxArray().size() == u_star[lev]->boxArray().size(),
736  "Surface-layer mask and parameters must have the same number of boxes.");
738  surface_mask.DistributionMap() == u_star[lev]->DistributionMap(),
739  "Surface-layer mask and parameters must have identical ownership.");
740 
741  surface_diagnostic_source[lev] = std::make_unique<amrex::MultiFab>(ba_flux, dm, ncomp, ng);
742  surface_diagnostic_source[lev]->setVal(
744 
745  // TODO: Do we want an enum struct for indexing?
746 
747  if (use_sst || use_tsk || m_has_lsm_tsurf || m_use_coupled_sst) {
748  // Valid SST, TSK, LSM or coupled-ocean data; t_surf set before computing
749  // fluxes (avoids extended lambda capture) Note that land temp will be set
750  // from m_tsk_lev while sea temp will be set from m_sst_lev
752 
753  // Pathways in fill_tsurf_with_sst_and_tsk
754  amrex::Print() << "Using MOST with specified surface temperature ";
755  if (m_has_lsm_tsurf && !use_sst && !use_tsk) {
756  amrex::Print() << "(LSM: " << m_lsm_data_name[m_lsm_tsurf_indx] << ")";
757  } else if (!use_sst && !use_tsk) {
758  amrex::Print() << "(land: T0, sea: none)";
759  } else {
760  // NOTE: SST from the LOW file populates TSK in update_sst_tsk.
761  // So if we have TSK, it contains everything and has been
762  // sanity checked for valid SST values.
763  if (use_tsk) { m_ignore_sst = true; }
764  if (use_tsk) {
765  amrex::Print() << "(land: TSK, ";
766  } else {
767  amrex::Print() << "(land: T0, ";
768  }
769  if (use_tsk && !use_sst) {
770  amrex::Print() << "sea: TSK)";
771  } else {
772  amrex::Print() << "sea: SST)";
774  }
775  }
776  // The coupler is layered on top of whatever the above selected: it
777  // overwrites only the water cells it actually covers, so the pathway
778  // named above remains the value for land and for uncovered water.
779  if (m_use_coupled_sst) {
780  amrex::Print() << " + coupled ocean SST where covered";
781  }
782  amrex::Print() << std::endl;
783  }
784  }
785 
786  /**
787  * Update surface fluxes and related surface-layer state.
788  *
789  * @param[in] lev level index
790  * @param[in] elapsed_time current elapsed simulation time
791  * @param[in] elapsed_time_since_start_low elapsed time relative to low-data start
792  * @param[in,out] cons_in conserved state used by the flux update
793  * @param[in] z_phys_nd nodal physical-height field
794  * @param[in] walldist wall-distance field
795  * @param[in] max_iters maximum MOST iteration count
796  */
797  void
798  update_fluxes (const int& lev,
799  const double& elapsed_time,
800  const double& elapsed_time_since_start_low,
801  amrex::MultiFab& cons_in,
802  const std::unique_ptr<amrex::MultiFab>& z_phys_nd,
803  const std::unique_ptr<amrex::MultiFab>& walldist,
804  int max_iters = 100);
805 
806  /**
807  * Compute MOST fluxes with a selected flux-iteration functor.
808  *
809  * @param[in] lev level index
810  * @param[in] max_iters maximum MOST iteration count
811  * @param[in,out] cons_in conserved state used by the flux computation
812  * @param[in] most_flux flux-iteration functor
813  * @param[in] is_land whether the land-surface branch is active
814  */
815  template <typename FluxIter>
816  void compute_fluxes (const int& lev,
817  const int& max_iters,
818  amrex::MultiFab& cons_in,
819  const FluxIter& most_flux,
820  bool is_land);
821 
822  /**
823  * Initialize TKE from the current surface friction velocity.
824  *
825  * @param[in] lev level index
826  * @param[in,out] cons conserved state whose TKE component is initialized
827  * @param[in] z_phys_nd nodal physical-height field
828  * @param[in] tkefac scale factor applied to the initialized TKE
829  * @param[in] zscale vertical decay scale
830  */
831  void init_tke_from_ustar (const int& lev,
832  amrex::MultiFab& cons,
833  const std::unique_ptr<amrex::MultiFab>& z_phys_nd,
834  const amrex::Real tkefac = one,
835  const amrex::Real zscale = amrex::Real(700.0));
836 
837  /**
838  * Fill the ghost cells of a planar surface-layer MultiFab, and the valid region of
839  * its uncomputed copies when the 3D BoxArray is split in z (see PlanarBoundary).
840  *
841  * @param[in] lev level index
842  * @param[in,out] mf planar MultiFab to fill
843  */
844  void fill_planar_boundary (const int& lev, amrex::MultiFab& mf);
845 
846  /**
847  * Sum a planar field over the valid cells of the surface, counting each surface cell once.
848  *
849  * On a level whose grids are split in the surface-normal direction the planar BoxArray holds
850  * one duplicate box per stacked 3D box (see PlanarBoundary), so a plain sum over the planar
851  * MultiFab counts every surface cell once per stacked box. Only the computed surface copies
852  * are read here, so the duplicates need not have been filled by fill_planar_boundary, and the
853  * gather needs no communication. On EB terrain the fields are 3D without duplicates, and the
854  * lowest plane of the domain is summed. Only for a surface layer on a z face.
855  *
856  * @param[in] lev level index
857  * @param[in] mf cell-centered planar MultiFab on this surface's planar BoxArray
858  * @param[in] comp component to sum
859  */
860  amrex::Real surface_sum (const int& lev, const amrex::MultiFab& mf, int comp = 0) const;
861 
862  /**
863  * Impose surface-layer boundary conditions for planar terrain.
864  *
865  * @param[in] lev level index
866  * @param[in] mfs state and velocity fields used by the BC computation
867  * @param[in,out] Tau_lev stress fields to fill
868  * @param[in,out] xheat_flux x-face heat flux field
869  * @param[in,out] yheat_flux y-face heat flux field
870  * @param[in,out] zheat_flux z-face heat flux field
871  * @param[in,out] xqv_flux x-face moisture flux field
872  * @param[in,out] yqv_flux y-face moisture flux field
873  * @param[in,out] zqv_flux z-face moisture flux field
874  * @param[in] z_phys physical-height field
875  */
876  void impose_SurfaceLayer_bcs (const int& lev,
877  amrex::Vector<const amrex::MultiFab*> mfs,
878  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& Tau_lev,
879  amrex::MultiFab* xheat_flux,
880  amrex::MultiFab* yheat_flux,
881  amrex::MultiFab* zheat_flux,
882  amrex::MultiFab* xqv_flux,
883  amrex::MultiFab* yqv_flux,
884  amrex::MultiFab* zqv_flux,
885  const amrex::MultiFab* z_phys);
886 
887  /**
888  * Impose surface-layer boundary conditions for embedded-boundary terrain.
889  *
890  * @param[in] lev level index
891  * @param[in] mfs state and velocity fields used by the BC computation
892  * @param[in,out] Tau_lev EB stress fields to fill
893  * @param[in,out] xheat_flux x-face heat flux field
894  * @param[in,out] yheat_flux y-face heat flux field
895  * @param[in,out] zheat_flux z-face heat flux field
896  * @param[in,out] xqv_flux x-face moisture flux field
897  * @param[in,out] yqv_flux y-face moisture flux field
898  * @param[in,out] zqv_flux z-face moisture flux field
899  */
900  void impose_SurfaceLayer_bcs_EB (const int& lev,
901  amrex::Vector<const amrex::MultiFab*> mfs,
902  amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab>>>& Tau_lev,
903  amrex::MultiFab* xheat_flux,
904  amrex::MultiFab* yheat_flux,
905  amrex::MultiFab* zheat_flux,
906  amrex::MultiFab* xqv_flux,
907  amrex::MultiFab* yqv_flux,
908  amrex::MultiFab* zqv_flux);
909 
910  /**
911  * Compute planar-terrain surface-layer flux boundary conditions.
912  *
913  * @param[in] lev level index
914  * @param[in] mfs state and velocity fields used by the BC computation
915  * @param[in,out] Tau_lev stress fields to fill
916  * @param[in,out] xheat_flux x-face heat flux field
917  * @param[in,out] yheat_flux y-face heat flux field
918  * @param[in,out] zheat_flux z-face heat flux field
919  * @param[in,out] xqv_flux x-face moisture flux field
920  * @param[in,out] yqv_flux y-face moisture flux field
921  * @param[in,out] zqv_flux z-face moisture flux field
922  * @param[in] z_phys physical-height field
923  * @param[in] flux_comp flux-computation functor
924  */
925  template <typename FluxCalc>
926  void compute_SurfaceLayer_bcs (const int& lev,
927  amrex::Vector<const amrex::MultiFab*> mfs,
928  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& Tau_lev,
929  amrex::MultiFab* xheat_flux,
930  amrex::MultiFab* yheat_flux,
931  amrex::MultiFab* zheat_flux,
932  amrex::MultiFab* xqv_flux,
933  amrex::MultiFab* yqv_flux,
934  amrex::MultiFab* zqv_flux,
935  const amrex::MultiFab* z_phys,
936  const FluxCalc& flux_comp);
937 
938  /**
939  * Compute embedded-boundary surface-layer flux boundary conditions.
940  *
941  * @param[in] lev level index
942  * @param[in] mfs state and velocity fields used by the BC computation
943  * @param[in,out] Tau_lev EB stress fields to fill
944  * @param[in,out] xheat_flux x-face heat flux field
945  * @param[in,out] yheat_flux y-face heat flux field
946  * @param[in,out] zheat_flux z-face heat flux field
947  * @param[in,out] xqv_flux x-face moisture flux field
948  * @param[in,out] yqv_flux y-face moisture flux field
949  * @param[in,out] zqv_flux z-face moisture flux field
950  * @param[in] flux_comp flux-computation functor
951  */
952  template <typename FluxCalc>
953  void compute_SurfaceLayer_bcs_EB (const int& lev,
954  amrex::Vector<const amrex::MultiFab*> mfs,
955  amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab>>>& Tau_lev,
956  amrex::MultiFab* xheat_flux,
957  amrex::MultiFab* yheat_flux,
958  amrex::MultiFab* zheat_flux,
959  amrex::MultiFab* xqv_flux,
960  amrex::MultiFab* yqv_flux,
961  amrex::MultiFab* zqv_flux,
962  const FluxCalc& flux_comp);
963 
964  /**
965  * Derive MOST surface parameters from LSM fluxes.
966  *
967  * @param[in] lev level index
968  * @param[in,out] cons_in conserved state used by the surface-parameter computation
969  */
970  void compute_sfc_params_from_lsm_fluxes(const int& lev,
971  amrex::MultiFab& cons_in);
972 
973  /**
974  * Fill surface temperature from available SST and TSK data.
975  *
976  * @param[in] lev level index
977  * @param[in] time interpolation time
978  */
979  void fill_tsurf_with_sst_and_tsk (const int& lev,
980  const double& time);
981 
982  /**
983  * Fill surface temperature interpolated from time varying SST file
984  *
985  * @param[in] lev level index
986  * @param[in] time interpolation time
987  */
988  void fill_tsurf_with_sfc_sst (const int& lev,
989  const double& time,
990  const amrex::MultiFab& cons_in,
991  const std::unique_ptr<amrex::MultiFab>& z_phys_nd);
992 
993  /**
994  * Overwrite surface temperature with coupled ocean SST where the coupler
995  * covers the cell.
996  *
997  * Runs after fill_tsurf_with_sst_and_tsk so that the lower-boundary data is
998  * the base layer: land cells, and water cells with no ocean donor, keep the
999  * value written there.
1000  *
1001  * @param[in] lev level index
1002  */
1003  void fill_tsurf_with_coupled_sst (const int& lev,
1004  const amrex::MultiFab& cons_in,
1005  const std::unique_ptr<amrex::MultiFab>& z_phys_nd);
1006 
1007  /**
1008  * Fill surface moisture from saturation specific humidity.
1009  *
1010  * @param[in] lev level index
1011  * @param[in] cons_in conserved state used to evaluate surface pressure
1012  * @param[in] z_phys_nd nodal physical-height field
1013  */
1014  void fill_qsurf_with_qsat (const int& lev,
1015  const amrex::MultiFab& cons_in,
1016  const std::unique_ptr<amrex::MultiFab>& z_phys_nd);
1017 
1018  void set_pblh(const int& lev, const amrex::MultiFab& pblh_in);
1019 
1020  /**
1021  * Updates current time index for interpolating data from SFC/SST file.
1022  *
1023  * @param[in] time elapsed time
1024  */
1025  void update_sfc_time_index (const amrex::Real& time);
1026 
1027  /**
1028  * Interpolates the SFC/SST data at the given column and time
1029  *
1030  * @param[in] time elapsed time
1031  * @param[in] col column index of file data
1032  */
1034  int col) const;
1035 
1036  /**
1037  * Fill surface temperature from the LSM surface-temperature field.
1038  *
1039  * @param[in] lev level index
1040  */
1041  void get_lsm_tsurf (const int& lev);
1042 
1043  /**
1044  * Wrapper around compute_pblh.
1045  *
1046  * @param[in] lev level index
1047  * @param[in,out] vars state variables used by the PBL-height calculation
1048  * @param[in] z_phys_cc cell-centered physical-height field
1049  * @param[in] moisture_indices indices for moisture components
1050  */
1051  void update_pblh (const int& lev,
1052  amrex::Vector<amrex::Vector<amrex::MultiFab>>& vars,
1053  amrex::MultiFab* z_phys_cc,
1054  const MoistureComponentIndices& moisture_indices);
1055 
1056  /**
1057  * Compute planetary-boundary-layer height with the selected estimator.
1058  *
1059  * @param[in] lev level index
1060  * @param[in,out] vars state variables used by the PBL-height calculation
1061  * @param[in] z_phys_cc cell-centered physical-height field
1062  * @param[in] est PBL-height estimator functor
1063  * @param[in] moisture_indice indices for moisture components
1064  */
1065  template <typename PBLHeightEstimator>
1066  void compute_pblh (const int& lev,
1067  amrex::Vector<amrex::Vector<amrex::MultiFab>>& vars,
1068  amrex::MultiFab* z_phys_cc,
1069  const PBLHeightEstimator& est,
1070  const MoistureComponentIndices& moisture_indice);
1071 
1072  /**
1073  * Read custom roughness data for one level.
1074  *
1075  * @param[in] lev level index
1076  * @param[in] fname roughness-data file name
1077  */
1078  void read_custom_roughness (const int& lev,
1079  const std::string& fname);
1080 
1081  /**
1082  * Update prescribed surface temperature from the configured heating rate.
1083  *
1084  * @param[in] time elapsed simulation time
1085  */
1086  void update_surf_temp (const double& time)
1087  {
1088  // NOTE: this is a whole-domain setVal, so it overwrites the SST/TSK fill
1089  // done earlier in update_fluxes. Coupled SST is applied after this
1090  // call and therefore still wins on the water cells it covers.
1091  if (surf_heating_rate != 0) {
1092  // Use the actual size of t_surf, not m_geom.size(), which is always
1093  // max_level+1 and so runs past the levels that exist. t_surf is sized for
1094  // all levels up front but filled one level at a time, so we also have to
1095  // skip the entries that have not been allocated yet.
1096  int nlevs = static_cast<int>(t_surf.size());
1097  for (int lev = 0; lev < nlevs; lev++) {
1098  if (!t_surf[lev]) { continue; }
1099  t_surf[lev]->setVal(surf_temp + surf_heating_rate * static_cast<amrex::Real>(time));
1100  amrex::Print() << "Surface temp at t=" << time << ": "
1101  << surf_temp + surf_heating_rate * time << std::endl;
1102  }
1103  }
1104  }
1105 
1106  /**
1107  * Update MOST-average field pointers.
1108  *
1109  * @param[in] lev level index
1110  * @param[in] vars_old old-time state variables
1111  * @param[in] Theta_prim primitive potential-temperature fields by level
1112  * @param[in] Qv_prim primitive water-vapor fields by level
1113  * @param[in] Qr_prim primitive rain-water fields by level
1114  */
1115  void update_mac_ptrs (const int& lev,
1116  amrex::Vector<amrex::Vector<amrex::MultiFab>>& vars_old,
1117  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& Theta_prim,
1118  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& Qv_prim,
1119  amrex::Vector<std::unique_ptr<amrex::MultiFab>>& Qr_prim)
1120  {
1121  m_ma.update_field_ptrs(lev, vars_old, Theta_prim, Qv_prim, Qr_prim);
1122  }
1123 
1124  /**
1125  * Return the friction-velocity field.
1126  *
1127  * @param[in] lev level index
1128  */
1129  amrex::MultiFab* get_u_star (const int& lev) { return u_star[lev].get(); }
1130 
1131  /**
1132  * Return the convective velocity scale field.
1133  *
1134  * @param[in] lev level index
1135  */
1136  amrex::MultiFab* get_w_star (const int& lev) { return w_star[lev].get(); }
1137 
1138  /**
1139  * Do we actually compute w*? If not then the field returned by get_w_star holds
1140  * only the value it was initialized with, and must not be reported as a diagnostic.
1141  */
1142  [[nodiscard]] bool computes_w_star () const { return m_include_wstar; }
1143 
1144  /**
1145  * Do we actually compute the PBL height? If not then the field returned by get_pblh
1146  * holds only the value it was initialized with, and must not be reported as a diagnostic.
1147  */
1148  [[nodiscard]] bool computes_pblh () const { return (pblh_type != PBLHeightCalcType::None); }
1149 
1150  /**
1151  * Return the temperature scale field.
1152  *
1153  * @param[in] lev level index
1154  */
1155  amrex::MultiFab* get_t_star (const int& lev) { return t_star[lev].get(); }
1156 
1157  /**
1158  * Return the moisture scale field.
1159  *
1160  * @param[in] lev level index
1161  */
1162  amrex::MultiFab* get_q_star (const int& lev) { return q_star[lev].get(); }
1163 
1164  /**
1165  * Return the Obukhov length field.
1166  *
1167  * @param[in] lev level index
1168  */
1169  amrex::MultiFab* get_olen (const int& lev) { return olen[lev].get(); }
1170 
1171  /**
1172  * Return the planetary-boundary-layer-height field.
1173  *
1174  * @param[in] lev level index
1175  */
1176  amrex::MultiFab* get_pblh (const int& lev) { return pblh[lev].get(); }
1177 
1178  /**
1179  * Return a MOST-average field.
1180  *
1181  * @param[in] lev level index
1182  * @param[in] comp component index
1183  */
1184  const amrex::MultiFab* get_mac_avg (const int& lev, int comp)
1185  {
1186  return m_ma.get_average(lev, comp);
1187  }
1188 
1189  /**
1190  * Return whether the MOST averages are filtered in time.
1191  */
1193 
1194  /**
1195  * Return the number of MOST-average components.
1196  */
1197  int get_num_mac_avg () const { return m_ma.get_navg(); }
1198 
1199  /**
1200  * Return whether the time filter at this level holds meaningful history.
1201  *
1202  * @param[in] lev level index
1203  */
1204  bool mac_avg_is_initialized (const int& lev) const { return m_ma.time_avg_is_initialized(lev); }
1205 
1206  /**
1207  * Declare the time filter at this level to hold meaningful history (restart).
1208  *
1209  * @param[in] lev level index
1210  */
1212 
1213  /**
1214  * Return a MOST-average field for modification (restart).
1215  *
1216  * @param[in] lev level index
1217  * @param[in] comp component index
1218  */
1219  amrex::MultiFab* get_mac_avg_ptr (const int& lev, int comp) { return m_ma.get_average(lev, comp); }
1220 
1221  /**
1222  * Return the filtered plane averages, which hold the filter state for the
1223  * plane and EB averaging policies.
1224  *
1225  * @param[in] lev level index
1226  */
1227  amrex::Vector<amrex::Real> get_mac_plane_avg (const int& lev) const { return m_ma.get_plane_average(lev); }
1228 
1229  /**
1230  * Restore the filtered plane averages from a checkpoint; returns false if the
1231  * checkpoint does not hold what this run expects.
1232  *
1233  * @param[in] lev level index
1234  * @param[in] pavg filtered plane averages, one per average component
1235  */
1236  bool set_mac_plane_avg (const int& lev, const amrex::Vector<amrex::Real>& pavg) { return m_ma.set_plane_average(lev, pavg); }
1237 
1238  /**
1239  * Return the surface-temperature field.
1240  *
1241  * @param[in] lev level index
1242  */
1243  amrex::MultiFab* get_t_surf (const int& lev) { return t_surf[lev].get(); }
1244 
1245  /**
1246  * Set the surface-temperature field to a constant value.
1247  *
1248  * @param[in] lev level index
1249  * @param[in] tsurf surface temperature
1250  */
1251  void set_t_surf(const int& lev, const amrex::Real tsurf) { t_surf[lev]->setVal(tsurf); }
1252 
1253  /**
1254  * Return the surface-moisture field.
1255  *
1256  * @param[in] lev level index
1257  */
1258  amrex::MultiFab* get_q_surf (const int& lev) { return q_surf[lev].get(); }
1259 
1260  /**
1261  * Set the surface-moisture field to a constant value.
1262  *
1263  * @param[in] lev level index
1264  * @param[in] qsurf surface moisture
1265  */
1266  void set_q_surf(const int& lev, const amrex::Real qsurf) { q_surf[lev]->setVal(qsurf); }
1267 
1268  /**
1269  * Return the surface-diagnostic provenance field.
1270  *
1271  * @param[in] lev level index
1272  */
1273  amrex::MultiFab* get_surface_diagnostic_source (const int& lev) { return surface_diagnostic_source[lev].get(); }
1274 
1275  /**
1276  * Return the minimum reference height for one level.
1277  *
1278  * @param[in] lev level index
1279  */
1280  amrex::Real get_zref (const int& lev) { return (m_ma.get_zref(lev))->min(0); }
1281 
1282  /**
1283  * Return the roughness-height field.
1284  *
1285  * @param[in] lev level index
1286  */
1287  amrex::MultiFab* get_z0 (const int& lev) { return &z_0[lev]; }
1288 
1289  /**
1290  * Return whether variable sea roughness is active.
1291  */
1293 
1294  /**
1295  * Return the land-mask field.
1296  *
1297  * @param[in] lev level index
1298  */
1299  amrex::iMultiFab* get_lmask (const int& lev) { return m_lmask_lev[lev][0]; }
1300 
1301  /**
1302  * Compute the minimum land-mask value over valid and optional ghost cells.
1303  *
1304  * @param[in] lmask land-mask field
1305  * @param[in] nghost number of ghost cells included in the reduction
1306  */
1307  int lmask_min_reduce (amrex::iMultiFab& lmask,
1308  const int& nghost)
1309  {
1310  int lmask_min = amrex::ReduceMin(lmask, nghost, [=] AMREX_GPU_HOST_DEVICE(
1311  amrex::Box const& bx, amrex::Array4<int const> const& lm_arr) -> int
1312  {
1313  int locmin = std::numeric_limits<int>::max();
1314  const auto lo = lbound(bx);
1315  const auto hi = ubound(bx);
1316  for (int j = lo.y; j <= hi.y; ++j) {
1317  for (int i = lo.x; i <= hi.x; ++i) {
1318  locmin = std::min(locmin, lm_arr(i, j, 0));
1319  }
1320  }
1321  return locmin;
1322  });
1323 
1324  return lmask_min;
1325  }
1326 
1327  /**
1328  * Update one stored sea-surface-temperature pointer.
1329  *
1330  * @param[in] lev level index
1331  * @param[in] itime time-slice index
1332  * @param[in] sst_ptr sea-surface-temperature field pointer
1333  */
1334  void update_sst_ptr(const int lev, const int itime, amrex::MultiFab* sst_ptr) {
1335  m_sst_lev[lev][itime] = sst_ptr;
1336  }
1337 
1338  /**
1339  * Update one stored skin-temperature pointer.
1340  *
1341  * @param[in] lev level index
1342  * @param[in] itime time-slice index
1343  * @param[in] tsk_ptr skin-temperature field pointer
1344  */
1345  void update_tsk_ptr(const int lev, const int itime, amrex::MultiFab* tsk_ptr) {
1346  m_tsk_lev[lev][itime] = tsk_ptr;
1347  }
1348 
1349  /**
1350  * Hand over the coupled sea-surface temperature and its per-cell coverage flag.
1351  *
1352  * Both pointers stay owned by the caller and must remain valid, and on the same
1353  * layout, until replaced. Passing a null sst_ptr retracts the coupled lane, so
1354  * the lower-boundary SST/TSK value stands everywhere again.
1355  *
1356  * @param[in] lev level index
1357  * @param[in] sst_ptr coupled sea-surface temperature [K]
1358  * @param[in] valid_ptr per-cell coverage flag; nonzero where the coupler
1359  * supplied a value. Null means no cell is covered.
1360  */
1361  void update_coupled_sst_ptr (const int lev,
1362  amrex::MultiFab* sst_ptr,
1363  amrex::iMultiFab* valid_ptr) {
1364  m_coupled_sst_lev[lev] = sst_ptr;
1365  m_coupled_sst_valid_lev[lev] = valid_ptr;
1366  }
1367 
1368  /**
1369  * Declare that an ocean coupler will supply SST for this run.
1370  *
1371  * Must be called before make_SurfaceLayer_at_level, which uses it to select
1372  * ThetaCalcType::SURFACE_TEMPERATURE.
1373  *
1374  * @param[in] active whether coupled SST is configured
1375  */
1376  void set_coupled_sst_active (const bool active) { m_use_coupled_sst = active; }
1377 
1378  /**
1379  * Set the domain faces that use the surface-layer boundary condition.
1380  *
1381  * The face set is used to suppress transpose stress writes at shared edges;
1382  * the face-normal component remains owned by its corresponding face path.
1383  *
1384  * @param[in] active_faces whether a specific x/y/z lo/hi face is enabled
1385  */
1386  void set_surface_layer_faces (const amrex::GpuArray<int, AMREX_SPACEDIM*2>& active_faces)
1387  {
1388  m_surface_layer_faces = active_faces;
1389  }
1390 
1391  /**
1392  * Reads columns of data from a text file, returning each column in a vector.
1393  *
1394  * @param[in] fname path to text file
1395  * @param[in] skip_nlines number of lines to skip before reading data (e.g, header lines)
1396  * @return Vector containing each column in the file as a vector
1397  */
1398  static amrex::Vector<amrex::Vector<amrex::Real>>
1399  read_cols(const std::string& fname, const int skip_nlines = 1);
1400 
1401  /**
1402  * Fills surface layer data for lateral faces. A normal FillBoundary on
1403  * lateral faces would bring in data from other ranks or interior grids
1404  * that do not own data on the lateral face.
1405  *
1406  * @param[in] lev level index
1407  * @param[in,out] selected_field if non-null, fill only this field;
1408  * otherwise fill all lateral surface fields
1409  */
1410  void fill_lateral_surface_parameter_ghosts (const int& lev,
1411  amrex::MultiFab* selected_field = nullptr);
1412 
1413  enum struct FluxCalcType {
1414  MOENG = 0, ///< Moeng functional form
1415  CUSTOM, ///< Custom constant flux functional form
1416  BULK_COEFF, ///< Bulk transfer coefficient functional form
1417  ROTATE, ///< Terrain rotation flux functional form
1418  RICO
1419  };
1420 
1421  enum struct ThetaCalcType {
1422  ADIABATIC = 0,
1423  HEAT_FLUX, ///< Heat-flux specified
1424  SURFACE_TEMPERATURE ///< Surface temperature specified
1425  };
1426 
1427  enum struct MoistCalcType {
1428  ADIABATIC = 0,
1429  MOISTURE_FLUX, ///< Qv-flux specified
1430  SURFACE_MOISTURE ///< Surface Qv specified
1431  };
1432 
1433  enum struct RoughCalcType {
1434  CONSTANT = 0, ///< Constant z0
1435  CHARNOCK,
1437  DONELAN,
1438  WAVE_COUPLED
1439  };
1440 
1442 
1449 
1450 private:
1451  // Set in constructor
1452  amrex::Orientation m_face;
1453  std::string m_pp_prefix;
1454  amrex::Vector<amrex::Geometry> m_geom;
1455  bool m_rotate = false;
1456  amrex::GpuArray<int, AMREX_SPACEDIM*2> m_surface_layer_faces{};
1460 
1461  bool m_include_wstar = false;
1464  // Negative sentinels: a surface temperature is absolute and a surface moisture is a
1465  // mixing ratio, so neither can legitimately be negative. The constructor probes
1466  // most.surf_temp / most.surf_moist with these and tests the resulting value to decide
1467  // whether the user supplied them. They were previously left uninitialized, which was
1468  // safe only because a plain query() leaves them untouched on a miss.
1478  amrex::Real custom_rhosurf{0}; // use specified value instead of rho from first cell
1479  bool specified_rho_surf{false};
1481  bool smooth_flow_visc{true};
1483  amrex::Vector<amrex::MultiFab> z_0;
1484  bool m_var_z0{false};
1485 
1488 
1490  bool m_has_lsm_fluxes = false;
1491  bool m_has_lsm_tsurf = false;
1493 
1494  // variables for sfc/sst forcing from file
1495  bool m_use_sfc_fluxes = false;
1496  bool m_use_sfc_sst = false;
1497  int sfc_time_ind = 0;
1498  amrex::Vector<amrex::Vector<amrex::Real>> sfc;
1502 
1503  // Set from solverChoice.use_coupled_sst before the per-level Define runs.
1504  // Configuration only: true means an ocean coupler will supply SST on some
1505  // subset of the water cells, not that it has supplied any yet.
1506  bool m_use_coupled_sst = false;
1507 
1511  bool m_ignore_sst = false;
1512 
1513  amrex::Vector<const eb_*> m_eb_vec;
1514  TerrainType m_terrain_type;
1517  amrex::Vector<std::unique_ptr<amrex::MultiFab>> u_star;
1518  amrex::Vector<std::unique_ptr<amrex::MultiFab>> w_star;
1519  amrex::Vector<std::unique_ptr<amrex::MultiFab>> t_star;
1520  amrex::Vector<std::unique_ptr<amrex::MultiFab>> q_star;
1521  amrex::Vector<std::unique_ptr<amrex::MultiFab>> olen;
1522  amrex::Vector<std::unique_ptr<amrex::MultiFab>> pblh;
1523  amrex::Vector<std::unique_ptr<amrex::MultiFab>> t_surf;
1524  amrex::Vector<std::unique_ptr<amrex::MultiFab>> q_surf;
1525  // The planar BoxArray is the z-collapse of the 3D BoxArray, so a 3D BoxArray split in z
1526  // gives duplicate planar boxes (see PlanarBoundary). fill_planar_boundary goes through
1527  // this for u*, w* (only when it is computed), t*, q*, L, t_surf, q_surf, pblh and the
1528  // diagnostic source, after each is computed on the surface copies.
1529  amrex::Vector<PlanarBoundary> m_planar_bndry;
1530  // Columns for the PBL-height estimator. The estimator scans each box it is given from its
1531  // lowest cell to its highest, so the box must start at the ground. When some box of a
1532  // level does not (grids stacked in z, or a refined region aloft), compute_pblh runs the
1533  // estimator on these boxes instead: the runs of cells that start at the ground, each joined
1534  // into one box that ends where the grids above that ground end. Built by
1535  // define_pblh_columns when the grids of the level change.
1537  {
1538  amrex::BoxArray ba; // Grids the columns were built for ...
1539  amrex::DistributionMapping dm; // ... and their ranks
1540  bool needed = false; // Whether some box does not start at the ground
1541  amrex::BoxArray ba_col; // The columns; empty on a level entirely aloft
1542  amrex::BoxArray ba_col2d; // Their ground plane
1543  amrex::DistributionMapping dm_col;
1544  amrex::MultiFab hold_col; // No data; keep the copy metadata alive
1545  amrex::MultiFab hold_col2d;
1546  };
1547  amrex::Vector<PBLHColumns> m_pblh_columns;
1548  void define_pblh_columns (const int& lev,
1549  const amrex::BoxArray& ba,
1550  const amrex::DistributionMapping& dm);
1551  // Diagnostic-only provenance mask. This records the cell-centered source
1552  // path used by the SurfaceLayer scalar diagnostic path. It must not feed
1553  // back into flux calculations, boundary conditions, or model state.
1554  amrex::Vector<std::unique_ptr<amrex::MultiFab>> surface_diagnostic_source;
1555 
1556  amrex::Vector<amrex::Vector<amrex::MultiFab*>> m_sst_lev;
1557  amrex::Vector<amrex::Vector<amrex::MultiFab*>> m_tsk_lev;
1558  amrex::Vector<amrex::Vector<amrex::iMultiFab*>> m_lmask_lev;
1559 
1560  // Coupled SST from an external ocean model, plus the per-cell coverage flag
1561  // that says whether the coupler actually supplied a value for that cell.
1562  // Both are owned by ERF and borrowed here, exactly like m_sst_lev. A null
1563  // m_coupled_sst_lev[lev] means "no coupled SST has arrived yet"; a zero entry in
1564  // m_coupled_sst_valid_lev means "this cell has no ocean donor", and in both cases the
1565  // lower-boundary SST/TSK value filled earlier in update_fluxes stands.
1566  amrex::Vector<amrex::MultiFab*> m_coupled_sst_lev;
1567  amrex::Vector<amrex::iMultiFab*> m_coupled_sst_valid_lev;
1568  amrex::Vector<amrex::Vector<amrex::MultiFab*>> m_lsm_data_lev;
1569  amrex::Vector<amrex::Vector<amrex::MultiFab*>> m_lsm_flux_lev;
1570  amrex::Vector<std::string> m_lsm_data_name;
1571  amrex::Vector<std::string> m_lsm_flux_name;
1572  amrex::Vector<amrex::MultiFab*> m_Hwave_lev;
1573  amrex::Vector<amrex::MultiFab*> m_Lwave_lev;
1574  amrex::Vector<amrex::MultiFab*> m_eddyDiffs_lev;
1575 
1576  bool m_update_k_rans = false;
1579 };
1580 
1581 #endif /* SURFACELAYER_H */
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:17
constexpr amrex::Real RdoCp
Definition: ERF_Constants.H:41
Defines EB Monin-Obukhov surface-layer flux functors.
Declares the embedded-boundary factory manager used by ERF levels.
ParmParse pp("prob")
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
pp get("wavelength", wavelength)
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:30
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:29
amrex::Real Real
Definition: ERF_ShocInterface.H:19
AMREX_ASSERT_WITH_MESSAGE(wbar_cutoff_min > wbar_cutoff_max, "ERROR: wbar_cutoff_min < wbar_cutoff_max")
Definition: ERF_MOSTAverage.H:16
bool set_plane_average(const int &lev, const amrex::Vector< amrex::Real > &pavg)
Definition: ERF_MOSTAverage.H:315
amrex::Vector< amrex::Real > get_plane_average(const int &lev) const
Definition: ERF_MOSTAverage.H:301
bool do_time_averaging() const
Definition: ERF_MOSTAverage.H:265
amrex::MultiFab * get_zref(const int &lev) const
Definition: ERF_MOSTAverage.H:329
void set_time_avg_initialized(const int &lev)
Definition: ERF_MOSTAverage.H:289
int get_navg() const
Definition: ERF_MOSTAverage.H:270
const amrex::MultiFab * get_average(const int &lev, const int &comp) const
Definition: ERF_MOSTAverage.H:251
void update_field_ptrs(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars_old, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Theta_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qr_prim)
Definition: ERF_MOSTAverage.cpp:441
void make_MOSTAverage_at_level(const int &lev, const amrex::Vector< amrex::MultiFab * > &vars_old, std::unique_ptr< amrex::MultiFab > &Theta_prim, std::unique_ptr< amrex::MultiFab > &Qv_prim, std::unique_ptr< amrex::MultiFab > &Qr_prim, std::unique_ptr< amrex::MultiFab > &z_phys_nd)
Definition: ERF_MOSTAverage.cpp:171
bool time_avg_is_initialized(const int &lev) const
Definition: ERF_MOSTAverage.H:278
Definition: ERF_SurfaceLayer.H:59
ThetaCalcType theta_type
Definition: ERF_SurfaceLayer.H:1444
int lmask_min_reduce(amrex::iMultiFab &lmask, const int &nghost)
Definition: ERF_SurfaceLayer.H:1307
amrex::Vector< std::string > m_lsm_data_name
Definition: ERF_SurfaceLayer.H:1570
bool m_include_wstar
Definition: ERF_SurfaceLayer.H:1461
amrex::Real interpolate_sfc_column(const amrex::Real &time, int col) const
Definition: ERF_SurfaceLayer.cpp:397
bool specified_rho_surf
Definition: ERF_SurfaceLayer.H:1479
void set_q_surf(const int &lev, const amrex::Real qsurf)
Definition: ERF_SurfaceLayer.H:1266
void update_fluxes(const int &lev, const double &elapsed_time, const double &elapsed_time_since_start_low, amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const std::unique_ptr< amrex::MultiFab > &walldist, int max_iters=100)
Definition: ERF_SurfaceLayer.cpp:23
bool m_rotate
Definition: ERF_SurfaceLayer.H:1455
amrex::Real sfc_tflux
Definition: ERF_SurfaceLayer.H:1500
PBLHeightCalcType pblh_type
Definition: ERF_SurfaceLayer.H:1448
amrex::Vector< amrex::Vector< amrex::iMultiFab * > > m_lmask_lev
Definition: ERF_SurfaceLayer.H:1558
double m_final_low_time
Definition: ERF_SurfaceLayer.H:1458
amrex::Vector< PlanarBoundary > m_planar_bndry
Definition: ERF_SurfaceLayer.H:1529
amrex::iMultiFab * get_lmask(const int &lev)
Definition: ERF_SurfaceLayer.H:1299
bool use_moisture
Definition: ERF_SurfaceLayer.H:1489
amrex::Vector< PBLHColumns > m_pblh_columns
Definition: ERF_SurfaceLayer.H:1547
amrex::MultiFab * get_q_surf(const int &lev)
Definition: ERF_SurfaceLayer.H:1258
bool m_has_lsm_tsurf
Definition: ERF_SurfaceLayer.H:1491
amrex::MultiFab * get_w_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1136
amrex::Real m_Cq
Definition: ERF_SurfaceLayer.H:1510
amrex::Vector< const eb_ * > m_eb_vec
Definition: ERF_SurfaceLayer.H:1513
RoughCalcType rough_type_land
Definition: ERF_SurfaceLayer.H:1446
void update_pblh(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars, amrex::MultiFab *z_phys_cc, const MoistureComponentIndices &moisture_indices)
Definition: ERF_SurfaceLayer.cpp:2216
amrex::Real sfc_qflux
Definition: ERF_SurfaceLayer.H:1499
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_surf
Definition: ERF_SurfaceLayer.H:1523
bool m_use_sfc_fluxes
Definition: ERF_SurfaceLayer.H:1495
amrex::Real z0_const
Definition: ERF_SurfaceLayer.H:1462
amrex::Vector< std::unique_ptr< amrex::MultiFab > > surface_diagnostic_source
Definition: ERF_SurfaceLayer.H:1554
amrex::Real cnk_a
Definition: ERF_SurfaceLayer.H:1480
amrex::Real m_Ch
Definition: ERF_SurfaceLayer.H:1509
amrex::Real surf_temp
Definition: ERF_SurfaceLayer.H:1469
void update_mac_ptrs(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars_old, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Theta_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qr_prim)
Definition: ERF_SurfaceLayer.H:1115
void compute_pblh(const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars, amrex::MultiFab *z_phys_cc, const PBLHeightEstimator &est, const MoistureComponentIndices &moisture_indice)
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_star
Definition: ERF_SurfaceLayer.H:1520
int m_lsm_tsurf_indx
Definition: ERF_SurfaceLayer.H:1492
double m_start_low_time
Definition: ERF_SurfaceLayer.H:1457
bool mac_avg_is_initialized(const int &lev) const
Definition: ERF_SurfaceLayer.H:1204
bool mac_avg_is_time_averaged() const
Definition: ERF_SurfaceLayer.H:1192
amrex::Real rico_qsat_z0
Definition: ERF_SurfaceLayer.H:1487
bool m_has_lsm_fluxes
Definition: ERF_SurfaceLayer.H:1490
bool m_update_k_rans
Definition: ERF_SurfaceLayer.H:1576
SurfaceLayer(amrex::Orientation face, const amrex::Vector< amrex::Geometry > &geom, bool &use_rot_surface_flux, std::string a_pp_prefix, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &z_phys_nd, const amrex::Vector< amrex::Vector< amrex::Real >> &zlevels_stag, const MeshType &a_mesh_type, const TerrainType &a_terrain_type, const TurbChoice &a_turb_choice, amrex::Real a_rdOcp, double start_low_time, double final_low_time, double low_time_interval=0.0, const amrex::Vector< const eb_ * > &eb_vec={})
Definition: ERF_SurfaceLayer.H:81
amrex::Vector< amrex::MultiFab * > m_Lwave_lev
Definition: ERF_SurfaceLayer.H:1573
void get_lsm_tsurf(const int &lev)
Definition: ERF_SurfaceLayer.cpp:2035
void fill_qsurf_with_qsat(const int &lev, const amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd)
Definition: ERF_SurfaceLayer.cpp:1883
amrex::Real get_zref(const int &lev)
Definition: ERF_SurfaceLayer.H:1280
amrex::MultiFab * get_olen(const int &lev)
Definition: ERF_SurfaceLayer.H:1169
bool computes_w_star() const
Definition: ERF_SurfaceLayer.H:1142
amrex::Vector< amrex::MultiFab > z_0
Definition: ERF_SurfaceLayer.H:1483
void fill_tsurf_with_coupled_sst(const int &lev, const amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd)
Definition: ERF_SurfaceLayer.cpp:2079
amrex::Real surf_moist_flux
Definition: ERF_SurfaceLayer.H:1474
bool smooth_flow_visc
Definition: ERF_SurfaceLayer.H:1481
void compute_SurfaceLayer_bcs(const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const amrex::MultiFab *z_phys, const FluxCalc &flux_comp)
RoughCalcType rough_type_sea
Definition: ERF_SurfaceLayer.H:1447
void update_coupled_sst_ptr(const int lev, amrex::MultiFab *sst_ptr, amrex::iMultiFab *valid_ptr)
Definition: ERF_SurfaceLayer.H:1361
void init_tke_from_ustar(const int &lev, amrex::MultiFab &cons, const std::unique_ptr< amrex::MultiFab > &z_phys_nd, const amrex::Real tkefac=one, const amrex::Real zscale=amrex::Real(700.0))
Definition: ERF_SurfaceLayer.cpp:2428
std::string m_pp_prefix
Definition: ERF_SurfaceLayer.H:1453
amrex::Real surface_sum(const int &lev, const amrex::MultiFab &mf, int comp=0) const
Definition: ERF_SurfaceLayer.cpp:360
amrex::Real surf_moist
Definition: ERF_SurfaceLayer.H:1473
amrex::Vector< std::unique_ptr< amrex::MultiFab > > w_star
Definition: ERF_SurfaceLayer.H:1518
bool m_ignore_sst
Definition: ERF_SurfaceLayer.H:1511
int get_num_mac_avg() const
Definition: ERF_SurfaceLayer.H:1197
amrex::Vector< amrex::Vector< amrex::Real > > sfc
Definition: ERF_SurfaceLayer.H:1498
amrex::MultiFab * get_u_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1129
double m_low_time_interval
Definition: ERF_SurfaceLayer.H:1459
amrex::GpuArray< int, AMREX_SPACEDIM *2 > m_surface_layer_faces
Definition: ERF_SurfaceLayer.H:1456
void compute_fluxes(const int &lev, const int &max_iters, amrex::MultiFab &cons_in, const FluxIter &most_flux, bool is_land)
bool set_mac_plane_avg(const int &lev, const amrex::Vector< amrex::Real > &pavg)
Definition: ERF_SurfaceLayer.H:1236
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_data_lev
Definition: ERF_SurfaceLayer.H:1568
void set_t_surf(const int &lev, const amrex::Real tsurf)
Definition: ERF_SurfaceLayer.H:1251
amrex::Real custom_qstar
Definition: ERF_SurfaceLayer.H:1477
amrex::Vector< std::unique_ptr< amrex::MultiFab > > u_star
Definition: ERF_SurfaceLayer.H:1517
void update_tsk_ptr(const int lev, const int itime, amrex::MultiFab *tsk_ptr)
Definition: ERF_SurfaceLayer.H:1345
amrex::Real custom_rhosurf
Definition: ERF_SurfaceLayer.H:1478
amrex::Vector< std::unique_ptr< amrex::MultiFab > > q_surf
Definition: ERF_SurfaceLayer.H:1524
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_sst_lev
Definition: ERF_SurfaceLayer.H:1556
FluxCalcType
Definition: ERF_SurfaceLayer.H:1413
@ MOENG
Moeng functional form.
@ BULK_COEFF
Bulk transfer coefficient functional form.
@ CUSTOM
Custom constant flux functional form.
@ ROTATE
Terrain rotation flux functional form.
amrex::Real m_rdOcp
Definition: ERF_SurfaceLayer.H:1515
MoistCalcType
Definition: ERF_SurfaceLayer.H:1427
@ SURFACE_MOISTURE
Surface Qv specified.
@ MOISTURE_FLUX
Qv-flux specified.
amrex::Real depth
Definition: ERF_SurfaceLayer.H:1482
amrex::Vector< amrex::MultiFab * > m_coupled_sst_lev
Definition: ERF_SurfaceLayer.H:1566
void fill_lateral_surface_parameter_ghosts(const int &lev, amrex::MultiFab *selected_field=nullptr)
Definition: ERF_SurfaceLayer.cpp:606
amrex::Vector< amrex::MultiFab * > m_Hwave_lev
Definition: ERF_SurfaceLayer.H:1572
amrex::Real default_land_surf_moist
Definition: ERF_SurfaceLayer.H:1472
void update_sfc_time_index(const amrex::Real &time)
Definition: ERF_SurfaceLayer.cpp:380
bool m_var_z0
Definition: ERF_SurfaceLayer.H:1484
amrex::Vector< amrex::iMultiFab * > m_coupled_sst_valid_lev
Definition: ERF_SurfaceLayer.H:1567
amrex::MultiFab * get_surface_diagnostic_source(const int &lev)
Definition: ERF_SurfaceLayer.H:1273
amrex::MultiFab * get_t_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1155
bool have_variable_sea_roughness()
Definition: ERF_SurfaceLayer.H:1292
void impose_SurfaceLayer_bcs_EB(const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux)
Definition: ERF_SurfaceLayer.cpp:789
amrex::MultiFab * get_q_star(const int &lev)
Definition: ERF_SurfaceLayer.H:1162
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_lsm_flux_lev
Definition: ERF_SurfaceLayer.H:1569
amrex::MultiFab * get_mac_avg_ptr(const int &lev, int comp)
Definition: ERF_SurfaceLayer.H:1219
amrex::Vector< amrex::Real > get_mac_plane_avg(const int &lev) const
Definition: ERF_SurfaceLayer.H:1227
void set_pblh(const int &lev, const amrex::MultiFab &pblh_in)
Definition: ERF_SurfaceLayer.cpp:2233
void fill_planar_boundary(const int &lev, amrex::MultiFab &mf)
Definition: ERF_SurfaceLayer.cpp:344
PBLHeightCalcType
Definition: ERF_SurfaceLayer.H:1441
amrex::MultiFab * get_pblh(const int &lev)
Definition: ERF_SurfaceLayer.H:1176
void compute_SurfaceLayer_bcs_EB(const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab >>> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const FluxCalc &flux_comp)
amrex::Real rico_theta_z0
Definition: ERF_SurfaceLayer.H:1486
amrex::Real sfc_ustar
Definition: ERF_SurfaceLayer.H:1501
amrex::Real surf_temp_flux
Definition: ERF_SurfaceLayer.H:1471
amrex::Vector< amrex::Geometry > m_geom
Definition: ERF_SurfaceLayer.H:1454
amrex::Vector< std::unique_ptr< amrex::MultiFab > > t_star
Definition: ERF_SurfaceLayer.H:1519
int sfc_time_ind
Definition: ERF_SurfaceLayer.H:1497
bool computes_pblh() const
Definition: ERF_SurfaceLayer.H:1148
amrex::Real theta_ref
Definition: ERF_SurfaceLayer.H:1578
amrex::MultiFab * get_z0(const int &lev)
Definition: ERF_SurfaceLayer.H:1287
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_tsk_lev
Definition: ERF_SurfaceLayer.H:1557
void set_surface_layer_faces(const amrex::GpuArray< int, AMREX_SPACEDIM *2 > &active_faces)
Definition: ERF_SurfaceLayer.H:1386
void update_surf_temp(const double &time)
Definition: ERF_SurfaceLayer.H:1086
void set_coupled_sst_active(const bool active)
Definition: ERF_SurfaceLayer.H:1376
void fill_tsurf_with_sst_and_tsk(const int &lev, const double &time)
Definition: ERF_SurfaceLayer.cpp:1682
amrex::Real custom_tstar
Definition: ERF_SurfaceLayer.H:1476
static amrex::Vector< amrex::Vector< amrex::Real > > read_cols(const std::string &fname, const int skip_nlines=1)
Definition: ERF_SurfaceLayer.cpp:2639
amrex::Real surf_heating_rate
Definition: ERF_SurfaceLayer.H:1470
void make_SurfaceLayer_at_level(const int &lev, int nlevs, const amrex::Vector< amrex::MultiFab * > &mfv, std::unique_ptr< amrex::MultiFab > &Theta_prim, std::unique_ptr< amrex::MultiFab > &Qv_prim, std::unique_ptr< amrex::MultiFab > &Qr_prim, std::unique_ptr< amrex::MultiFab > &z_phys_nd, amrex::MultiFab *Hwave, amrex::MultiFab *Lwave, amrex::MultiFab *eddyDiffs, amrex::Vector< amrex::MultiFab * > lsm_data, amrex::Vector< std::string > lsm_data_name, amrex::Vector< amrex::MultiFab * > lsm_flux, amrex::Vector< std::string > lsm_flux_name, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &sst_lev, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &tsk_lev, amrex::Vector< std::unique_ptr< amrex::iMultiFab >> &lmask_lev)
Definition: ERF_SurfaceLayer.H:400
RoughCalcType
Definition: ERF_SurfaceLayer.H:1433
FluxCalcType flux_type
Definition: ERF_SurfaceLayer.H:1443
MoistCalcType moist_type
Definition: ERF_SurfaceLayer.H:1445
bool m_use_sfc_sst
Definition: ERF_SurfaceLayer.H:1496
void compute_sfc_params_from_lsm_fluxes(const int &lev, amrex::MultiFab &cons_in)
Definition: ERF_SurfaceLayer.cpp:1595
amrex::Real inv_Cmu2
Definition: ERF_SurfaceLayer.H:1577
void impose_SurfaceLayer_bcs(const int &lev, amrex::Vector< const amrex::MultiFab * > mfs, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Tau_lev, amrex::MultiFab *xheat_flux, amrex::MultiFab *yheat_flux, amrex::MultiFab *zheat_flux, amrex::MultiFab *xqv_flux, amrex::MultiFab *yqv_flux, amrex::MultiFab *zqv_flux, const amrex::MultiFab *z_phys)
Definition: ERF_SurfaceLayer.cpp:725
amrex::Vector< amrex::MultiFab * > m_eddyDiffs_lev
Definition: ERF_SurfaceLayer.H:1574
const amrex::MultiFab * get_mac_avg(const int &lev, int comp)
Definition: ERF_SurfaceLayer.H:1184
void update_sst_ptr(const int lev, const int itime, amrex::MultiFab *sst_ptr)
Definition: ERF_SurfaceLayer.H:1334
amrex::Real custom_ustar
Definition: ERF_SurfaceLayer.H:1475
amrex::Vector< std::unique_ptr< amrex::MultiFab > > olen
Definition: ERF_SurfaceLayer.H:1521
bool m_use_coupled_sst
Definition: ERF_SurfaceLayer.H:1506
void fill_tsurf_with_sfc_sst(const int &lev, const double &time, const amrex::MultiFab &cons_in, const std::unique_ptr< amrex::MultiFab > &z_phys_nd)
Definition: ERF_SurfaceLayer.cpp:1769
amrex::Orientation m_face
Definition: ERF_SurfaceLayer.H:1452
amrex::MultiFab * get_t_surf(const int &lev)
Definition: ERF_SurfaceLayer.H:1243
amrex::Real m_Cd
Definition: ERF_SurfaceLayer.H:1508
void set_mac_avg_initialized(const int &lev)
Definition: ERF_SurfaceLayer.H:1211
amrex::Vector< std::unique_ptr< amrex::MultiFab > > pblh
Definition: ERF_SurfaceLayer.H:1522
amrex::Real default_land_surf_temp
Definition: ERF_SurfaceLayer.H:1463
ThetaCalcType
Definition: ERF_SurfaceLayer.H:1421
@ SURFACE_TEMPERATURE
Surface temperature specified.
@ HEAT_FLUX
Heat-flux specified.
void define_pblh_columns(const int &lev, const amrex::BoxArray &ba, const amrex::DistributionMapping &dm)
Definition: ERF_SurfaceLayer.cpp:2363
void read_custom_roughness(const int &lev, const std::string &fname)
Definition: ERF_SurfaceLayer.cpp:2511
TerrainType m_terrain_type
Definition: ERF_SurfaceLayer.H:1514
amrex::Vector< std::string > m_lsm_flux_name
Definition: ERF_SurfaceLayer.H:1571
MOSTAverage m_ma
Definition: ERF_SurfaceLayer.H:1516
@ ng
Definition: ERF_Morrison.H:50
@ cons
Definition: ERF_IndexDefines.H:214
Definition: ERF_SurfaceLayer.H:28
bool planar_sources_supported_for_terrain(TerrainType terrain_type, bool use_sst, bool use_tsk, bool use_coupled_sst, bool has_lsm_tsurf, bool has_lsm_fluxes, bool has_custom_roughness)
Definition: ERF_SurfaceLayer.H:31
real(c_double), parameter epsilon
Definition: ERF_module_model_constants.F90:12
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real to_plot_value(SurfaceDiagnosticSource source) noexcept
Definition: ERF_SurfaceDiagnosticSource.H:45
The moisture data carried by the active microphysics scheme.
Definition: ERF_DataStruct.H:223
Definition: ERF_SurfaceLayer.H:1537
bool needed
Definition: ERF_SurfaceLayer.H:1540
amrex::BoxArray ba
Definition: ERF_SurfaceLayer.H:1538
amrex::BoxArray ba_col2d
Definition: ERF_SurfaceLayer.H:1542
amrex::MultiFab hold_col2d
Definition: ERF_SurfaceLayer.H:1545
amrex::MultiFab hold_col
Definition: ERF_SurfaceLayer.H:1544
amrex::DistributionMapping dm_col
Definition: ERF_SurfaceLayer.H:1543
amrex::DistributionMapping dm
Definition: ERF_SurfaceLayer.H:1539
amrex::BoxArray ba_col
Definition: ERF_SurfaceLayer.H:1541
Definition: ERF_TurbStruct.H:115
RANSType rans_type
Selected RANS closure.
Definition: ERF_TurbStruct.H:754
amrex::Real theta_ref
Reference potential temperature for stable stratification.
Definition: ERF_TurbStruct.H:743
bool dirichlet_k
Whether TKE uses Dirichlet boundary treatment.
Definition: ERF_TurbStruct.H:756
amrex::Real Cmu0
One-equation RANS Cmu0 coefficient.
Definition: ERF_TurbStruct.H:732