ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_MOSTUtils.H File Reference
#include <AMReX_Algorithm.H>
#include <AMReX_BLassert.H>
#include <AMReX_Math.H>
#include "ERF_Constants.H"
Include dependency graph for ERF_MOSTUtils.H:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Classes

struct  most_data
 
struct  similarity_funs
 
struct  most_flux_scale
 
struct  most_aitken
 

Functions

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_bracket_floor (amrex::Real C)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_bracket (amrex::Real C, amrex::Real psi)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_rib_resid (const similarity_funs &sfuns, amrex::Real C, amrex::Real Rib, amrex::Real zeta)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool most_rib_zeta_is_root (const similarity_funs &sfuns, amrex::Real C, amrex::Real Rib, amrex::Real zeta, amrex::Real delta)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_rib_zeta_bracketed (const similarity_funs &sfuns, amrex::Real C, amrex::Real Rib, amrex::Real zeta0, amrex::Real tol)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_clamp_zeta (amrex::Real zeta, amrex::Real zeta_fold, bool &at_fold)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE most_flux_scale most_fold_flux_scale (bool at_fold, amrex::Real zeta_fold, amrex::Real ustar, amrex::Real thv, amrex::Real tflux_s, amrex::Real tflux_q, bool spec_qflux, amrex::Real kappa, amrex::Real gravity, amrex::Real zref)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void most_flux_outputs (const most_data &mdata, bool spec_qflux, bool at_fold, amrex::Real zeta_fold, amrex::Real C, amrex::Real psi_h, amrex::Real ustar, amrex::Real zref, amrex::Real tm, amrex::Real tvm, amrex::Real qvm, amrex::Real qflux, amrex::Real &olen, amrex::Real &t_surf, amrex::Real &t_star, amrex::Real &q_surf, amrex::Real &q_star)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real air_viscosity (amrex::Real T_degK)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Charnock_roughness (amrex::Real ustar, amrex::Real Cnk_a, amrex::Real nu, bool visc)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Mod_Charnock_roughness (amrex::Real ustar, amrex::Real Cnk_b)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real COARE3_roughness (amrex::Real zref, amrex::Real umm, amrex::Real ustar, amrex::Real nu, bool visc)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Donelan_roughness (amrex::Real ustar, amrex::Real nu, bool visc)
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real WaveCoupled_roughness (amrex::Real Hwave, amrex::Real Lwave, amrex::Real eps, amrex::Real ustar, amrex::Real nu, bool visc)
 
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real bound_inverted_roughness (amrex::Real z0, amrex::Real zref) noexcept
 
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_roughness (amrex::Real zref, amrex::Real Olen, amrex::Real umm, amrex::Real ustar)
 

Variables

constexpr amrex::Real most_zeta_lim = amrex::Real(100.0)
 
constexpr amrex::Real most_min_ustar = amrex::Real(0.001)
 
constexpr amrex::Real inverted_roughness_lo = amrex::Real(1.0e-7)
 

Function Documentation

◆ air_viscosity()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real air_viscosity ( amrex::Real  T_degK)

Empirical kinematic viscosity [m2/s] formula from Andreas (1989) CRREL Rep. 89-11, valid between -173 and 277 deg C.

Parameters
[in]T_degKair temperature in Kelvin
582 {
583  amrex::Real TC = T_degK - amrex::Real(273.15);
584  return amrex::Real(1.326e-5)*(one + amrex::Real(6.542e-3)*TC + amrex::Real(8.301e-6)*TC*TC - amrex::Real(4.84e-9)*TC*TC*TC);
585 }
constexpr amrex::Real one
Definition: ERF_NumericalConstants.H:37
amrex::Real Real
Definition: ERF_ShocInterface.H:19

Referenced by adiabatic_charnock::iterate_flux(), adiabatic_donelan::iterate_flux(), adiabatic_wave_coupled::iterate_flux(), surface_flux_charnock::iterate_flux(), surface_flux_donelan::iterate_flux(), surface_temp_charnock::iterate_flux(), surface_temp_donelan::iterate_flux(), surface_flux_wave_coupled::iterate_flux(), and surface_temp_wave_coupled::iterate_flux().

