ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
ERF_CanopyBiophysics.H File Reference
#include <cmath>
#include <AMReX_MultiFab.H>
#include <ERF_Constants.H>
#include <ERF_DataStruct.H>
#include <ERF_IndexDefines.H>
Include dependency graph for ERF_CanopyBiophysics.H:
This graph shows which files directly or indirectly include this file:

Go to the source code of this file.

Functions

AMREX_INLINE void AddCanopyBiophysicsHeatSources (amrex::MultiFab &cell_source, const amrex::MultiFab &S_data, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &zvel, const amrex::MultiFab *frontal_area, const amrex::MultiFab &base_state, const SolverChoice &solver_choice)
 

Function Documentation

◆ AddCanopyBiophysicsHeatSources()

AMREX_INLINE void AddCanopyBiophysicsHeatSources ( amrex::MultiFab &  cell_source,
const amrex::MultiFab &  S_data,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  zvel,
const amrex::MultiFab *  frontal_area,
const amrex::MultiFab &  base_state,
const SolverChoice solver_choice 
)

Add the portion of the ICLASS canopy-biophysics model exercised by the fixed-leaf-potential-temperature configuration. The source uses the leaf area density produced by ForestDrag and adds sensible heat to rho-theta. When moisture is active, it also adds the corresponding transpiration flux using the reference model's default minimum stomatal conductance.

