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