Here is the caller graph for this function:

◆ bound_inverted_roughness()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real bound_inverted_roughness ( amrex::Real  z0,
amrex::Real  zref 
)
noexcept

Bounds on the roughness recovered by Compute_roughness. The inversion is unbounded above: in weak wind under strong stability (u* from an LSM or bulk flux large relative to kappa*U, psi_m << 0) it returns z0 >> zref. The upper bound is above every tabulated land-cover roughness in NoahmpTable.TBL (Z0MVT <= 1.10 m) and below the 2 m diagnostic height; z0 is also kept below half of zref so that ln(zref/z0) stays positive.

740 {
741  const amrex::Real z0_lo = inverted_roughness_lo;
742  const amrex::Real z0_hi = amrex::min(amrex::Real(1.5), amrex::Real(0.5) * zref);
743  return amrex::min(amrex::max(z0, z0_lo), z0_hi);
744 }
Real z0
Definition: ERF_InitCustomPertVels_ScalarAdvDiff.H:12
constexpr amrex::Real inverted_roughness_lo
Definition: ERF_MOSTUtils.H:726

Referenced by Compute_roughness().

Here is the caller graph for this function:

◆ Charnock_roughness()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Charnock_roughness ( amrex::Real  ustar,
amrex::Real  Cnk_a,
amrex::Real  nu,
bool  visc 
)

Charnock roughness with optional aerodynamically-smooth contribution

Smooth-flow limit follows z0 = 0.11 nu / u* (Smith 1988; ECMWF; WRF)

Parameters
[in]ustarfriction velocity
[in]cnk_acharnock constant
[in]nuintrinsic fluid viscosity
[in]viscflag to apply smooth flow limit
604 {
605  amrex::Real z0 = (Cnk_a / CONST_GRAV) * ustar * ustar;
606  if (visc) {
607  z0 += amrex::Real(0.11) * nu / std::max(ustar, amrex::Real(0.01));
608  }
609  // From Davis et al 2008 MWR
610  return std::min(std::max(z0, amrex::Real(1.27e-7)), amrex::Real(2.85e-3));
611 }
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:72
@ ustar
Definition: ERF_SLM.H:28

Referenced by adiabatic_charnock::iterate_flux(), surface_flux_charnock::iterate_flux(), and surface_temp_charnock::iterate_flux().

Here is the caller graph for this function:

◆ COARE3_roughness()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real COARE3_roughness ( amrex::Real  zref,
amrex::Real  umm,
amrex::Real  ustar,
amrex::Real  nu,
bool  visc 
)

COARE3.0, following WRF phys/module_sf_mynn.F

Smooth-flow limit follows z0 = 0.11 nu / u* (Smith 1988; ECMWF; WRF)

Parameters
[in]zrefreference height for the wind speed
[in]ummhorizontal wind speed magnitude at zref
[in]ustarfriction velocity
[in]nuintrinsic fluid viscosity
[in]viscflag to apply smooth flow limit
650 {
651  amrex::Real ws10m = umm*std::log(amrex::Real(10.)/amrex::Real(1e-4)) / std::log(zref/amrex::Real(1e-4));
652  amrex::Real Czc = amrex::Real(0.011) + amrex::Real(0.007)*std::min(std::max((ws10m-amrex::Real(10.))/amrex::Real(8.), amrex::Real(0.0)), amrex::Real(1.0));
653  amrex::Real z0 = (Czc / CONST_GRAV) * ustar * ustar;
654  if (visc) {
655  z0 += amrex::Real(0.11) * nu / std::max(ustar, amrex::Real(0.01));
656  }
657  // From Davis et al 2008 MWR
658  return std::min(std::max(z0, amrex::Real(1.27e-7)), amrex::Real(2.85e-3));
659 }

Referenced by adiabatic_charnock::iterate_flux(), surface_flux_charnock::iterate_flux(), and surface_temp_charnock::iterate_flux().