Parameters
[in,out]cell_sourcestaged source terms for the conserved state
[in]S_dataconserved atmospheric state
[in]xvelx-face velocity
[in]yvely-face velocity
[in]zvelz-face velocity
[in]frontal_areacell-centered leaf/frontal area density
[in]base_statehydrostatic pressure and Exner state
[in]solver_choiceactive physics and canopy configuration
36 {
37  BL_PROFILE("AddCanopyBiophysicsHeatSources");
38 
39  if (frontal_area == nullptr || !solver_choice.forest_biophysics_heat) {
40  return;
41  }
42 
43  const bool has_moisture = solver_choice.moisture_type != MoistureType::None;
44  const amrex::Real leaf_theta = solver_choice.forest_leaf_theta_fixed;
45 
46  // Defaults used by the ICLASS canopy implementation for the fixed-theta
47  // path. Radiation and the bisection solve are not needed in this mode.
48  constexpr amrex::Real leaf_width = amrex::Real(0.05); // m
49  constexpr amrex::Real min_wind = amrex::Real(0.01); // m/s
50  constexpr amrex::Real boundary_coeff = amrex::Real(100.0);
51  constexpr amrex::Real min_stomatal_conductance = amrex::Real(0.01); // mol/(m^2 s)
52  constexpr amrex::Real gas_constant = amrex::Real(8.314); // J/(mol K)
53 
54  // Make namespace-scope constants explicit lambda captures for CUDA. The
55  // host compiler can resolve these constexpr variables directly, whereas
56  // NVCC does not expose them as device symbols from an implicit [=] capture.
57  const amrex::Real zero_d = zero;
58  const amrex::Real one_d = one;
59  const amrex::Real two_d = two;
60  const amrex::Real myhalf_d = myhalf;
61  const amrex::Real cp_d = Cp_d;
62  const amrex::Real rdo_rv_d = RdoRv;
63  const amrex::Real lv_d = L_v;
64 
65 #ifdef _OPENMP
66 #pragma omp parallel if (amrex::Gpu::notInLaunchRegion())
67 #endif
68  for (amrex::MFIter mfi(cell_source, amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi) {
69  const amrex::Box& box = mfi.tilebox();
70 
71  const auto& source = cell_source.array(mfi);
72  const auto& state = S_data.const_array(mfi);
73  const auto& u = xvel.const_array(mfi);
74  const auto& v = yvel.const_array(mfi);
75  const auto& w = zvel.const_array(mfi);
76  const auto& leaf_area_density = frontal_area->const_array(mfi);
77  const auto& base = base_state.const_array(mfi);
78 
79  amrex::ParallelFor(box, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
80  {
81  const amrex::Real lad = leaf_area_density(i, j, k);
82  if (lad <= zero_d) {
83  return;
84  }
85 
86  const amrex::Real rho = state(i, j, k, Rho_comp);
87  const amrex::Real theta_air = state(i, j, k, RhoTheta_comp) / rho;
88  const amrex::Real exner = base(i, j, k, BaseState::pi0_comp);
89  const amrex::Real pressure = base(i, j, k, BaseState::p0_comp);
90  const amrex::Real temp_air = theta_air * exner;
91  const amrex::Real temp_leaf = leaf_theta * exner;
92 
93  const amrex::Real u_cc = myhalf_d * (u(i, j, k) + u(i+1, j, k));
94  const amrex::Real v_cc = myhalf_d * (v(i, j, k) + v(i, j+1, k));
95  const amrex::Real w_cc = myhalf_d * (w(i, j, k) + w(i, j, k+1));
96  const amrex::Real wind = std::sqrt(u_cc*u_cc + v_cc*v_cc + w_cc*w_cc);
97  const amrex::Real resistance_boundary =
98  boundary_coeff * std::sqrt(leaf_width / amrex::max(wind, min_wind));
99 
100  const amrex::Real sensible_heat =
101  two_d * rho * cp_d * (temp_leaf - temp_air) / resistance_boundary;
102  source(i, j, k, RhoTheta_comp) +=
103  sensible_heat * lad / (cp_d * exner);
104 
105  if (has_moisture) {
106  const amrex::Real qv = state(i, j, k, RhoQ1_comp) / rho;
107  const amrex::Real temp_celsius = temp_leaf - amrex::Real(273.15);
108  const amrex::Real saturation_pressure =
109  amrex::Real(611.2) * std::exp(amrex::Real(17.67) * temp_celsius /
110  (temp_celsius + amrex::Real(243.5)));
111  const amrex::Real qv_saturation =
112  rdo_rv_d * saturation_pressure /
113  amrex::max(pressure - (one_d - rdo_rv_d) * saturation_pressure, one_d);
114  const amrex::Real resistance_stomatal =
115  pressure /
116  amrex::max(min_stomatal_conductance * gas_constant * temp_air,
117  amrex::Real(1.0e-20));
118  const amrex::Real latent_flux =
119  rho * lv_d * amrex::max(qv_saturation - qv, zero_d) /
120  (amrex::Real(1.075) * resistance_boundary + resistance_stomatal);
121 
122  source(i, j, k, RhoQ1_comp) += latent_flux * lad / lv_d;
123  }
124  });
125  }
126 }
constexpr amrex::Real Cp_d
Definition: ERF_Constants.H:49
constexpr amrex::Real two
Definition: ERF_Constants.H:10
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
constexpr amrex::Real L_v
Definition: ERF_Constants.H:59
constexpr amrex::Real RdoRv
Definition: ERF_Constants.H:57
#define Rho_comp
Definition: ERF_IndexDefines.H:39
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:40
#define RhoQ1_comp
Definition: ERF_IndexDefines.H:45
ParallelFor(fab_box, [=] AMREX_GPU_DEVICE(int i, int j, int k) { qrcuten_arr(i, j, k)=Real(0);qscuten_arr(i, j, k)=Real(0);qicuten_arr(i, j, k)=Real(0);})
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
amrex::Real Real
Definition: ERF_ShocInterface.H:19
@ pi0_comp
Definition: ERF_IndexDefines.H:78
@ p0_comp
Definition: ERF_IndexDefines.H:77
@ rho
Definition: ERF_Kessler.H:24
@ qv
Definition: ERF_Kessler.H:30
@ xvel
Definition: ERF_IndexDefines.H:215
@ zvel
Definition: ERF_IndexDefines.H:217
@ yvel
Definition: ERF_IndexDefines.H:216
amrex::Real forest_leaf_theta_fixed
Definition: ERF_DataStruct.H:2179
MoistureType moisture_type
Moisture or microphysics model.
Definition: ERF_DataStruct.H:2124
bool forest_biophysics_heat
Definition: ERF_DataStruct.H:2178
Here is the call graph for this function: