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