Here is the caller graph for this function:

◆ Compute_roughness()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_roughness ( amrex::Real  zref,
amrex::Real  Olen,
amrex::Real  umm,
amrex::Real  ustar 
)

Invert log-law to get roughness

Parameters
[in]zrefreference height for the wind speed
[in]OlenObukhov length
[in]ummhorizontal wind speed magnitude at zref
[in]ustarfriction velocity
761 {
762  similarity_funs sfuns;
763  amrex::Real zeta = zref / Olen;
764  amrex::Real psi_m = sfuns.calc_psi_m2(zeta);
765  // z0 = zref / exp(arg) is below the lower bound once arg > log(zref / z0_lo),
766  // so the bound is returned without evaluating exp(arg), which overflows when
767  // u* sits at its floor of machine epsilon (zero stress: bulk_coeff with
768  // Cd = 0, or an LSM that returns none). At or below the threshold exp(arg)
769  // is finite and the inversion runs as before, so no other input changes.
770  const amrex::Real arg = KAPPA*umm/ustar + psi_m;
771  if (arg > std::log(zref / inverted_roughness_lo)) {
773  }
774  amrex::Real z0 = zref / std::exp(arg);
775  return bound_inverted_roughness(z0, zref);
776 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:71
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real bound_inverted_roughness(amrex::Real z0, amrex::Real zref) noexcept
Definition: ERF_MOSTUtils.H:739
Definition: ERF_MOSTUtils.H:67
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_m2(amrex::Real zeta) const
Definition: ERF_MOSTUtils.H:79

Referenced by SurfaceLayer::compute_sfc_params_from_lsm_fluxes(), and SurfaceLayer::compute_SurfaceLayer_bcs().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ Donelan_roughness()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Donelan_roughness ( amrex::Real  ustar,
amrex::Real  nu,
bool  visc 
)

Updated Donelan 2004 as found in AHW

Matches implementations in WRF phys/physics_mmm/sf_sfclayrev.F90 and phys/module_sf_mynn.F

Smooth flow limit is intrinsic in zn2.

Parameters
[in]ustarfriction velocity
[in]nuintrinsic fluid viscosity
[in]viscflag to apply smooth flow limit
679 {
680  constexpr amrex::Real ozo{amrex::Real(1.59e-5)};
681  amrex::Real zw = std::min(std::pow(ustar/amrex::Real(1.06), amrex::Real(0.3)),amrex::Real(1.0));
682  amrex::Real zn1 = amrex::Real(0.011)*ustar*ustar/CONST_GRAV + ozo;
683  amrex::Real zn2 = amrex::Real(10.)*std::exp(-amrex::Real(9.5)*std::pow(ustar, -amrex::Real(.3333)));
684  if (visc) {
685  zn2 += amrex::Real(0.11) * nu / std::max(ustar, amrex::Real(0.01));
686  }
687  amrex::Real z0 = (amrex::Real(1.0)-zw) * zn1 + zw * zn2;
688  // From Davis et al 2008 MWR
689  return std::min(std::max(z0, amrex::Real(1.27e-7)), amrex::Real(2.85e-3));
690 }

Referenced by adiabatic_donelan::iterate_flux(), surface_flux_donelan::iterate_flux(), and surface_temp_donelan::iterate_flux().

Here is the caller graph for this function:

◆ Mod_Charnock_roughness()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Mod_Charnock_roughness ( amrex::Real  ustar,
amrex::Real  Cnk_b 
)

Modified Charnock roughness

Smooth-flow limit is intrinsic.

Parameters
[in]ustarfriction velocity
[in]cnk_bmodified charnock constant
626 {
627  amrex::Real z0 = std::exp( (amrex::Real(2.7)*ustar - amrex::Real(1.8)/Cnk_b) / (ustar + amrex::Real(0.17)/Cnk_b) );
628  return std::min(std::max(z0, amrex::Real(1.27e-7)), amrex::Real(1.0e-2));
629 }

Referenced by adiabatic_mod_charnock::iterate_flux(), surface_flux_mod_charnock::iterate_flux(), and surface_temp_mod_charnock::iterate_flux().

Here is the caller graph for this function:

◆ most_bracket()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_bracket ( amrex::Real  C,
amrex::Real  psi 
)

Log-law bracket ln(zref/z0) - psi of the MOST flux kernels, held at or above most_bracket_floor(C).

In strongly unstable air psi exceeds C = ln(zref/z0) and the bracket changes sign, which turns u* negative and puts the diagnosed surface temperature on the wrong side of the air for a prescribed flux. With the floor the bracket is at least 1/2, so u* stays positive and finite for every C.

Parameters
[in]Cln(zref/z0)
[in]psistability correction (momentum or heat)
256 {
257  return amrex::max(C - psi, most_bracket_floor(C));
258 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_bracket_floor(amrex::Real C)
Definition: ERF_MOSTUtils.H:57

Referenced by adiabatic::iterate_flux(), adiabatic_eb::iterate_flux(), adiabatic_mod_charnock::iterate_flux(), adiabatic_charnock::iterate_flux(), adiabatic_donelan::iterate_flux(), adiabatic_wave_coupled::iterate_flux(), surface_flux_charnock::iterate_flux(), surface_flux_mod_charnock::iterate_flux(), surface_flux_donelan::iterate_flux(), surface_temp_charnock::iterate_flux(), surface_temp_mod_charnock::iterate_flux(), surface_temp_donelan::iterate_flux(), surface_flux_wave_coupled::iterate_flux(), surface_temp_wave_coupled::iterate_flux(), surface_flux::iterate_flux(), surface_temp::iterate_flux(), surface_temp_eb::iterate_flux(), surface_flux_eb::iterate_flux(), most_flux_outputs(), and most_rib_resid().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ most_bracket_floor()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_bracket_floor ( amrex::Real  C)

Lower bound of the log-law bracket ln(zref/z0) - psi of the MOST flux kernels: C/2 held between 1/2 and 1.

The bound is 1 for zref >= e^2 z0 and C/2 down to zref = e z0, so it does not exceed the neutral bracket C wherever zref >= e^(1/2) z0. The lower limit of 1/2 keeps the bracket, and so u*, positive and finite when z0 approaches or exceeds zref (C <= 0).

The bound is set by the under-relaxed u* iteration, not by the physics. At an unstable fixed point the relaxed map has slope (1 - alpha) - alpha s / bracket, with s = -3 (z/L) dpsi_m/d(z/L) between 1.3 (z/L = -0.5) and 2.4 (z/L = -50) for Businger-Dyer. With alpha = 0.5 the iteration converges only for bracket > s/3 (0.4 to 0.8), so a bound of 1 keeps the switch between the bounded and free branches inside the convergent range, while 0.1 C (0.3 at C = 3) does not and the iteration oscillates between two values. If alpha changes, the bound needs revisiting; see PR #4866.

Parameters
[in]Cln(zref/z0)
58 {
59  return amrex::max(amrex::min(amrex::Real(0.5) * C, amrex::Real(1.0)), amrex::Real(0.5));
60 }

Referenced by similarity_funs::calc_zeta_fold(), and most_bracket().

Here is the caller graph for this function:

◆ most_clamp_zeta()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_clamp_zeta ( amrex::Real  zeta,
amrex::Real  zeta_fold,
bool &  at_fold 
)

z/L of the prescribed-flux kernels, bounded below by -most_zeta_lim and above by the fold (see similarity_funs::calc_zeta_fold), where the prescribed stable flux stops having a solution.

Parameters
[in]zetaz/L from the current u* and flux
[in]zeta_foldz/L at the fold
[out]at_foldwhether the fold bound was applied
397 {
398  at_fold = (zeta >= zeta_fold);
399  return amrex::max(amrex::min(zeta, zeta_fold), -most_zeta_lim);
400 }
constexpr amrex::Real most_zeta_lim
Definition: ERF_MOSTUtils.H:263

Referenced by surface_flux_charnock::iterate_flux(), surface_flux_mod_charnock::iterate_flux(), surface_flux_donelan::iterate_flux(), surface_flux_wave_coupled::iterate_flux(), surface_flux::iterate_flux(), and surface_flux_eb::iterate_flux().

Here is the caller graph for this function:

◆ most_flux_outputs()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void most_flux_outputs ( const most_data &  mdata,
bool  spec_qflux,
bool  at_fold,
amrex::Real  zeta_fold,
amrex::Real  C,
amrex::Real  psi_h,
amrex::Real  ustar,
amrex::Real  zref,
amrex::Real  tm,
amrex::Real  tvm,
amrex::Real  qvm,
amrex::Real  qflux,
amrex::Real &  olen,
amrex::Real &  t_surf,
amrex::Real &  t_star,
amrex::Real &  q_surf,
amrex::Real &  q_star 
)

Surface state of a prescribed-flux kernel once its u* iteration has converged.

Sets t_surf, t_star and q_star (and q_surf when the moisture flux is prescribed). At the fold the prescribed fluxes are reduced by most_fold_flux_scale and the Obukhov length is set to the fold value. u* is floored at most_min_ustar here, so every kernel applies the same floor.

Parameters
[in]mdataMOST data (prescribed fluxes, kappa, gravity)
[in]spec_qfluxwhether the moisture flux is prescribed
[in]at_foldwhether z/L was held at the fold on the last pass
[in]zeta_foldz/L at the fold
[in]Cln(zref/z0)
[in]psi_hheat stability correction of the last pass
[in]ustarfriction velocity (floored at most_min_ustar)
[in]zrefreference height
[in]tmpotential temperature at zref
[in]tvmvirtual potential temperature at zref
[in]qvmwater vapor at zref
[in]qfluxmoisture flux of the last pass
[in,out]olenObukhov length
[out]t_surfsurface potential temperature
[out]t_startemperature scale
[in,out]q_surfsurface water vapor (read when the moisture flux is diagnosed)
[out]q_starmoisture scale
498 {
499  ustar = amrex::max(ustar, amrex::Real(most_min_ustar));
500  const most_flux_scale scale = most_fold_flux_scale(at_fold, zeta_fold, ustar, tvm,
501  mdata.surf_temp_flux * (one + epsv * qvm),
502  qflux * epsv * tm, spec_qflux,
503  mdata.kappa, mdata.gravity, zref);
504  if (at_fold) { olen = zref / zeta_fold; }
505  const amrex::Real CmPsih = most_bracket(C, psi_h);
506  const amrex::Real hflux = scale.temp * mdata.surf_temp_flux;
507  t_surf = hflux * CmPsih / (ustar * mdata.kappa) + tm;
508  t_star = -hflux / ustar;
509  if (spec_qflux) {
510  const amrex::Real mflux = scale.moist * mdata.surf_moist_flux;
511  q_surf = mflux * CmPsih / (ustar * mdata.kappa) + qvm;
512  q_star = -mflux / ustar;
513  } else {
514  q_star = mdata.kappa * (qvm - q_surf) / CmPsih;
515  }
516 }
constexpr amrex::Real epsv
Definition: ERF_Constants.H:56
constexpr amrex::Real most_min_ustar
Definition: ERF_MOSTUtils.H:462
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE most_flux_scale most_fold_flux_scale(bool at_fold, amrex::Real zeta_fold, amrex::Real ustar, amrex::Real thv, amrex::Real tflux_s, amrex::Real tflux_q, bool spec_qflux, amrex::Real kappa, amrex::Real gravity, amrex::Real zref)
Definition: ERF_MOSTUtils.H:439
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_bracket(amrex::Real C, amrex::Real psi)
Definition: ERF_MOSTUtils.H:255
@ olen
Definition: ERF_SLM.H:104
amrex::Real surf_moist_flux
Moisture flux.
Definition: ERF_MOSTUtils.H:19
amrex::Real kappa
von Karman constant
Definition: ERF_MOSTUtils.H:16
amrex::Real gravity
Acceleration due to gravity (m/s^2)
Definition: ERF_MOSTUtils.H:17
amrex::Real surf_temp_flux
Heat flux TODO: decide whether this is <θ'w'> or <θv'w'> under moist conditions.
Definition: ERF_MOSTUtils.H:18
Definition: ERF_MOSTUtils.H:406
amrex::Real temp
factor on surf_temp_flux
Definition: ERF_MOSTUtils.H:407
amrex::Real moist
factor on surf_moist_flux
Definition: ERF_MOSTUtils.H:408

Referenced by surface_flux_charnock::iterate_flux(), surface_flux_mod_charnock::iterate_flux(), surface_flux_donelan::iterate_flux(), surface_flux_wave_coupled::iterate_flux(), surface_flux::iterate_flux(), and surface_flux_eb::iterate_flux().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ most_fold_flux_scale()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE most_flux_scale most_fold_flux_scale ( bool  at_fold,
amrex::Real  zeta_fold,
amrex::Real  ustar,
amrex::Real  thv,
amrex::Real  tflux_s,
amrex::Real  tflux_q,
bool  spec_qflux,
amrex::Real  kappa,
amrex::Real  gravity,
amrex::Real  zref 
)

Part of a prescribed stable flux the wind can carry at the fold.

The virtual heat flux is tflux_s + tflux_q, with tflux_s = surf_temp_flux (1 + epsv qv) and tflux_q = qflux epsv theta. When z/L is held at the fold (see similarity_funs::calc_zeta_fold), the wind is below the minimum the prescribed flux needs and carries only -u*^3 thv zeta_fold / (kappa g zref). Only the prescribed components that are downward drive the fold, so only they are reduced, by a common factor, until the net virtual flux equals that value. An upward component, or a moisture flux diagnosed from q_surf, is left unchanged; if no prescribed component is downward, nothing is reduced. The factor is not taken below 0, so when a diagnosed condensation flux alone is stronger than the fold value, the downward prescribed components are removed and the net flux remains stronger than the fold value. Returns 1 for both factors away from the fold.

Parameters
[in]at_foldwhether z/L was held at the fold
[in]zeta_foldz/L at the fold
[in]ustarfriction velocity
[in]thvvirtual potential temperature at zref
[in]tflux_ssensible part of the virtual heat flux
[in]tflux_qmoisture part of the virtual heat flux
[in]spec_qfluxwhether the moisture flux is prescribed
[in]kappavon Karman constant
[in]gravitygravitational acceleration
[in]zrefreference height
443 {
444  most_flux_scale scale;
445  const amrex::Real tflux = tflux_s + tflux_q;
446  if (!at_fold || tflux >= zero) { return scale; }
447  const amrex::Real tflux_fold = -ustar * ustar * ustar * thv * zeta_fold / (kappa * gravity * zref);
448  if (tflux_fold <= tflux) { return scale; }
449  const bool down_s = (tflux_s < zero);
450  const bool down_q = (spec_qflux && tflux_q < zero);
451  const amrex::Real down = (down_s ? tflux_s : zero) + (down_q ? tflux_q : zero);
452  if (down >= zero) { return scale; }
453  const amrex::Real r = amrex::max((down + tflux_fold - tflux) / down, amrex::Real(0.0));
454  if (down_s) { scale.temp = r; }
455  if (down_q) { scale.moist = r; }
456  return scale;
457 }
constexpr amrex::Real zero
Definition: ERF_NumericalConstants.H:36

Referenced by most_flux_outputs().

Here is the caller graph for this function:

◆ most_rib_resid()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_rib_resid ( const similarity_funs &  sfuns,
amrex::Real  C,
amrex::Real  Rib,
amrex::Real  zeta 
)

Residual zeta - Rib (C - psi_m)^2 / (C - psi_h) of the prescribed-temperature kernels' z/L equation, with the Jimenez functions and the brackets of most_bracket.

Parameters
[in]sfunssimilarity functions
[in]Cln(zref/z0)
[in]Ribbulk Richardson number
[in]zetaz/L
278 {
279  const amrex::Real CmPsim = most_bracket(C, sfuns.calc_psi_m2(zeta));
280  const amrex::Real CmPsih = most_bracket(C, sfuns.calc_psi_h2(zeta));
281  return zeta - Rib * CmPsim * CmPsim / CmPsih;
282 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_h2(amrex::Real zeta) const
Definition: ERF_MOSTUtils.H:104

Referenced by most_rib_zeta_bracketed(), and most_rib_zeta_is_root().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ most_rib_zeta_bracketed()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_rib_zeta_bracketed ( const similarity_funs &  sfuns,
amrex::Real  C,
amrex::Real  Rib,
amrex::Real  zeta0,
amrex::Real  tol 
)

Solve zeta = Rib (C - psi_m)^2 / (C - psi_h) of the prescribed-temperature kernels by bracketing and bisection, with the Jimenez functions and the brackets of most_bracket.

The relaxed fixed-point iteration stalls where zeta - Rib F(zeta) nearly touches zero without crossing it, which the Jimenez stable functions produce when z0 is comparable to or above zref (C below about 1): each pass then moves zeta by about the tolerance. From zeta0 this steps, with doubling steps, in the direction the iteration moves until the residual changes sign, and bisects that interval, so it returns the root the iteration was heading to, or the next one past a near-touch. The first step is limited so that two nearby roots are not stepped over together. The search stays within +-most_zeta_lim, and returns that bound when no root lies inside it, as the prescribed-flux kernels bound zeta.

Parameters
[in]sfunssimilarity functions
[in]Cln(zref/z0)
[in]Ribbulk Richardson number
[in]zeta0z/L to start from
[in]tolwidth of the final interval
332 {
333  auto resid = [&] (amrex::Real zeta) { return most_rib_resid(sfuns, C, Rib, zeta); };
334 
335  amrex::Real a = zeta0;
336  amrex::Real ra = resid(a);
337  if (ra == zero) { return a; }
338 
339  // A non-finite residual (a NaN or infinite C, Rib or zeta0) cannot be bracketed: with it the
340  // step below is NaN and the search never reaches zlim. Return it, so the bad input shows up
341  // in the caller's results rather than as a hang.
342  if (!amrex::Math::isfinite(ra)) { return ra; }
343 
344  // Step against the residual, within +-most_zeta_lim, until it changes sign
345  const amrex::Real dir = (ra > zero) ? amrex::Real(-1.0) : amrex::Real(1.0);
346  const amrex::Real zlim = dir * most_zeta_lim;
347  auto step = [&] (amrex::Real z, amrex::Real dz) {
348  return (dir > zero) ? amrex::min(z + dz, zlim) : amrex::max(z - dz, zlim);
349  };
350  amrex::Real h = amrex::max(amrex::min(std::abs(ra),
351  amrex::Real(0.1) * amrex::max(amrex::Real(one), std::abs(a))),
352  tol);
353  amrex::Real b = step(a, h);
354  amrex::Real rb = resid(b);
355  // With finite values the doubling step reaches zlim in well under 200 steps (from tol to
356  // 2 most_zeta_lim); the cap only guarantees that this loop ends
357  int nstep = 0;
358  while ((rb > zero) == (ra > zero) && b != zlim && nstep < 200) {
359  a = b;
360  ra = rb;
361  h *= two;
362  b = step(a, h);
363  rb = resid(b);
364  ++nstep;
365  }
366  // No root within the bound: hold zeta there
367  if ((rb > zero) == (ra > zero)) { return zlim; }
368 
369  // Bisect [a, b]
370  int n = 0;
371  while (std::abs(b - a) > tol && n < 60) {
372  const amrex::Real m = myhalf * (a + b);
373  const amrex::Real rm = resid(m);
374  if ((rm > zero) == (ra > zero)) {
375  a = m;
376  ra = rm;
377  } else {
378  b = m;
379  }
380  ++n;
381  }
382  return myhalf * (a + b);
383 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real most_rib_resid(const similarity_funs &sfuns, amrex::Real C, amrex::Real Rib, amrex::Real zeta)
Definition: ERF_MOSTUtils.H:277
constexpr amrex::Real two
Definition: ERF_NumericalConstants.H:38
constexpr amrex::Real myhalf
Definition: ERF_NumericalConstants.H:41
@ dz
Definition: ERF_AdvanceWDM6.cpp:273
Definition: ERF_ConsoleIO.cpp:17

Referenced by surface_temp::iterate_flux(), and surface_temp_eb::iterate_flux().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ most_rib_zeta_is_root()

AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool most_rib_zeta_is_root ( const similarity_funs &  sfuns,
amrex::Real  C,
amrex::Real  Rib,
amrex::Real  zeta,
amrex::Real  delta 
)

Whether a root of most_rib_resid lies within delta of zeta.

The relaxed fixed-point iteration stops on a small step, which a near-touch of the residual also gives where no root is near; the residual must change sign across [zeta - delta, zeta + delta] for zeta to be taken as a solution.

Parameters
[in]sfunssimilarity functions
[in]Cln(zref/z0)
[in]Ribbulk Richardson number
[in]zetaz/L the relaxed iteration stopped at
[in]deltahalf-width of the interval checked
302 {
303  const amrex::Real rlo = most_rib_resid(sfuns, C, Rib, zeta - delta);
304  const amrex::Real rhi = most_rib_resid(sfuns, C, Rib, zeta + delta);
305  return (rlo > zero) != (rhi > zero) || rlo == zero || rhi == zero;
306 }
@ rhi
Definition: ERF_WSM6.H:360

Referenced by surface_temp::iterate_flux(), and surface_temp_eb::iterate_flux().

Here is the call graph for this function:
Here is the caller graph for this function:

◆ WaveCoupled_roughness()

AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real WaveCoupled_roughness ( amrex::Real  Hwave,
amrex::Real  Lwave,
amrex::Real  eps,
amrex::Real  ustar,
amrex::Real  nu,
bool  visc 
)

Wave coupled roughness

Smooth-flow limit follows z0 = 0.11 nu / u* (Smith 1988; ECMWF; WRF)

Parameters
[in]Hwavewave height
[in]Lwavewave length
[in]epsavoid divide by zero
[in]ustarfriction velocity
[in]nuintrinsic fluid viscosity
[in]viscflag to apply smooth flow limit
714 {
715 
716  amrex::Real z0 = amrex::Real(1200.0) * Hwave * std::pow( Hwave/(Lwave+eps), amrex::Real(4.5) );
717  if (visc) {
718  z0 += amrex::Real(0.11) * nu / std::max(ustar, amrex::Real(0.01));
719  }
720  return std::min(std::max(z0, amrex::Real(1.27e-7)), amrex::Real(1.0e-2));
721 }

Referenced by adiabatic_wave_coupled::iterate_flux(), surface_flux_wave_coupled::iterate_flux(), and surface_temp_wave_coupled::iterate_flux().

Here is the caller graph for this function:

Variable Documentation

◆ inverted_roughness_lo

constexpr amrex::Real inverted_roughness_lo = amrex::Real(1.0e-7)
constexpr

Lower bound of the roughness recovered by Compute_roughness; shared with the guard in Compute_roughness so the two cannot drift apart.

Referenced by bound_inverted_roughness(), and Compute_roughness().

◆ most_min_ustar

constexpr amrex::Real most_min_ustar = amrex::Real(0.001)
inlineconstexpr

Minimum friction velocity [m/s] of the prescribed-flux kernels' outputs (see most_flux_outputs).

Referenced by most_flux_outputs().

◆ most_zeta_lim

constexpr amrex::Real most_zeta_lim = amrex::Real(100.0)
inlineconstexpr

Bound on |z/L| of the MOST kernels (see most_rib_zeta_bracketed and most_clamp_zeta).

Referenced by most_clamp_zeta(), and most_rib_zeta_bracketed().