ERF
Energy Research and Forecasting: An Atmospheric Modeling Code
IBFaceSet Class Reference

#include <ERF_IBFaceSet.H>

Collaboration diagram for IBFaceSet:

Classes

struct  SunState
 Sun and irradiances of the last compute_shortwave() call, for reports. More...
 
struct  TwoStreamSun
 The two-stream sun of the next compute_shortwave() (sun_mode = two_stream). More...
 

Public Member Functions

 IBFaceSet (const IBSEBParams &params, int lev)
 
void build (const amrex::MultiFab &blanking, const amrex::Geometry &geom)
 
void add_outside_occluders (const IBFaceSet &coarser, const amrex::IntVect &ratio, const amrex::BoxArray &grids_crse)
 
void set_two_stream_sun (amrex::Real declination, amrex::Real hour_angle, amrex::Real latitude_deg, amrex::Real toa_irradiance)
 
void map_buildings_to_level0 (const IBFaceSet &coarser, const amrex::IntVect &ratio)
 
int building_level0 (int b) const
 
void release_labels ()
 
int n_faces () const
 
int n_buildings () const
 
int n_layers () const
 
int state_ncomp () const
 
int n_slots () const
 
const amrex::BoxArray & state_boxarray () const
 
bool has_state () const
 
amrex::MultiFab make_state () const
 
void compute_view_fractions ()
 
void compute_longwave (const amrex::MultiFab &cons, const TwoStreamCanopyView &canopy=TwoStreamCanopyView{})
 
void assign_materials ()
 
void compute_ground (amrex::Real dt)
 
void solve_balance (amrex::Real dt)
 
void compute_sensible (const amrex::MultiFab &cons, const amrex::MultiFab &xvel, const amrex::MultiFab &yvel, const amrex::MultiFab &zvel, amrex::Real c_p, const amrex::MultiFab *olen_ground=nullptr, const amrex::MultiFab *pblh_ground=nullptr, amrex::Real z_i_bulk=0.0)
 
void add_heat_flux_to_source (amrex::MultiFab &source, const amrex::MultiFab &cons, const amrex::Geometry &geom, amrex::Real c_p, amrex::Real rdOcp) const
 
void compute_shortwave (amrex::Real time, const TwoStreamCanopyView &canopy=TwoStreamCanopyView{})
 
int top_sample_interface () const
 
bool has_faces () const
 
void check_canopy_layout (const amrex::MultiFab &cons, const TwoStreamCanopyView &canopy) const
 
void compute_shortwave_two_stream (const TwoStreamCanopyView &canopy)
 
void compute_longwave_two_stream (const amrex::MultiFab &cons, const TwoStreamCanopyView &canopy)
 
void scatter_field (const amrex::Gpu::DeviceVector< amrex::Real > &v, amrex::MultiFab &out) const
 
void dump_faces (const std::string &prefix) const
 
void scatter_diagnostics (amrex::MultiFab &nfaces, amrex::MultiFab &tskin) const
 
void save_state (amrex::MultiFab &state) const
 
void load_state (const amrex::MultiFab &state)
 
void report (amrex::Real time, int step, bool write_csv) const
 
const SunState & sun () const
 
void print_debug_summary () const
 
void add_cost (double seconds)
 
void set_init_cost (double seconds)
 
const std::vector< int > & fab_start () const
 

Public Attributes

amrex::Gpu::DeviceVector< int > d_i
 
amrex::Gpu::DeviceVector< int > d_j
 
amrex::Gpu::DeviceVector< int > d_k
 Fluid cell of the face. More...
 
amrex::Gpu::DeviceVector< int > d_dir
 Face direction: 0 x, 1 y, 2 z. More...
 
amrex::Gpu::DeviceVector< int > d_side
 Solid neighbour at -1 (low) or +1 (high) side. More...
 
amrex::Gpu::DeviceVector< int > d_slot
 Slot of the face within its cell's checkpoint record. More...
 
amrex::Gpu::DeviceVector< int > d_bid
 Building id (1-based) More...
 
amrex::Gpu::DeviceVector< int > d_mat
 Material id (0 = uniform inputs) More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_albedo
 
amrex::Gpu::DeviceVector< amrex::Real > d_emis
 Optical properties of the face. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_kth
 
amrex::Gpu::DeviceVector< amrex::Real > d_rhocp
 
amrex::Gpu::DeviceVector< amrex::Real > d_thick
 Slab conductivity [W/m/K], heat capacity [J/m3/K], thickness [m]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_area
 Face area [m2]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_xf
 
amrex::Gpu::DeviceVector< amrex::Real > d_yf
 
amrex::Gpu::DeviceVector< amrex::Real > d_zf
 Face centre [m]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_T_skin
 Skin temperature [K]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_T_slab
 Slab layers [K], face-major: [face * n_layers + layer]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_f_sky
 
amrex::Gpu::DeviceVector< amrex::Real > d_f_ground
 
amrex::Gpu::DeviceVector< amrex::Real > d_f_bldg
 View fractions, sum to 1. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_SW_abs
 
amrex::Gpu::DeviceVector< amrex::Real > d_LW_net
 
amrex::Gpu::DeviceVector< amrex::Real > d_H
 
amrex::Gpu::DeviceVector< amrex::Real > d_LE
 
amrex::Gpu::DeviceVector< amrex::Real > d_G
 
amrex::Gpu::DeviceVector< amrex::Real > d_Q_ext
 External incident flux [W/m2], e.g. fire radiation. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_shadow
 1 when the ray to the sun hits a building, else 0 More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_SW_direct_in
 Direct beam incident on the face. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_SW_diffuse_in
 Sky and ground-reflected diffuse incident. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_LW_down_in
 Incoming longwave on the face (sky, ground and walls) More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_T_air
 Air temperature of the fluid cell. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_theta_air
 Potential temperature of the fluid cell [K]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_rho
 Density of the fluid cell [kg/m3]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_U_tan
 Tangential wind speed at the fluid cell [m/s]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_ustar
 Friction velocity of the wall function [m/s]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_H_coeff
 H = H_coeff (theta_skin - theta_air) [W/m2/K]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_hbld
 Height of the face's building column [m]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_w_star
 Convective velocity scale of the step [m/s]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_olen
 Obukhov length used [m] (1e30 when neutral) More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_z_i
 Depth in w* [m]: mixed layer above a roof, building height for a wall. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_LW_ext
 Incoming longwave from sky and ground only [W/m2]. More...
 
amrex::Gpu::DeviceVector< amrex::Real > d_resid
 |balance| after the solve [W/m2] More...
 
amrex::Gpu::DeviceVector< int > d_niter
 Newton iterations of the last solve. More...
 
amrex::Gpu::DeviceVector< int > d_col_top
 
int m_col_i0 = 0
 
int m_col_j0 = 0
 
int m_col_nx = 0
 
int m_col_ny = 0
 
amrex::BoxArray m_state_ba
 
amrex::BoxArray m_xfer_ba
 
amrex::DistributionMapping m_xfer_dm
 
std::vector< int > m_xfer_src
 
amrex::Long m_domain_cells = 0
 Cells of the level, for the debug summary of the field. More...
 

Private Attributes

IBSEBParams m_params
 Copy of the inputs. More...
 
int m_lev = 0
 AMR level, for reports. More...
 
int m_nface = 0
 Faces owned by this rank. More...
 
int m_nslots = 0
 Slots per cell of the checkpoint field (largest face count on a cell) More...
 
int m_top_sample = -1
 Highest interface a face samples, over all ranks (-1: no faces) More...
 
int m_nbld = 0
 Buildings of the level (global) More...
 
std::vector< int > m_fab_start
 See fab_start() More...
 
std::vector< amrex::Long > m_bld_nface
 Faces per building. More...
 
std::vector< amrex::Real > m_bld_area
 Face area per building [m2]. More...
 
amrex::Long m_nface_dir [3] = {0, 0, 0}
 Faces per direction. More...
 
amrex::Real m_area_total = 0.0
 Total face area [m2]. More...
 
std::vector< amrex::Long > m_bld_ncol
 
std::vector< int > m_bld_ilo
 
std::vector< int > m_bld_ihi
 
std::vector< int > m_bld_jlo
 
std::vector< int > m_bld_jhi
 
std::vector< int > m_to_lev0
 
std::vector< int > m_lab
 
int m_lab_i0 = 0
 
int m_lab_j0 = 0
 
int m_lab_nx = 0
 
int m_lab_ny = 0
 
int m_nx = 0
 
int m_ny = 0
 Columns of the domain. More...
 
amrex::IntVect m_dom_lo {AMREX_D_DECL(0, 0, 0)}
 Low corner of the level's domain. More...
 
amrex::Real m_x_lo = 0.0
 
amrex::Real m_y_lo = 0.0
 Domain origin [m]. More...
 
amrex::Real m_dx [3] = {0.0, 0.0, 0.0}
 Cell sizes [m]. More...
 
bool m_per_x = false
 
bool m_per_y = false
 Periodicity in x and y. More...
 
amrex::Real m_col_top_max = 0.0
 Tallest column [m]. More...
 
amrex::Real m_z_ground = 0.0
 Ground height [m] (flat, the domain bottom) More...
 
amrex::Real m_max_path = 0.0
 Ray path cap [m]. More...
 
SunState m_sun
 
TwoStreamSun m_ts_sun
 
double m_cost_s = 0.0
 Wall-clock seconds spent in the per-step update on this rank. More...
 
amrex::Long m_cost_n = 0
 Number of per-step updates timed. More...
 
double m_init_cost_s = 0.0
 Wall-clock seconds of build() and compute_view_fractions() More...
 

Constructor & Destructor Documentation

◆ IBFaceSet()

IBFaceSet::IBFaceSet ( const IBSEBParams &  params,
int  lev 
)
inline

Construct an empty face set for one level; build() fills it.

Parameters
paramsInputs of the balance (copied; the set never reads ParmParse).
levAMR level the set belongs to, for reports only.
78 : m_params(params), m_lev(lev) {}
int m_lev
AMR level, for reports.
Definition: ERF_IBFaceSet.H:528
IBSEBParams m_params
Copy of the inputs.
Definition: ERF_IBFaceSet.H:527

Member Function Documentation

◆ add_cost()

void IBFaceSet::add_cost ( double  seconds)
inline

Add the wall-clock time of one per-step update of this rank, for the cost line of report().

515 { m_cost_s += seconds; ++m_cost_n; }
double m_cost_s
Wall-clock seconds spent in the per-step update on this rank.
Definition: ERF_IBFaceSet.H:564
amrex::Long m_cost_n
Number of per-step updates timed.
Definition: ERF_IBFaceSet.H:565

◆ add_heat_flux_to_source()

void IBFaceSet::add_heat_flux_to_source ( amrex::MultiFab &  source,
const amrex::MultiFab &  cons,
const amrex::Geometry &  geom,
amrex::Real  c_p,
amrex::Real  rdOcp 
) const

Add the face heat flux to the temperature equation: every face deposits H A / (c_p V Pi) into the rho-theta source of its fluid cell (Pi the Exner function, so the flux heats the cell by H A / (c_p V) in temperature). Called after the sources are rebuilt at every slow stage; adds, never overwrites, and uses atomic adds because a corner cell receives several faces.

Face flux into the temperature equation. The rho-theta tendency of the fluid cell is H A / (c_p V Pi); with several faces on one cell the adds are atomic.

1134 {
1135  const auto dx = geom.CellSizeArray();
1136  const Real vol = dx[0] * dx[1] * dx[2];
1137  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1138  const Real* pH = d_H.data(); const Real* pA = d_area.data();
1139  for (MFIter mfi(source); mfi.isValid(); ++mfi) {
1140  const int f0 = m_fab_start[mfi.LocalIndex()];
1141  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
1142  auto const& src = source.array(mfi);
1143  auto const& c = cons.const_array(mfi);
1144  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1145  const int f = f0 + m;
1146  const int i = pi[f], j = pj[f], k = pk[f];
1147  const Real exner = getExnergivenRTh(c(i, j, k, RhoTheta_comp), rdOcp);
1148  const Real tend = pH[f] * pA[f] / (c_p * vol * exner);
1149  Gpu::Atomic::AddNoRet(&src(i, j, k, RhoTheta_comp), tend);
1150  });
1151  }
1152  Gpu::streamSynchronize();
1153 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getExnergivenRTh(const amrex::Real rhotheta, const amrex::Real rdOcp, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:156
#define RhoTheta_comp
Definition: ERF_IndexDefines.H:43
const Real rdOcp
Definition: ERF_InitCustomPert_ABL.H:72
const Real dx
Definition: ERF_InitCustomPert_ABL.H:44
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);})
amrex::Real Real
Definition: ERF_ShocInterface.H:19
amrex::Gpu::DeviceVector< int > d_k
Fluid cell of the face.
Definition: ERF_IBFaceSet.H:436
amrex::Gpu::DeviceVector< amrex::Real > d_H
Definition: ERF_IBFaceSet.H:453
amrex::Gpu::DeviceVector< amrex::Real > d_area
Face area [m2].
Definition: ERF_IBFaceSet.H:444
amrex::Gpu::DeviceVector< int > d_j
Definition: ERF_IBFaceSet.H:436
std::vector< int > m_fab_start
See fab_start()
Definition: ERF_IBFaceSet.H:533
amrex::Gpu::DeviceVector< int > d_i
Definition: ERF_IBFaceSet.H:436
@ cons
Definition: ERF_IndexDefines.H:217
real(c_double), parameter, private pi
Definition: ERF_module_mp_morr_two_moment.F90:100
Here is the call graph for this function:

◆ add_outside_occluders()

void IBFaceSet::add_outside_occluders ( const IBFaceSet &  coarser,
const amrex::IntVect &  ratio,
const amrex::BoxArray &  grids_crse 
)

Add the buildings this refined level does not hold to the column map of its ray cast, from the map of the level below.

build() takes the map from the level's own cells, so on a refined level that covers part of the domain the rays of the shortwave and the view fractions would pass through every building outside it. Every column of the coarser map whose top cell this level's grids do not cover (ERF::ibseb_check_refined_levels() has made sure a building is wholly inside or wholly outside) is copied onto the ratio x ratio columns of this level beneath it, at the same height, and the map grows to the bounding box of both. Only the ray cast reads the result: the building ids, the face heights of the wall function and the reports keep the level's own buildings. Call after build() and before compute_view_fractions(); the coarser set must have had its own call already, so buildings outside several levels pass down the hierarchy.

Parameters
coarserFace set of the level below.
ratioRefinement ratio from the level below to this one.
grids_crseThis level's grids coarsened by ratio.

Copy the coarser level's columns that this level does not cover into the column map of the ray cast (see the header). The coarser map is copied to the host, as both maps are replicated on every rank, and the merged map is uploaded again: one pass over the two built bounding boxes at initialisation.

526 {
527  const size_t ncol_c = static_cast<size_t>(coarser.m_col_nx) * coarser.m_col_ny;
528  if (ncol_c == 0) { return; }
529  std::vector<int> top_c(ncol_c);
530  Gpu::copy(Gpu::deviceToHost, coarser.d_col_top.begin(), coarser.d_col_top.end(), top_c.begin());
531  std::vector<int> top_f(static_cast<size_t>(m_col_nx) * m_col_ny);
532  if (!top_f.empty()) {
533  Gpu::copy(Gpu::deviceToHost, d_col_top.begin(), d_col_top.end(), top_f.begin());
534  }
535  const Real dz_c = coarser.m_dx[2], dz_f = m_dx[2];
536 
537  // Coarse columns outside this level, as the first of their ratio x ratio
538  // columns here (0-based) and a top index of this level; and the bounding
539  // box of those and of the level's own columns.
540  struct Outside { int i0, j0, kt; };
541  std::vector<Outside> outside;
542  const bool own = (m_col_nx > 0 && m_col_ny > 0);
543  int bi0 = own ? m_col_i0 : std::numeric_limits<int>::max();
544  int bj0 = own ? m_col_j0 : std::numeric_limits<int>::max();
545  int bi1 = own ? m_col_i0 + m_col_nx - 1 : std::numeric_limits<int>::min();
546  int bj1 = own ? m_col_j0 + m_col_ny - 1 : std::numeric_limits<int>::min();
547  for (int ci = 0; ci < coarser.m_col_nx; ++ci) {
548  for (int cj = 0; cj < coarser.m_col_ny; ++cj) {
549  const int kc = top_c[static_cast<size_t>(ci) * coarser.m_col_ny + cj];
550  if (kc < 0) { continue; }
551  const int gi = coarser.m_col_i0 + ci, gj = coarser.m_col_j0 + cj;
552  // A building is wholly inside this level or wholly outside it, so
553  // its top cell decides.
554  const IntVect top_cell(AMREX_D_DECL(gi + coarser.m_dom_lo[0], gj + coarser.m_dom_lo[1],
555  kc + coarser.m_dom_lo[2]));
556  if (grids_crse.contains(top_cell)) { continue; }
557  // The same height here: the top is kc + 1 cells of the level below up.
558  const Real h = (kc + 1) * dz_c;
559  // h / dz_f is a whole number of cells (the ratio times kc + 1);
560  // rounding keeps it so whatever the precision of the division.
561  const int kt = static_cast<int>(std::lround(h / dz_f)) - 1;
562  outside.push_back({gi * ratio[0], gj * ratio[1], kt});
563  bi0 = std::min(bi0, gi * ratio[0]); bi1 = std::max(bi1, (gi + 1) * ratio[0] - 1);
564  bj0 = std::min(bj0, gj * ratio[1]); bj1 = std::max(bj1, (gj + 1) * ratio[1] - 1);
565  }
566  }
567  if (outside.empty()) { return; }
568 
569  const int nxb = bi1 - bi0 + 1, nyb = bj1 - bj0 + 1;
570  std::vector<int> top(static_cast<size_t>(nxb) * nyb, -1);
571  for (int ci = 0; ci < m_col_nx; ++ci) {
572  for (int cj = 0; cj < m_col_ny; ++cj) {
573  top[static_cast<size_t>(m_col_i0 + ci - bi0) * nyb + (m_col_j0 + cj - bj0)] =
574  top_f[static_cast<size_t>(ci) * m_col_ny + cj];
575  }
576  }
577  for (const auto& o : outside) {
578  for (int a = 0; a < ratio[0]; ++a) {
579  for (int b = 0; b < ratio[1]; ++b) {
580  int& t = top[static_cast<size_t>(o.i0 + a - bi0) * nyb + (o.j0 + b - bj0)];
581  t = std::max(t, o.kt);
582  }
583  }
584  m_col_top_max = std::max(m_col_top_max, Real(m_z_ground + (o.kt + 1.0) * dz_f));
585  }
586  m_col_i0 = bi0; m_col_j0 = bj0; m_col_nx = nxb; m_col_ny = nyb;
587  upload(d_col_top, top);
588  if (m_params.debug) {
589  Print() << "[IBSEB DEBUG] lev=" << m_lev << " ray cast: " << outside.size()
590  << " columns of the level below, outside this level, added as occluders\n";
591  }
592 }
if(KE_decay_height > 0 &&state_pert.nComp() > RhoKE_comp)
Definition: ERF_InitCustomPert_ABL.H:76
amrex::Gpu::DeviceVector< int > d_col_top
Definition: ERF_IBFaceSet.H:484
amrex::Real m_col_top_max
Tallest column [m].
Definition: ERF_IBFaceSet.H:557
int m_col_i0
Definition: ERF_IBFaceSet.H:485
amrex::Real m_z_ground
Ground height [m] (flat, the domain bottom)
Definition: ERF_IBFaceSet.H:558
int m_col_ny
Definition: ERF_IBFaceSet.H:485
amrex::Real m_dx[3]
Cell sizes [m].
Definition: ERF_IBFaceSet.H:555
int m_col_j0
Definition: ERF_IBFaceSet.H:485
amrex::IntVect m_dom_lo
Low corner of the level's domain.
Definition: ERF_IBFaceSet.H:553
int m_col_nx
Definition: ERF_IBFaceSet.H:485
@ t
Definition: ERF_WSM6.H:272
real(c_double), private ci
Definition: ERF_module_mp_morr_two_moment.F90:203
bool debug
[IBSEB DEBUG] prints: set description at build, per-step summary, per-building rows
Definition: ERF_IBSEBParams.H:32
Here is the call graph for this function:

◆ assign_materials()

void IBFaceSet::assign_materials ( )

Assign every face its material properties: from the material library by building id, or the uniform inputs. Called once after build(); fills d_mat, d_albedo, d_emis, d_k, d_rhocp, d_thick and initialises the slab layers to the skin temperature.

Materials per face from the library by building id, or the uniform inputs. Host work on the static geometry, once.

850 {
851  if (m_params.material_file.empty()) { return; }
852  const Vector<IBSEBMaterial> table = read_ibseb_materials(m_params.material_file);
853  auto find = [&](int id) -> const IBSEBMaterial& {
854  for (const auto& m : table) { if (m.mat_id == id) return m; }
855  Abort("erf.ibseb: material id " + std::to_string(id) + " is not in " + m_params.material_file);
856  return table[0];
857  };
858  std::vector<int> h_b(m_nface);
859  Gpu::copy(Gpu::deviceToHost, d_bid.begin(), d_bid.end(), h_b.begin());
860  Gpu::streamSynchronize();
861  std::vector<int> h_mat(m_nface);
862  std::vector<Real> h_alb(m_nface), h_emi(m_nface), h_k(m_nface), h_rc(m_nface), h_th(m_nface);
863  for (int n = 0; n < m_nface; ++n) {
864  // erf.ibseb.material_by_building follows level 0's numbering; a
865  // building only a refined level resolves (0 here) takes the default.
866  const int b = m_to_lev0[h_b[n]];
867  int id = m_params.material_default;
868  if (b >= 1 && b <= static_cast<int>(m_params.material_by_building.size())) {
869  id = m_params.material_by_building[b - 1];
870  }
871  const IBSEBMaterial& m = find(id);
872  h_mat[n] = m.mat_id; h_alb[n] = m.albedo; h_emi[n] = m.emissivity;
873  h_k[n] = m.k_therm; h_rc[n] = m.rho_cp; h_th[n] = m.thickness;
874  }
875  upload(d_mat, h_mat); upload(d_albedo, h_alb); upload(d_emis, h_emi);
876  upload(d_kth, h_k); upload(d_rhocp, h_rc); upload(d_thick, h_th);
877  Gpu::streamSynchronize();
878  if (m_params.debug) {
879  Print() << "[IBSEB DEBUG] lev=" << m_lev << " materials: " << table.size() << " in "
880  << m_params.material_file << ", default id " << m_params.material_default;
881  for (size_t b = 0; b < m_params.material_by_building.size(); ++b) {
882  Print() << ", building " << b + 1 << " -> " << m_params.material_by_building[b];
883  }
884  Print() << "\n";
885  for (const auto& m : table) {
886  Print() << "[IBSEB DEBUG] material " << m.mat_id << " (" << m.name << "): albedo=" << m.albedo
887  << " emissivity=" << m.emissivity << " k=" << m.k_therm << " W/m/K rho_cp=" << m.rho_cp
888  << " J/m3/K thickness=" << m.thickness << " m\n";
889  }
890  }
891 }
Vector< IBSEBMaterial > read_ibseb_materials(const std::string &file)
Definition: ERF_IBSEBMaterials.cpp:25
DiagnosticID id
Definition: ERF_Plotfile2DWaterPath.cpp:27
amrex::Gpu::DeviceVector< amrex::Real > d_thick
Slab conductivity [W/m/K], heat capacity [J/m3/K], thickness [m].
Definition: ERF_IBFaceSet.H:443
amrex::Gpu::DeviceVector< amrex::Real > d_albedo
Definition: ERF_IBFaceSet.H:442
amrex::Gpu::DeviceVector< int > d_mat
Material id (0 = uniform inputs)
Definition: ERF_IBFaceSet.H:441
std::vector< int > m_to_lev0
Definition: ERF_IBFaceSet.H:547
amrex::Gpu::DeviceVector< amrex::Real > d_emis
Optical properties of the face.
Definition: ERF_IBFaceSet.H:442
int m_nface
Faces owned by this rank.
Definition: ERF_IBFaceSet.H:529
amrex::Gpu::DeviceVector< int > d_bid
Building id (1-based)
Definition: ERF_IBFaceSet.H:440
amrex::Gpu::DeviceVector< amrex::Real > d_rhocp
Definition: ERF_IBFaceSet.H:443
amrex::Gpu::DeviceVector< amrex::Real > d_kth
Definition: ERF_IBFaceSet.H:443
Definition: ERF_IBSEBMaterials.H:24
amrex::Real albedo
Definition: ERF_IBSEBMaterials.H:26
amrex::Real k_therm
[W/m/K]
Definition: ERF_IBSEBMaterials.H:28
amrex::Real thickness
[m]
Definition: ERF_IBSEBMaterials.H:30
int mat_id
Definition: ERF_IBSEBMaterials.H:25
amrex::Real rho_cp
[J/m3/K]
Definition: ERF_IBSEBMaterials.H:29
amrex::Real emissivity
Definition: ERF_IBSEBMaterials.H:27
int material_default
Definition: ERF_IBSEBParams.H:115
std::string material_file
Definition: ERF_IBSEBParams.H:114
std::vector< int > material_by_building
Definition: ERF_IBSEBParams.H:116
Here is the call graph for this function:

◆ build()

void IBFaceSet::build ( const amrex::MultiFab &  blanking,
const amrex::Geometry &  geom 
)

Detect this rank's faces from the cell-centred blanking and allocate every per-face array.

Runs on the host once at initialisation: the blanking is copied fab by fab (a pinned copy on GPU builds), the valid cells are scanned, and the arrays are uploaded to the device at the end. Must be called after the blanking has been built and its ghost cells filled, since the solid neighbour of a fluid cell on a box edge lives in a ghost cell. Stops when a solid cell outside the level's grids neighbours a cell of the level, fluid or solid: a building across the edge of a refined level.

Parameters
blankingCell-centred blanking of the level (needs at least one ghost cell, filled).
geomGeometry of the level: cell sizes for the face areas, domain and periodicity for the neighbour tests.

Detect the faces owned by this rank and allocate the per-face arrays.

The scan visits every valid cell of every local fab in MFIter order. A fluid cell (blanking < 0.5) contributes one face for each of its six neighbours that is solid (blanking >= 0.5); the neighbour may sit in a ghost cell, which is why the blanking must have its ghost cells filled. Neighbours outside the domain in a non-periodic direction are skipped, so a building against a non-periodic boundary has no face there. Solid cells contribute no face, only the column mask used for the building ids. A solid neighbour outside the level's grids, of a fluid or a solid cell, means a building crosses the edge of a refined level; the scan counts them and stops the run once all ranks have reported.

Faces are appended in scan order, which makes them contiguous per fab; m_fab_start records where each fab's faces begin. The per-direction, per-building and total counts are reduced over all ranks here, once, so report() does not have to reduce static numbers every time.

95 {
96  const Box& domain = geom.Domain();
97  const int nx = domain.length(0);
98  const int ny = domain.length(1);
99  const int ilo = domain.smallEnd(0);
100  const int jlo = domain.smallEnd(1);
101  const auto dx = geom.CellSizeArray();
102  const auto plo = geom.ProbLoArray();
103  const Real face_area[3] = { dx[1] * dx[2], dx[0] * dx[2], dx[0] * dx[1] };
104  m_nx = nx; m_ny = ny;
105  m_dom_lo = domain.smallEnd();
106  m_x_lo = plo[0]; m_y_lo = plo[1];
107  for (int d = 0; d < 3; ++d) { m_dx[d] = dx[d]; }
108  m_per_x = geom.isPeriodic(0); m_per_y = geom.isPeriodic(1);
109  m_max_path = 4.0 * (geom.ProbLength(0) + geom.ProbLength(1));
110  m_z_ground = plo[2];
111 
112  // The face list of a cell, and with it the slot numbering of the
113  // checkpoint record (see state_ncomp()), must not depend on the
114  // decomposition: every valid cell has to see its six neighbours, which
115  // needs a filled ghost layer on the blanking.
116  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(blanking.nGrowVect().min() >= 1,
117  "erf.ibseb: the blanking needs at least one ghost cell");
118 
119  std::vector<int> h_i, h_j, h_k, h_dir, h_side, h_nbi, h_nbj, h_slot;
120  std::vector<Real> h_area, h_xf, h_yf, h_zf;
121  m_domain_cells = domain.numPts();
122  // Solid neighbours outside the level's grids, of a fluid cell (a wall
123  // against the edge) or of a solid one (a building running across the
124  // edge, which a low roof can do with no fluid cell in between): a
125  // building that crosses the edge of a refined level, whose columns there
126  // this level cannot see.
127  const BoxArray& level_ba = blanking.boxArray();
128  Long n_outside = 0;
129  Long first_outside = std::numeric_limits<Long>::max(); // lowest such cell, as a linear domain index
130  auto solid_outside = [&] (const Array4<const Real>& b, const Box& vbox, const Box& fbox, const IntVect& nb) {
131  // A neighbour in the same valid box is in the level (the common case,
132  // and every case on level 0): no lookup.
133  if (vbox.contains(nb) || !fbox.contains(nb) || b(nb) < 0.5) { return false; }
134  IntVect nbw = nb;
135  for (int dp = 0; dp < 2; ++dp) {
136  if (!geom.isPeriodic(dp)) { continue; }
137  const int lo_d = domain.smallEnd(dp), len_d = domain.length(dp);
138  nbw[dp] = lo_d + ((nbw[dp] - lo_d) % len_d + len_d) % len_d;
139  }
140  if (level_ba.contains(nbw)) { return false; }
141  const IntVect r = nbw - domain.smallEnd();
142  first_outside = std::min(first_outside,
143  (Long(r[0]) * domain.length(1) + r[1]) * domain.length(2) + r[2]);
144  ++n_outside;
145  return true;
146  };
147 
148  // Bounding box of the solid columns (global), so the column arrays below
149  // cover the built area only: 8 bytes per built column per rank.
150  int bi0 = std::numeric_limits<int>::max(), bi1 = std::numeric_limits<int>::min();
151  int bj0 = bi0, bj1 = bi1;
152  for (MFIter mfi(blanking); mfi.isValid(); ++mfi) {
153  const Box& bx = mfi.validbox();
154  const HostFab hf(blanking[mfi]);
155  auto const& b = hf.array();
156  const auto lo = lbound(bx);
157  const auto hi = ubound(bx);
158  for (int k = lo.z; k <= hi.z; ++k) {
159  for (int j = lo.y; j <= hi.y; ++j) {
160  for (int i = lo.x; i <= hi.x; ++i) {
161  if (b(i, j, k) >= 0.5) {
162  bi0 = std::min(bi0, i); bi1 = std::max(bi1, i);
163  bj0 = std::min(bj0, j); bj1 = std::max(bj1, j);
164  }
165  }}}
166  }
167  ParallelDescriptor::ReduceIntMin(bi0); ParallelDescriptor::ReduceIntMin(bj0);
168  ParallelDescriptor::ReduceIntMax(bi1); ParallelDescriptor::ReduceIntMax(bj1);
169  const bool any_solid = (bi1 >= bi0) && (bj1 >= bj0);
170  m_col_i0 = any_solid ? bi0 - ilo : 0; m_col_j0 = any_solid ? bj0 - jlo : 0;
171  m_col_nx = any_solid ? bi1 - bi0 + 1 : 0; m_col_ny = any_solid ? bj1 - bj0 + 1 : 0;
172  const int bw = m_col_nx, bh = m_col_ny;
173  const size_t ncol = static_cast<size_t>(bw) * bh;
174  // Highest solid cell of each built column of this rank, as a domain-
175  // relative k index, -1 where the column carries no solid cell; reduced
176  // below so every rank sees the same columns. This one array is both the
177  // solid-column mask of the labelling (a column is solid exactly when its
178  // top is not -1) and the column top of the ray cast, and it is an index
179  // rather than a height so that it costs 4 bytes per column instead of the
180  // 24 a separate mask, index and height in Real took.
181  std::vector<int> coltop_k(ncol, -1);
182 
183  m_fab_start.clear();
184  for (MFIter mfi(blanking); mfi.isValid(); ++mfi) {
185  m_fab_start.push_back(static_cast<int>(h_i.size()));
186  const Box& bx = mfi.validbox();
187  const HostFab hf(blanking[mfi]);
188  auto const& b = hf.array();
189  const Box& fbox = Box(b);
190  const auto lo = lbound(bx);
191  const auto hi = ubound(bx);
192  for (int k = lo.z; k <= hi.z; ++k) {
193  for (int j = lo.y; j <= hi.y; ++j) {
194  for (int i = lo.x; i <= hi.x; ++i) {
195  if (b(i, j, k) >= 0.5) {
196  const size_t c = static_cast<size_t>(i - bi0) * bh + (j - bj0);
197  coltop_k[c] = std::max(coltop_k[c], k - domain.smallEnd(2));
198  for (int d = 0; d < 3; ++d) {
199  for (int s = -1; s <= 1; s += 2) {
200  IntVect nb(i, j, k);
201  nb[d] += s;
202  if (!geom.isPeriodic(d) && !domain.contains(nb)) { continue; }
203  solid_outside(b, bx, fbox, nb);
204  }
205  }
206  continue;
207  }
208  // Slot of each face within its cell's checkpoint record: the
209  // faces of a cell are numbered in the (dir, side) order of the
210  // loops below, which the rebuild from the same blanking
211  // reproduces on restart whatever the rank count.
212  int cell_slot = 0;
213  for (int d = 0; d < 3; ++d) {
214  for (int s = -1; s <= 1; s += 2) {
215  IntVect nb(i, j, k);
216  nb[d] += s;
217  if (!fbox.contains(nb)) { continue; }
218  if (!geom.isPeriodic(d) && !domain.contains(nb)) { continue; }
219  if (b(nb) < 0.5) { continue; }
220  solid_outside(b, bx, fbox, nb);
221  h_i.push_back(i); h_j.push_back(j); h_k.push_back(k);
222  h_dir.push_back(d); h_side.push_back(s);
223  h_slot.push_back(cell_slot++);
224  // Face centre: the fluid cell centre moved half a cell toward the solid.
225  Real c[3] = { Real(plo[0] + (i + 0.5) * dx[0]), Real(plo[1] + (j + 0.5) * dx[1]), Real(plo[2] + (k + 0.5) * dx[2]) };
226  c[d] += 0.5 * s * dx[d];
227  h_xf.push_back(c[0]); h_yf.push_back(c[1]); h_zf.push_back(c[2]);
228  // Column of the solid neighbour, wrapped for periodic directions.
229  int ci = nb[0] - ilo, cj = nb[1] - jlo;
230  ci = (ci % nx + nx) % nx;
231  cj = (cj % ny + ny) % ny;
232  h_nbi.push_back(ci); h_nbj.push_back(cj);
233  h_area.push_back(face_area[d]);
234  }
235  }
236  }}}
237  }
238  m_fab_start.push_back(static_cast<int>(h_i.size()));
239  m_nface = static_cast<int>(h_i.size());
240 
241  // The column map, the building ids and the ray cast below see only the
242  // level's own cells, so a building must lie wholly inside the level.
243  // ERF::init_ibseb() checks that for the buildings of the level below; this
244  // catches one only the refined level resolves.
245  {
246  Long n_out_all = n_outside;
247  Long first_all = first_outside;
248  ParallelDescriptor::ReduceLongSum(n_out_all);
249  ParallelDescriptor::ReduceLongMin(first_all);
250  if (n_out_all > 0) {
251  const Long nyz = Long(domain.length(1)) * domain.length(2);
252  const int fi = static_cast<int>(first_all / nyz);
253  const int fj = static_cast<int>((first_all / domain.length(2)) % domain.length(1));
254  const int fk = static_cast<int>(first_all % domain.length(2));
255  std::ostringstream os;
256  os << "erf.ibseb: level " << m_lev << " has " << n_out_all
257  << " cell faces against solid cells outside its grids (the first solid one at x = "
258  << plo[0] + (fi + 0.5) * dx[0] << " m, y = " << plo[1] + (fj + 0.5) * dx[1]
259  << " m, z = " << plo[2] + (fk + 0.5) * dx[2] << " m)";
260  os << ": a building crosses the edge of the refined level. To fix the refined box, run"
261  " `python3 Exec/CanonicalTests/SEB/ibseb_refinement_box.py <inputs>`: it checks the deck's"
262  " erf.<name>.in_box_lo / in_box_hi against the height map and prints a box that no building"
263  " crosses (or `... <inputs> --all --fit tight|relaxed` for a box around every building).";
264  Abort(os.str());
265  }
266  }
267 
268  // Width of the checkpoint record: the largest number of faces on one cell
269  // anywhere on the level (see state_ncomp()), reduced so every rank lays
270  // the field out the same way.
271  m_nslots = 0;
272  for (int n = 0; n < m_nface; ++n) { m_nslots = std::max(m_nslots, h_slot[n] + 1); }
273  ParallelDescriptor::ReduceIntMax(m_nslots);
274 
275  // The highest interface any face samples (two_stream_canopy_sample(): a roof its
276  // fluid cell's bottom, a ceiling and a wall the top of its cell too), counted from the domain
277  // bottom; -1 when the level has no faces. The two-stream beam the faces read needs
278  // no interface above it.
279  m_top_sample = -1;
280  for (int n = 0; n < m_nface; ++n) {
281  m_top_sample = std::max(m_top_sample, h_k[n] - domain.smallEnd(2) + ((h_dir[n] != 2 || h_side[n] > 0) ? 1 : 0));
282  }
283  ParallelDescriptor::ReduceIntMax(m_top_sample);
284 
285  // Checkpoint layout: 4 x 4 column blocks clipped to the k-range that owns
286  // faces (see state_boxarray()), the same on every rank from a reduced
287  // block k-range map; and the transfer layer, this rank's grids cut by
288  // those blocks, gathered into one global BoxArray owned by the rank whose
289  // grid each piece came from.
290  {
291  constexpr int BS = 4;
292  const int klo = domain.smallEnd(2);
293  const int nbx = (nx + BS - 1) / BS, nby = (ny + BS - 1) / BS;
294  // Lowest and highest face-owning cell of each block. Face-owning cells
295  // are the fluid cells against solid, a one-cell shell, so a block over
296  // the interior of a wide footprint owns faces only in the layer above
297  // the roof and its box collapses to that layer instead of carrying the
298  // whole column from the ground.
299  std::vector<int> blk_lo(static_cast<size_t>(nbx) * nby, std::numeric_limits<int>::max());
300  std::vector<int> blk_hi(static_cast<size_t>(nbx) * nby, -1);
301  for (int n = 0; n < m_nface; ++n) {
302  const size_t p = static_cast<size_t>((h_i[n] - ilo) / BS) * nby + (h_j[n] - jlo) / BS;
303  blk_lo[p] = std::min(blk_lo[p], h_k[n] - klo);
304  blk_hi[p] = std::max(blk_hi[p], h_k[n] - klo);
305  }
306  ParallelDescriptor::ReduceIntMin(blk_lo.data(), static_cast<int>(blk_lo.size()));
307  ParallelDescriptor::ReduceIntMax(blk_hi.data(), static_cast<int>(blk_hi.size()));
308  BoxList bl;
309  for (int bi = 0; bi < nbx; ++bi) {
310  for (int bj = 0; bj < nby; ++bj) {
311  const size_t p = static_cast<size_t>(bi) * nby + bj;
312  const int kmax = blk_hi[p];
313  if (kmax < 0) { continue; }
314  const int kmin = blk_lo[p];
315  const IntVect blo(ilo + bi * BS, jlo + bj * BS, klo + kmin);
316  const IntVect bhi(std::min(ilo + (bi + 1) * BS - 1, domain.bigEnd(0)),
317  std::min(jlo + (bj + 1) * BS - 1, domain.bigEnd(1)), klo + kmax);
318  bl.push_back(Box(blo, bhi));
319  }
320  }
321  m_state_ba = BoxArray(bl);
322  Vector<Box> mybx;
323  m_xfer_src.clear();
324  for (MFIter mfi(blanking); mfi.isValid(); ++mfi) {
325  for (const auto& pr : m_state_ba.intersections(mfi.validbox())) {
326  mybx.push_back(pr.second);
327  m_xfer_src.push_back(mfi.LocalIndex());
328  }
329  }
330  const int nranks = ParallelDescriptor::NProcs();
331  const int nmine = static_cast<int>(mybx.size());
332  Vector<int> counts(nranks, 0);
333  if (nranks == 1) {
334  // The non-MPI implementation of AllGather leaves its output
335  // untouched, so populate the single-rank count explicitly.
336  counts[0] = nmine;
337  } else {
338  ParallelAllGather::AllGather(nmine, counts.data(), ParallelDescriptor::Communicator());
339  }
340  Vector<Box> allbx(mybx);
341  AllGatherBoxes(allbx);
342  Vector<int> owner;
343  owner.reserve(allbx.size());
344  for (int r = 0; r < nranks; ++r) { for (int n = 0; n < counts[r]; ++n) { owner.push_back(r); } }
345  AMREX_ALWAYS_ASSERT(owner.size() == allbx.size());
346  BoxList xl;
347  for (const Box& b : allbx) { xl.push_back(b); }
348  m_xfer_ba = BoxArray(xl);
349  m_xfer_dm = DistributionMapping(owner);
350  }
351 
352  // Buildings: 4-connected solid columns, numbered in scan order. The column
353  // mask is reduced so every rank labels the same columns with the same ids;
354  // the labelling itself is a plain depth-first flood fill on the host.
355  if (ncol > 0) {
356  ParallelDescriptor::ReduceIntMax(coltop_k.data(), static_cast<int>(ncol));
357  }
358  // Column index in the built box of a face's solid neighbour (a 0-based,
359  // wrapped domain column; the neighbour is solid, so it lies in the box).
360  auto col_index = [&] (int n) {
361  const size_t c = static_cast<size_t>(h_nbi[n] - m_col_i0) * bh + (h_nbj[n] - m_col_j0);
362  AMREX_ALWAYS_ASSERT(h_nbi[n] >= m_col_i0 && h_nbi[n] < m_col_i0 + bw && h_nbj[n] >= m_col_j0 && h_nbj[n] < m_col_j0 + bh);
363  return c;
364  };
365  // Column tops for the ray cast: the index map itself, which
366  // ibseb::column_top() turns into a height one cell above the highest
367  // solid cell, the ground where the column is fluid.
368  upload(d_col_top, coltop_k);
369  m_col_top_max = plo[2];
370  for (size_t c = 0; c < ncol; ++c) {
371  if (coltop_k[c] >= 0) {
372  m_col_top_max = std::max(m_col_top_max, Real(plo[2] + (coltop_k[c] + 1.0) * dx[2]));
373  }
374  }
375  // Height of each face's building column, for the wall-function depth.
376  {
377  std::vector<Real> h_hb(h_i.size());
378  for (size_t n = 0; n < h_i.size(); ++n) {
379  const int kt = coltop_k[col_index(n)];
380  h_hb[n] = (kt >= 0) ? (kt + 1.0) * dx[2] : 0.0;
381  }
382  upload(d_hbld, h_hb);
383  }
384  std::vector<int> h_bid(m_nface);
385  {
386  std::vector<int> label(ncol, 0);
387  std::vector<int> stack;
388  m_nbld = 0;
389  for (int ci = 0; ci < bw; ++ci) {
390  for (int cj = 0; cj < bh; ++cj) {
391  const size_t p = static_cast<size_t>(ci) * bh + cj;
392  if (coltop_k[p] < 0 || label[p] > 0) { continue; }
393  ++m_nbld;
394  label[p] = m_nbld;
395  stack.clear();
396  stack.push_back(static_cast<int>(p));
397  while (!stack.empty()) {
398  const int q = stack.back(); stack.pop_back();
399  const int qi = q / bh, qj = q % bh;
400  const int ni[4] = {qi - 1, qi + 1, qi, qi};
401  const int nj[4] = {qj, qj, qj - 1, qj + 1};
402  for (int n = 0; n < 4; ++n) {
403  if (ni[n] < 0 || ni[n] >= bw || nj[n] < 0 || nj[n] >= bh) { continue; }
404  const size_t r = static_cast<size_t>(ni[n]) * bh + nj[n];
405  if (coltop_k[r] >= 0 && label[r] == 0) {
406  label[r] = m_nbld;
407  stack.push_back(static_cast<int>(r));
408  }
409  }
410  }
411  }
412  }
413  for (int n = 0; n < m_nface; ++n) {
414  h_bid[n] = label[col_index(n)];
415  }
416  // Footprints, for the debug summary: columns and bounding box per building.
417  m_bld_ncol.assign(m_nbld + 1, 0);
418  m_bld_ilo.assign(m_nbld + 1, nx); m_bld_ihi.assign(m_nbld + 1, -1);
419  m_bld_jlo.assign(m_nbld + 1, ny); m_bld_jhi.assign(m_nbld + 1, -1);
420  for (int ci = 0; ci < bw; ++ci) {
421  for (int cj = 0; cj < bh; ++cj) {
422  const int b = label[static_cast<size_t>(ci) * bh + cj];
423  if (b == 0) { continue; }
424  const int gi = ci + m_col_i0 + ilo, gj = cj + m_col_j0 + jlo;
425  m_bld_ncol[b] += 1;
426  m_bld_ilo[b] = std::min(m_bld_ilo[b], gi); m_bld_ihi[b] = std::max(m_bld_ihi[b], gi);
427  m_bld_jlo[b] = std::min(m_bld_jlo[b], gj); m_bld_jhi[b] = std::max(m_bld_jhi[b], gj);
428  }
429  }
430  // The column labels, by which a refined level finds its buildings in the
431  // level below (map_buildings_to_level0()), are kept until then.
433  m_lab.swap(label);
434  m_to_lev0.resize(m_nbld + 1);
435  for (int b = 0; b <= m_nbld; ++b) { m_to_lev0[b] = b; }
436  } // stack is freed here
437  // The column map has done its work too; free it before the per-face
438  // device uploads below.
439  std::vector<int>().swap(coltop_k);
440 
441  // Static totals: per direction, per building, and the summed area.
442  std::vector<Long> nd(3, 0);
443  std::vector<Long> bn(m_nbld + 1, 0);
444  std::vector<Real> ba(m_nbld + 1, 0.0);
445  Real area = 0.0;
446  for (int n = 0; n < m_nface; ++n) {
447  nd[h_dir[n]] += 1;
448  bn[h_bid[n]] += 1;
449  ba[h_bid[n]] += h_area[n];
450  area += h_area[n];
451  }
452  ParallelDescriptor::ReduceLongSum(nd.data(), 3);
453  ParallelDescriptor::ReduceLongSum(bn.data(), m_nbld + 1);
454  ParallelDescriptor::ReduceRealSum(ba.data(), m_nbld + 1);
455  ParallelDescriptor::ReduceRealSum(area);
456  for (int d = 0; d < 3; ++d) { m_nface_dir[d] = nd[d]; }
457  m_bld_nface = bn;
458  m_bld_area = ba;
459  m_area_total = area;
460 
461  // Device arrays. The state starts uniform at T_skin_init (the slab too;
462  // the interior boundary is applied when the slab is advanced); the view
463  // fractions and fluxes start at zero and are filled by
464  // compute_view_fractions() and the per-step routines.
465  const size_t nf = static_cast<size_t>(m_nface);
466  upload(d_i, h_i); upload(d_j, h_j); upload(d_k, h_k);
467  upload(d_dir, h_dir); upload(d_side, h_side); upload(d_bid, h_bid);
468  upload(d_slot, h_slot);
469  upload(d_area, h_area);
470  upload(d_xf, h_xf); upload(d_yf, h_yf); upload(d_zf, h_zf);
471  fill(d_mat, nf, 0);
474  fill(d_kth, nf, m_params.k_therm);
475  fill(d_rhocp, nf, m_params.rho_cp);
478  fill(d_T_slab, nf * static_cast<size_t>(n_layers()), m_params.T_skin_init);
479  // View fractions: placeholders (a roof sees the whole sky, a wall half sky
480  // and half ground) that compute_view_fractions() replaces at initialisation.
481  {
482  std::vector<Real> fs(nf), fg(nf), fb(nf, 0.0);
483  for (size_t n = 0; n < nf; ++n) {
484  const bool roof = (h_dir[n] == 2);
485  fs[n] = roof ? 1.0 : 0.5;
486  fg[n] = roof ? 0.0 : 0.5;
487  }
488  upload(d_f_sky, fs); upload(d_f_ground, fg); upload(d_f_bldg, fb);
489  }
490  fill(d_shadow, nf, Real(0.0));
491  fill(d_SW_direct_in, nf, Real(0.0));
492  fill(d_SW_diffuse_in, nf, Real(0.0));
493  fill(d_LW_down_in, nf, Real(0.0));
496  fill(d_rho, nf, Real(1.0));
497  fill(d_U_tan, nf, Real(0.0));
498  fill(d_ustar, nf, Real(0.0));
499  fill(d_SW_abs, nf, Real(0.0));
500  fill(d_LW_net, nf, Real(0.0));
501  fill(d_H, nf, Real(0.0));
502  fill(d_LE, nf, Real(0.0));
503  fill(d_G, nf, Real(0.0));
505  fill(d_H_coeff, nf, Real(0.0));
506  fill(d_w_star, nf, Real(0.0));
507  fill(d_olen, nf, Real(1.0e30));
508  fill(d_z_i, nf, Real(0.0));
509  fill(d_LW_ext, nf, Real(0.0));
510  fill(d_resid, nf, Real(0.0));
511  fill(d_niter, nf, 0);
512  Gpu::streamSynchronize();
513 
515 }
const int nx
Definition: ERF_InitCustomPertVels_CloudChamber.H:14
const int ny
Definition: ERF_InitCustomPertVels_CloudChamber.H:15
const int klo
Definition: ERF_InitCustomPert_ABL.H:75
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
amrex::Gpu::DeviceVector< amrex::Real > d_resid
|balance| after the solve [W/m2]
Definition: ERF_IBFaceSet.H:475
amrex::Gpu::DeviceVector< amrex::Real > d_theta_air
Potential temperature of the fluid cell [K].
Definition: ERF_IBFaceSet.H:463
std::vector< int > m_bld_jhi
Definition: ERF_IBFaceSet.H:542
amrex::Gpu::DeviceVector< amrex::Real > d_hbld
Height of the face's building column [m].
Definition: ERF_IBFaceSet.H:469
int m_nslots
Slots per cell of the checkpoint field (largest face count on a cell)
Definition: ERF_IBFaceSet.H:530
int m_ny
Columns of the domain.
Definition: ERF_IBFaceSet.H:552
amrex::Gpu::DeviceVector< amrex::Real > d_shadow
1 when the ray to the sun hits a building, else 0
Definition: ERF_IBFaceSet.H:456
amrex::Gpu::DeviceVector< amrex::Real > d_yf
Definition: ERF_IBFaceSet.H:445
std::vector< int > m_xfer_src
Definition: ERF_IBFaceSet.H:494
amrex::Real m_max_path
Ray path cap [m].
Definition: ERF_IBFaceSet.H:559
std::vector< amrex::Long > m_bld_ncol
Definition: ERF_IBFaceSet.H:541
amrex::Gpu::DeviceVector< amrex::Real > d_SW_abs
Definition: ERF_IBFaceSet.H:453
amrex::DistributionMapping m_xfer_dm
Definition: ERF_IBFaceSet.H:493
void print_debug_summary() const
Definition: ERF_IBFaceSet.cpp:1472
amrex::Gpu::DeviceVector< amrex::Real > d_LW_ext
Incoming longwave from sky and ground only [W/m2].
Definition: ERF_IBFaceSet.H:474
bool m_per_x
Definition: ERF_IBFaceSet.H:556
amrex::Long m_nface_dir[3]
Faces per direction.
Definition: ERF_IBFaceSet.H:538
std::vector< int > m_bld_jlo
Definition: ERF_IBFaceSet.H:542
amrex::Gpu::DeviceVector< amrex::Real > d_w_star
Convective velocity scale of the step [m/s].
Definition: ERF_IBFaceSet.H:470
amrex::Gpu::DeviceVector< amrex::Real > d_rho
Density of the fluid cell [kg/m3].
Definition: ERF_IBFaceSet.H:464
amrex::Gpu::DeviceVector< amrex::Real > d_T_skin
Skin temperature [K].
Definition: ERF_IBFaceSet.H:447
amrex::Gpu::DeviceVector< amrex::Real > d_LW_net
Definition: ERF_IBFaceSet.H:453
amrex::Gpu::DeviceVector< amrex::Real > d_z_i
Depth in w* [m]: mixed layer above a roof, building height for a wall.
Definition: ERF_IBFaceSet.H:472
amrex::BoxArray m_xfer_ba
Definition: ERF_IBFaceSet.H:492
amrex::Gpu::DeviceVector< int > d_side
Solid neighbour at -1 (low) or +1 (high) side.
Definition: ERF_IBFaceSet.H:438
int m_top_sample
Highest interface a face samples, over all ranks (-1: no faces)
Definition: ERF_IBFaceSet.H:531
std::vector< amrex::Real > m_bld_area
Face area per building [m2].
Definition: ERF_IBFaceSet.H:537
amrex::Gpu::DeviceVector< int > d_niter
Newton iterations of the last solve.
Definition: ERF_IBFaceSet.H:476
amrex::Gpu::DeviceVector< amrex::Real > d_SW_diffuse_in
Sky and ground-reflected diffuse incident.
Definition: ERF_IBFaceSet.H:458
amrex::Gpu::DeviceVector< amrex::Real > d_f_sky
Definition: ERF_IBFaceSet.H:450
amrex::Gpu::DeviceVector< amrex::Real > d_olen
Obukhov length used [m] (1e30 when neutral)
Definition: ERF_IBFaceSet.H:471
amrex::Gpu::DeviceVector< int > d_dir
Face direction: 0 x, 1 y, 2 z.
Definition: ERF_IBFaceSet.H:437
amrex::Gpu::DeviceVector< amrex::Real > d_f_bldg
View fractions, sum to 1.
Definition: ERF_IBFaceSet.H:450
std::vector< int > m_lab
Definition: ERF_IBFaceSet.H:548
std::vector< int > m_bld_ilo
Definition: ERF_IBFaceSet.H:542
amrex::Gpu::DeviceVector< int > d_slot
Slot of the face within its cell's checkpoint record.
Definition: ERF_IBFaceSet.H:439
amrex::Gpu::DeviceVector< amrex::Real > d_T_slab
Slab layers [K], face-major: [face * n_layers + layer].
Definition: ERF_IBFaceSet.H:448
int n_layers() const
Definition: ERF_IBFaceSet.H:170
amrex::Real m_y_lo
Domain origin [m].
Definition: ERF_IBFaceSet.H:554
amrex::Gpu::DeviceVector< amrex::Real > d_SW_direct_in
Direct beam incident on the face.
Definition: ERF_IBFaceSet.H:457
amrex::BoxArray m_state_ba
Definition: ERF_IBFaceSet.H:491
int m_lab_ny
Definition: ERF_IBFaceSet.H:549
amrex::Long m_domain_cells
Cells of the level, for the debug summary of the field.
Definition: ERF_IBFaceSet.H:495
amrex::Gpu::DeviceVector< amrex::Real > d_G
Definition: ERF_IBFaceSet.H:453
amrex::Gpu::DeviceVector< amrex::Real > d_T_air
Air temperature of the fluid cell.
Definition: ERF_IBFaceSet.H:461
amrex::Gpu::DeviceVector< amrex::Real > d_Q_ext
External incident flux [W/m2], e.g. fire radiation.
Definition: ERF_IBFaceSet.H:454
amrex::Real m_area_total
Total face area [m2].
Definition: ERF_IBFaceSet.H:539
int m_lab_nx
Definition: ERF_IBFaceSet.H:549
bool m_per_y
Periodicity in x and y.
Definition: ERF_IBFaceSet.H:556
amrex::Gpu::DeviceVector< amrex::Real > d_f_ground
Definition: ERF_IBFaceSet.H:450
int m_nx
Definition: ERF_IBFaceSet.H:552
amrex::Gpu::DeviceVector< amrex::Real > d_LE
Definition: ERF_IBFaceSet.H:453
int m_lab_j0
Definition: ERF_IBFaceSet.H:549
amrex::Gpu::DeviceVector< amrex::Real > d_H_coeff
H = H_coeff (theta_skin - theta_air) [W/m2/K].
Definition: ERF_IBFaceSet.H:467
int m_nbld
Buildings of the level (global)
Definition: ERF_IBFaceSet.H:532
amrex::Gpu::DeviceVector< amrex::Real > d_xf
Definition: ERF_IBFaceSet.H:445
amrex::Gpu::DeviceVector< amrex::Real > d_zf
Face centre [m].
Definition: ERF_IBFaceSet.H:445
std::vector< amrex::Long > m_bld_nface
Faces per building.
Definition: ERF_IBFaceSet.H:536
amrex::Gpu::DeviceVector< amrex::Real > d_LW_down_in
Incoming longwave on the face (sky, ground and walls)
Definition: ERF_IBFaceSet.H:460
std::vector< int > m_bld_ihi
Definition: ERF_IBFaceSet.H:542
amrex::Real m_x_lo
Definition: ERF_IBFaceSet.H:554
amrex::Gpu::DeviceVector< amrex::Real > d_ustar
Friction velocity of the wall function [m/s].
Definition: ERF_IBFaceSet.H:466
amrex::Gpu::DeviceVector< amrex::Real > d_U_tan
Tangential wind speed at the fluid cell [m/s].
Definition: ERF_IBFaceSet.H:465
int m_lab_i0
Definition: ERF_IBFaceSet.H:549
@ fi
Definition: ERF_AdvanceMorrison.cpp:107
@ fs
Definition: ERF_AdvanceMorrison.cpp:122
@ fg
Definition: ERF_AdvanceMorrison.cpp:109
@ ni
Definition: ERF_Morrison.H:48
@ qi
Definition: ERF_WDM6.H:28
@ q
Definition: ERF_WSM6.H:273
@ xl
Definition: ERF_WSM6.H:324
@ p
Definition: ERF_WSM6.H:280
real(c_double), private bi
Definition: ERF_module_mp_morr_two_moment.F90:182
void fill(MultiFab &dst, int temperature_comp, int mixing_ratio_comp, int source_comp, const Sources &sources, Real missing_value)
Definition: ERF_NearSurfaceDiagnostics.cpp:41
amrex::Real Q_ext_uniform
Uniform external incident flux on every face [W/m2] (test hook)
Definition: ERF_IBSEBParams.H:135
amrex::Real k_therm
Uniform conductivity [W/m/K].
Definition: ERF_IBSEBParams.H:117
amrex::Real rho_cp
Uniform volumetric heat capacity [J/m3/K].
Definition: ERF_IBSEBParams.H:118
amrex::Real thickness
Uniform slab thickness [m].
Definition: ERF_IBSEBParams.H:119
amrex::Real albedo
Face shortwave albedo.
Definition: ERF_IBSEBParams.H:69
amrex::Real T_skin_init
Initial skin temperature [K].
Definition: ERF_IBSEBParams.H:28
amrex::Real emissivity
Face longwave emissivity.
Definition: ERF_IBSEBParams.H:71
Here is the call graph for this function:

◆ building_level0()

int IBFaceSet::building_level0 ( int  b) const
inline

Level 0's number for building b of this level (1-based; 0 for one only this level resolves).

160 { return m_to_lev0.at(b); }

◆ check_canopy_layout()

void IBFaceSet::check_canopy_layout ( const amrex::MultiFab &  cons,
const TwoStreamCanopyView &  canopy 
) const

Stop unless the fields of canopy (this level's two-stream view) lie on the columns of cons, with the same distribution: the two-stream kernels read the column of a face from the fabs with the local index of the face's own fab.

The fields of a two-stream view must lie on this level's columns, with the level's distribution, so that the fabs of local index n hold the columns of the faces fab_start()[n] .. fab_start()[n+1]-1 own: the interface fluxes on the level's own grids (with the top-of-atmosphere ghost, which a face whose cell is the top of the domain reads); the beam on its boxes in x and y with the interfaces from the surface up to at least top_sample_interface() in z; the cosine of the zenith on them flattened to index 0 in z.

816 {
818  "erf.ibseb.radiation = two_stream: level " + std::to_string(m_lev) + "'s two-stream view is incomplete");
819  const BoxArray& ba = cons.boxArray();
820  const DistributionMapping& dm = cons.DistributionMap();
821  const auto& beam = *canopy.beam;
822  const auto& cosz = *canopy.cos_zenith;
823  const auto& flux = *canopy.fluxes;
824  bool ok = flux.boxArray() == ba && flux.DistributionMap() == dm && flux.nComp() >= 4 && flux.nGrowVect()[2] >= 1 &&
825  beam.boxArray().size() == ba.size() && beam.DistributionMap() == dm &&
826  cosz.boxArray().size() == ba.size() && cosz.DistributionMap() == dm;
827  // Same columns: the boxes agree in x and y.
828  for (int n = 0; ok && n < static_cast<int>(ba.size()); ++n) {
829  const Box& b = beam.boxArray()[n];
830  const Box& c = cosz.boxArray()[n];
831  for (int d = 0; d < 2; ++d) {
832  ok = ok && b.smallEnd(d) == ba[n].smallEnd(d) && b.bigEnd(d) == ba[n].bigEnd(d) &&
833  c.smallEnd(d) == ba[n].smallEnd(d) && c.bigEnd(d) == ba[n].bigEnd(d);
834  }
835  // The kernels read the beam at interfaces 0 .. top_sample_interface() and the
836  // cosine of the zenith at index 0 in z.
837  ok = ok && b.smallEnd(2) == 0 && b.bigEnd(2) >= top_sample_interface() && c.smallEnd(2) == 0;
838  }
840  "erf.ibseb.radiation = two_stream: level " + std::to_string(m_lev) +
841  "'s two-stream beam, cosine of the zenith or interface fluxes are not laid out on the level's grids");
842 }
int top_sample_interface() const
Definition: ERF_IBFaceSet.H:341
const amrex::MultiFab * fluxes
The level's rad_fluxes (SW up, SW down, LW up, LW down) [W/m^2].
Definition: ERF_TwoStreamCanopyForcing.H:48
const amrex::MultiFab * beam
Direct beam on a horizontal surface, interfaces 0 .. m_top [W/m^2].
Definition: ERF_TwoStreamCanopyForcing.H:46
const amrex::MultiFab * cos_zenith
Cosine of the zenith of the sweep, floored at zero (2D) [1].
Definition: ERF_TwoStreamCanopyForcing.H:47
bool valid() const noexcept
Definition: ERF_TwoStreamCanopyForcing.H:49
Here is the call graph for this function:

◆ compute_ground()

void IBFaceSet::compute_ground ( amrex::Real  dt)

Ground heat flux of the current step: advance every face's slab by dt with the skin temperature at the top and the interior temperature at the bottom (implicit, ERF_IBSEBSlab.H) and store the conduction into the slab, G, positive into the wall.

Slab conduction on every face: one kernel over the faces, each solving its own tridiagonal system with the skin at the top and the interior at the bottom.

900 {
901  const int nl = n_layers();
902  const Real Tint = m_params.T_interior;
903  const Real* pT = d_T_skin.data();
904  const Real* pkt = d_kth.data(); const Real* prc = d_rhocp.data(); const Real* pth = d_thick.data();
905  Real* pS = d_T_slab.data(); Real* pG = d_G.data();
906  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
907  // Zero-initialized: only the first nl entries are ever used, but the
908  // compiler cannot see that through the inlined solve and warns otherwise.
910  for (int l = 0; l < nl; ++l) { T[l] = pS[f * nl + l]; }
911  const Real dz = pth[f] / nl;
912  pG[f] = ibseb::advance_slab_dirichlet(T, pT[f], Tint, pkt[f], prc[f], dz, dt, nl);
913  for (int l = 0; l < nl; ++l) { pS[f * nl + l] = T[l]; }
914  });
915  Gpu::streamSynchronize();
916 }
@ T
Definition: ERF_IndexDefines.H:131
@ dz
Definition: ERF_AdvanceWDM6.cpp:272
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real advance_slab_dirichlet(amrex::Real *T, amrex::Real T_skin, amrex::Real T_int, amrex::Real k, amrex::Real rho_cp, amrex::Real dz, amrex::Real dt, int N)
Definition: ERF_IBSEBSlab.H:51
constexpr int SLAB_MAX_LAYERS
Definition: ERF_IBSEBSlab.H:28
amrex::Real T_interior
Interior (deep) temperature [K].
Definition: ERF_IBSEBParams.H:29
Here is the call graph for this function:

◆ compute_longwave()

void IBFaceSet::compute_longwave ( const amrex::MultiFab &  cons,
const TwoStreamCanopyView &  canopy = TwoStreamCanopyView{} 
)

Longwave of the current step. Incoming on a face: f_sky LW_sky + f_ground LW_ground + f_bldg sigma T_skin^4, the sky term fixed or gray (sky_emissivity sigma T_air^4 with the air temperature of the face's fluid cell) with the ground term eps_g sigma T_g^4, or with lw_mode = two_stream the longwave down and up at the face's height in the face's column (canopy); the building term is the isothermal-surroundings approximation. Net: eps (LW_in - sigma T_skin^4), positive into the face.

Parameters
consConserved state of the level, for the air temperature.
canopyWith lw_mode = two_stream, this step's two-stream view of the level (ERF_TwoStreamCanopyForcing.H), or an empty one before the first sweep, when the incoming and net longwave are zero.

Longwave of the current step. One kernel per fab over that fab's faces, reading the air temperature of the fluid cell from the conserved state (potential temperature and density through the equation of state). With lw_mode = two_stream the sky and ground terms come from the canopy forcing of the face's column, read from the 2D fab of the same local index (the level's grids flattened, on the same distribution).

716 {
717  if (m_params.lw_mode == "two_stream") {
719  return;
720  }
721  const Real sigma = ibseb::SIGMA_SB;
722  const bool gray = (m_params.lw_mode == "gray");
723  const Real lw_fix = m_params.lw_down, eps_sky = m_params.sky_emissivity;
724  const Real eps_g = m_params.emissivity_ground;
725  const Real* peps = d_emis.data();
726  const Real Tg4 = std::pow(m_params.T_ground, 4);
727  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
728  const Real* pfs = d_f_sky.data(); const Real* pfg = d_f_ground.data(); const Real* pfb = d_f_bldg.data();
729  const Real* pT = d_T_skin.data();
730  Real* pTa = d_T_air.data(); Real* pin = d_LW_down_in.data(); Real* pnet = d_LW_net.data();
731  Real* pext = d_LW_ext.data();
732  for (MFIter mfi(cons); mfi.isValid(); ++mfi) {
733  const int f0 = m_fab_start[mfi.LocalIndex()];
734  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
735  auto const& c = cons.const_array(mfi);
736  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
737  const int f = f0 + m;
738  const Real rho = c(pi[f], pj[f], pk[f], Rho_comp);
739  const Real Ta = getTgivenRandRTh(rho, c(pi[f], pj[f], pk[f], RhoTheta_comp));
740  const Real lw_sky = gray ? eps_sky * sigma * Ta * Ta * Ta * Ta : lw_fix;
741  const Real Ts4 = pT[f] * pT[f] * pT[f] * pT[f];
742  // Sky and ground do not depend on the face; the wall term does
743  // (isothermal surroundings), so the balance keeps them apart.
744  const Real lw_ext = pfs[f] * lw_sky + pfg[f] * eps_g * sigma * Tg4;
745  const Real lw_in = lw_ext + pfb[f] * sigma * Ts4;
746  pTa[f] = Ta;
747  pext[f] = lw_ext;
748  pin[f] = lw_in;
749  pnet[f] = peps[f] * (lw_in - sigma * Ts4);
750  });
751  }
752  Gpu::streamSynchronize();
753 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real getTgivenRandRTh(const amrex::Real rho, const amrex::Real rhotheta, const amrex::Real qv=amrex::Real(0))
Definition: ERF_EOS.H:46
#define Rho_comp
Definition: ERF_IndexDefines.H:42
amrex::Real sigma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:11
void compute_longwave_two_stream(const amrex::MultiFab &cons, const TwoStreamCanopyView &canopy)
Definition: ERF_IBFaceSet.cpp:766
@ rho
Definition: ERF_Kessler.H:25
constexpr amrex::Real SIGMA_SB
Stefan-Boltzmann constant [W/m2/K4].
Definition: ERF_IBSEBBalance.H:54
amrex::Real T_ground
Ground temperature for the ground term [K].
Definition: ERF_IBSEBParams.H:90
amrex::Real sky_emissivity
gray: effective clear-sky emissivity
Definition: ERF_IBSEBParams.H:89
std::string lw_mode
Definition: ERF_IBSEBParams.H:87
amrex::Real emissivity_ground
Ground emissivity for the ground term.
Definition: ERF_IBSEBParams.H:72
amrex::Real lw_down
fixed: sky longwave [W/m2]
Definition: ERF_IBSEBParams.H:88
Here is the call graph for this function:

◆ compute_longwave_two_stream()

void IBFaceSet::compute_longwave_two_stream ( const amrex::MultiFab &  cons,
const TwoStreamCanopyView &  canopy 
)

Longwave of the current step with lw_mode = two_stream: the sky term is the longwave down of the face's column at the face's height (a roof's interface, a wall's mean of its cell's two; two_stream_canopy_sample()), the ground term its longwave up at the same height (the ground seen through the air in between), so the ground's temperature, emissivity and reflection of the sky are the column's (two_stream_face_sky()). Before the level's first sweep (an empty canopy, the initial report) the air temperature is read and the incoming and net longwave are zero.

767 {
768  const Real sigma = ibseb::SIGMA_SB;
769  const bool have = canopy.valid();
770  const int k0 = m_dom_lo[2];
771  const Real* peps = d_emis.data();
772  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
773  const int* pd = d_dir.data(); const int* ps = d_side.data();
774  const Real* pfs = d_f_sky.data(); const Real* pfg = d_f_ground.data(); const Real* pfb = d_f_bldg.data();
775  const Real* pT = d_T_skin.data();
776  Real* pTa = d_T_air.data(); Real* pin = d_LW_down_in.data(); Real* pnet = d_LW_net.data();
777  Real* pext = d_LW_ext.data();
778  for (MFIter mfi(cons); mfi.isValid(); ++mfi) {
779  const int f0 = m_fab_start[mfi.LocalIndex()];
780  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
781  auto const& c = cons.const_array(mfi);
782  const auto beam = have ? canopy.beam->const_array(mfi) : Array4<const Real>{};
783  const auto cosz = have ? canopy.cos_zenith->const_array(mfi) : Array4<const Real>{};
784  const auto flux = have ? canopy.fluxes->const_array(mfi) : Array4<const Real>{};
785  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
786  const int f = f0 + m;
787  const Real rho = c(pi[f], pj[f], pk[f], Rho_comp);
788  pTa[f] = getTgivenRandRTh(rho, c(pi[f], pj[f], pk[f], RhoTheta_comp));
789  if (!have) {
790  pext[f] = 0.0; pin[f] = 0.0; pnet[f] = 0.0;
791  return;
792  }
793  const Real Ts4 = pT[f] * pT[f] * pT[f] * pT[f];
794  const TwoStreamFaceSky sky = two_stream_face_sky(pi[f], pj[f], pk[f], k0, pd[f], ps[f], beam, cosz, flux);
795  const Real lw_ext = pfs[f] * sky.lw_down + pfg[f] * sky.lw_up;
796  const Real lw_in = lw_ext + pfb[f] * sigma * Ts4;
797  pext[f] = lw_ext;
798  pin[f] = lw_in;
799  pnet[f] = peps[f] * (lw_in - sigma * Ts4);
800  });
801  }
802  Gpu::streamSynchronize();
803 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE TwoStreamFaceSky two_stream_face_sky(int i, int j, int k, int k0, int dir, int side, const amrex::Array4< const amrex::Real > &beam, const amrex::Array4< const amrex::Real > &cosz, const amrex::Array4< const amrex::Real > &fluxes)
The column radiation a face at fluid cell (i,j,k) of direction dir, with its solid on side side,...
Definition: ERF_TwoStreamCanopyForcing.H:133
What one face takes from its column (all W/m^2).
Definition: ERF_TwoStreamCanopyForcing.H:112
amrex::Real lw_up
Longwave up at the face's height (the ground's emission and the air's below)
Definition: ERF_TwoStreamCanopyForcing.H:117
amrex::Real lw_down
Longwave down at the face's height.
Definition: ERF_TwoStreamCanopyForcing.H:115
Here is the call graph for this function:

◆ compute_sensible()

void IBFaceSet::compute_sensible ( const amrex::MultiFab &  cons,
const amrex::MultiFab &  xvel,
const amrex::MultiFab &  yvel,
const amrex::MultiFab &  zvel,
amrex::Real  c_p,
const amrex::MultiFab *  olen_ground = nullptr,
const amrex::MultiFab *  pblh_ground = nullptr,
amrex::Real  z_i_bulk = 0.0 
)

Sensible heat of the current step through a wall function on every face: the tangential wind of the fluid cell at half a cell from the wall gives u* with the roughness z0_wall, the skin-to-air potential-temperature difference gives theta* with z0h_wall, and H = rho c_p u* theta*, positive out of the face. Neutral, or with the surface layer's stability functions on roofs when asked. The latent flux is not modelled and stays zero.

Beyond neutral: with convective_velocity = deardorff the wind is sqrt(U_tan^2 + (beta w*)^2) with w* from the previous step's H (zero when H is into the face) and a depth that is the mixed layer above a roof or the building height for a wall; with stability_correction the roofs iterate the similarity functions on the face's own Obukhov length, seeded from the ground surface layer's 2D field at the face's column when given.

Fills d_H, d_ustar, d_U_tan, d_rho, d_theta_air, d_H_coeff (the coefficient of the skin-to-air potential-temperature difference that the balance keeps frozen), d_w_star, d_olen and d_z_i; T_air was read by compute_longwave(), which must run first in a step.

Parameters
consConserved state of the level (density, rho theta).
xvel,yvel,zvelFace-centred velocities of the level.
c_pSpecific heat of air [J/kg/K].
olen_groundGround surface layer's Obukhov length (2D field on the level's box array collapsed to k = 0), or null.
pblh_groundIts boundary-layer height field, or null.
z_i_bulkMixed-layer depth of the level from the bulk Richardson diagnostic or the fixed input [m].

Wall function on every face. One kernel per fab over that fab's faces: the cell-centred velocity of the fluid cell from its face values, its tangential part with respect to the wall, the neutral log law for u* and theta*, and on request two additions: a convective velocity scale in the wind (Beljaars' gustiness form with Deardorff's w* from the previous step's flux) and, on roofs, the surface layer's similarity functions iterated on the face's own Obukhov length. The skin temperature is converted to a potential temperature with the fluid cell's Exner function before the difference is taken.

The Obukhov length of a face is L = u*^2 theta / (kappa g theta*) (negative over a hot face). Walls stay on the log law even with the stability correction: the similarity functions assume a horizontal surface, and the convective scale is what carries free convection on a wall. The ground's 2D Obukhov field only seeds the roof iteration.

1013 {
1014  const Real z0 = m_params.z0_wall, z0h = m_params.z0h_wall;
1015  const bool stab = m_params.stability_correction;
1016  const bool conv = (m_params.convective_velocity == "deardorff");
1017  const bool seed = stab && (olen_ground != nullptr) && (m_params.obukhov_seed == "ground");
1018  const bool use_pblh = conv && (pblh_ground != nullptr) && (m_params.z_i_mode == "pblh");
1019  const Real beta = m_params.beta_conv;
1020  const Real relax = m_params.obukhov_relax;
1021  const Real zi_fallback = (z_i_bulk > 0.0) ? z_i_bulk : m_params.z_i;
1022  const Real dxa[3] = {m_dx[0], m_dx[1], m_dx[2]};
1023  const Real z_lo = m_z_ground;
1024  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1025  const int* pd = d_dir.data();
1026  const Real* pT = d_T_skin.data(); const Real* phb = d_hbld.data(); const Real* pzf = d_zf.data();
1027  Real* pth = d_theta_air.data(); Real* prho = d_rho.data(); Real* pU = d_U_tan.data();
1028  Real* pus = d_ustar.data(); Real* pH = d_H.data(); Real* pTa = d_T_air.data();
1029  Real* pHc = d_H_coeff.data(); Real* pws = d_w_star.data(); Real* pol = d_olen.data(); Real* pzi = d_z_i.data();
1030  for (MFIter mfi(cons); mfi.isValid(); ++mfi) {
1031  const int f0 = m_fab_start[mfi.LocalIndex()];
1032  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
1033  auto const& c = cons.const_array(mfi);
1034  auto const& u = xvel.const_array(mfi);
1035  auto const& v = yvel.const_array(mfi);
1036  auto const& w = zvel.const_array(mfi);
1037  // The surface layer's 2D fields share the level's distribution with
1038  // the boxes collapsed to k = 0, so the same iterator indexes them.
1039  Array4<const Real> ol = seed ? olen_ground->const_array(mfi) : Array4<const Real>{};
1040  Array4<const Real> pb = use_pblh ? pblh_ground->const_array(mfi) : Array4<const Real>{};
1041  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1042  const int f = f0 + m;
1043  const int i = pi[f], j = pj[f], k = pk[f], d = pd[f];
1044  const Real rho = c(i, j, k, Rho_comp);
1045  const Real rth = c(i, j, k, RhoTheta_comp);
1046  const Real th = rth / rho;
1047  const Real Ta = getTgivenRandRTh(rho, rth);
1048  const Real th_skin = pT[f] * th / Ta; // skin temperature as a potential temperature
1049  // Cell-centred velocity and its tangential part with respect to the wall.
1050  Real U[3] = { Real(0.5) * (u(i, j, k) + u(i + 1, j, k)),
1051  Real(0.5) * (v(i, j, k) + v(i, j + 1, k)),
1052  Real(0.5) * (w(i, j, k) + w(i, j, k + 1)) };
1053  U[d] = 0.0;
1054  const Real Ut = amrex::max(std::sqrt(U[0] * U[0] + U[1] * U[1] + U[2] * U[2]), Real(1.0e-3));
1055  // Convective velocity scale from the previous step's flux out of the face.
1056  Real wstar = 0.0, depth = 0.0;
1057  if (conv && pH[f] > 0.0) {
1058  if (d == 2) {
1059  Real zi = use_pblh ? pb(i, j, 0) : zi_fallback;
1060  if (!(zi > 0.0 && zi < 1.0e5)) { zi = zi_fallback; }
1061  depth = amrex::max(zi - (pzf[f] - z_lo), phb[f]);
1062  } else {
1063  depth = phb[f];
1064  }
1065  wstar = std::cbrt(CONST_GRAV / th * pH[f] / (rho * c_p) * depth);
1066  }
1067  const Real Ut_eff = std::sqrt(Ut * Ut + beta * beta * wstar * wstar);
1068  const Real delta = 0.5 * dxa[d];
1069  const Real lnm = std::log(delta / z0), lnh = std::log(delta / z0h);
1070  Real ustar = KAPPA * Ut_eff / lnm;
1071  Real thstar = KAPPA * (th - th_skin) / lnh;
1072  Real lnh_eff = lnh;
1073  Real olen = 1.0e30;
1074  if (stab && d == 2) {
1075  // Roofs: the surface layer's similarity functions on the
1076  // face's own Obukhov length, a few fixed-point passes, seeded
1077  // from the ground's field at this column when available.
1078  similarity_funs sf;
1079  Real L = 1.0e30;
1080  if (seed) {
1081  const Real Lg = ol(i, j, 0);
1082  if (std::abs(Lg) > 0.0 && std::abs(Lg) < 1.0e5) { L = Lg; }
1083  }
1084  if (!(std::abs(L) < 1.0e5)) {
1085  L = ustar * ustar * th / (KAPPA * CONST_GRAV * thstar + 1.0e-20);
1086  }
1087  // Fixed point on L until u*, theta* and L agree to 1e-6, so
1088  // the stored values are mutually consistent (a fixed number
1089  // of passes leaves L one pass behind). u* is under-relaxed
1090  // between passes, as the surface layer's iteration is in
1091  // erf-model #3486, which keeps a strongly unstable roof from
1092  // oscillating at low wind; the converged values do not
1093  // depend on the factor.
1094  Real us_it = ustar;
1095  for (int it = 0; it < 50; ++it) {
1096  const Real zeta = delta / L;
1097  const Real psi_m = sf.calc_psi_m(zeta), psi_h = sf.calc_psi_h(zeta);
1098  const Real us_new = KAPPA * Ut_eff / amrex::max(lnm - psi_m, Real(0.1));
1099  us_it = (it == 0) ? us_new : (1.0 - relax) * us_it + relax * us_new;
1100  lnh_eff = amrex::max(lnh - psi_h, Real(0.1));
1101  thstar = KAPPA * (th - th_skin) / lnh_eff;
1102  const Real L_new = us_it * us_it * th / (KAPPA * CONST_GRAV * thstar + 1.0e-20);
1103  const bool done = std::abs(L_new - L) <= 1.0e-6 * std::abs(L_new) &&
1104  std::abs(us_new - us_it) <= 1.0e-6 * us_new;
1105  L = L_new;
1106  if (done) { us_it = us_new; break; }
1107  }
1108  ustar = us_it;
1109  olen = L;
1110  }
1111  pth[f] = th;
1112  pTa[f] = Ta;
1113  prho[f] = rho;
1114  pU[f] = Ut;
1115  pus[f] = ustar;
1116  pws[f] = wstar;
1117  pol[f] = olen;
1118  pzi[f] = depth;
1119  pHc[f] = rho * c_p * KAPPA * ustar / lnh_eff; // H = H_coeff (theta_skin - theta_air)
1120  pH[f] = -rho * c_p * ustar * thstar; // positive out of the face
1121  });
1122  }
1123  Gpu::streamSynchronize();
1124 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:67
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:68
Real z0
Definition: ERF_InitCustomPertVels_ScalarAdvDiff.H:8
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
@ olen
Definition: ERF_SLM.H:104
@ ustar
Definition: ERF_SLM.H:28
@ U
Definition: ERF_IndexDefines.H:129
@ beta
Definition: ERF_SLM.H:152
@ xvel
Definition: ERF_IndexDefines.H:218
@ zvel
Definition: ERF_IndexDefines.H:220
@ yvel
Definition: ERF_IndexDefines.H:219
@ zi
Definition: ERF_AdvanceWDM6.cpp:276
std::string convective_velocity
"none" or "deardorff"
Definition: ERF_IBSEBParams.H:151
amrex::Real obukhov_relax
Under-relaxation of the roof's u*, theta* iteration (1 = none), as erf-model #3486.
Definition: ERF_IBSEBParams.H:156
amrex::Real z0_wall
Momentum roughness of the faces [m].
Definition: ERF_IBSEBParams.H:100
std::string z_i_mode
"bulk_ri", "pblh" or "fixed"
Definition: ERF_IBSEBParams.H:153
amrex::Real beta_conv
Gustiness factor on w*.
Definition: ERF_IBSEBParams.H:152
amrex::Real z_i
Fixed mixed-layer depth [m] (z_i_mode = fixed, and the fallback)
Definition: ERF_IBSEBParams.H:154
amrex::Real z0h_wall
Heat roughness of the faces [m].
Definition: ERF_IBSEBParams.H:101
bool stability_correction
Definition: ERF_IBSEBParams.H:102
std::string obukhov_seed
"ground" or "neutral"
Definition: ERF_IBSEBParams.H:150
Definition: ERF_MOSTUtils.H:37
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_m(amrex::Real zeta) const
Definition: ERF_MOSTUtils.H:102
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real calc_psi_h(amrex::Real zeta) const
Definition: ERF_MOSTUtils.H:121

◆ compute_shortwave()

void IBFaceSet::compute_shortwave ( amrex::Real  time,
const TwoStreamCanopyView &  canopy = TwoStreamCanopyView{} 
)

Shortwave of the current step: the sun's position (sun_mode), the irradiances of the prescribed provider or, with radiation = two_stream, those of the face's own column at its height (canopy), then per face the direct beam on the face (zero when the ray toward the sun hits a building), the diffuse light through the view fractions, and the absorbed sum.

Fills d_shadow, d_SW_direct_in, d_SW_diffuse_in and d_SW_abs; stores the sun vector and irradiances for the reports (with two_stream the means over all the level's faces of the direct-normal and diffuse irradiance each takes, written to every row of the level).

Parameters
timeSimulation time [s]; with sun_mode = solar it is added to time_zero_utc_s (with sun_mode = two_stream the sun comes from set_two_stream_sun()).
canopyWith radiation = two_stream, this step's two-stream view of the level (see compute_longwave()), or an empty one before the first sweep, when every face gets no shortwave.

Shortwave of the current step. The sun and the irradiances are scalars computed on the host from the provider inputs; the per-face work is one kernel: incidence cosine, ray cast against the column tops, direct and diffuse incident, absorbed.

With sun_mode = fixed the direct-normal irradiance and the horizontal diffuse are the inputs as given. With sun_mode = solar the sun follows the site and time and the clear-sky formulas give both irradiances; with sun_mode = two_stream the position and the top-of-atmosphere irradiance are the two-stream radiation's (set_two_stream_sun()) and the same clear-sky formulas give the irradiances. The sun below the horizon gives zero everywhere.

Diffuse on a face: f_sky * diffuse_h + f_ground * albedo_ground * (direct_h + diffuse_h), the second term being the ground-reflected part, with the direct on a horizontal surface direct_h = dni * cos z. The view fractions are the placeholders of build() until compute_view_fractions() has run.

1177 {
1178  // ---- Sun and irradiances (host scalars) ----
1179  SunState s;
1180  if (m_params.sun_mode == "fixed") {
1181  s.zenith = m_params.sun_zenith_deg * PI / 180.0;
1182  s.azimuth = m_params.sun_azimuth_deg * PI / 180.0;
1183  const Real cz = std::cos(s.zenith);
1184  s.dni = (cz > 0.0) ? m_params.sw_direct_normal : 0.0;
1185  s.diffuse_h = (cz > 0.0) ? m_params.sw_diffuse : 0.0;
1186  } else if (m_params.sun_mode == "two_stream") {
1188  "erf.ibseb.sun_mode = two_stream: the two-stream sun was not given before the shortwave");
1189  // Used once: the next shortwave needs the sun of its own time.
1190  m_ts_sun.set = false;
1191  const Real decl = m_ts_sun.decl;
1192  const Real ha = m_ts_sun.hour_angle;
1193  s.zenith = ibseb::solar_zenith(m_ts_sun.lat_deg, decl, ha);
1194  s.azimuth = ibseb::solar_azimuth(m_ts_sun.lat_deg, decl, ha, s.zenith);
1195  const Real cz = std::cos(s.zenith);
1196  // The irradiance carries the date's distance factor already.
1200  } else {
1201  const Real t_utc = m_params.time_zero_utc_s + time;
1203  // Hour angle from UTC and the longitude (a time-zone offset would cancel).
1205  s.zenith = ibseb::solar_zenith(m_params.latitude_deg, decl, ha);
1206  s.azimuth = ibseb::solar_azimuth(m_params.latitude_deg, decl, ha, s.zenith);
1207  const Real cz = std::cos(s.zenith);
1212  }
1213  ibseb::sun_vector(s.zenith, s.azimuth, s.sx, s.sy, s.sz);
1214  m_sun = s;
1215 
1216  if (m_params.radiation == "two_stream") {
1218  return;
1219  }
1220 
1221  // ---- Per face ----
1222  const Real sx = s.sx, sy = s.sy, sz = s.sz;
1223  const Real dni = s.dni, dif_h = s.diffuse_h;
1224  const Real dir_h = dni * amrex::max(Real(0.0), sz);
1225  const Real alb_g = m_params.albedo_ground;
1226  const Real* palb = d_albedo.data();
1227  const int* col_top = d_col_top.data();
1228  const int nx = m_nx, ny = m_ny;
1229  const int ci0 = m_col_i0, cj0 = m_col_j0, cbw = m_col_nx, cbh = m_col_ny;
1230  const Real x_lo = m_x_lo, y_lo = m_y_lo, dx = m_dx[0], dy = m_dx[1], dz = m_dx[2];
1231  const bool per_x = m_per_x, per_y = m_per_y;
1232  const Real z_ground = m_z_ground, z_max = m_col_top_max, max_path = m_max_path;
1233  const int* pd = d_dir.data(); const int* ps = d_side.data();
1234  const Real* pxf = d_xf.data(); const Real* pyf = d_yf.data(); const Real* pzf = d_zf.data();
1235  const Real* pfs = d_f_sky.data(); const Real* pfg = d_f_ground.data();
1236  Real* psh = d_shadow.data(); Real* pdir = d_SW_direct_in.data();
1237  Real* pdif = d_SW_diffuse_in.data(); Real* pabs = d_SW_abs.data();
1238  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
1239  // Outward normal: opposite to the side the solid is on.
1240  Real n[3] = {0.0, 0.0, 0.0};
1241  n[pd[f]] = -static_cast<Real>(ps[f]);
1242  const Real cosi = n[0] * sx + n[1] * sy + n[2] * sz;
1243  Real shadow = 0.0, direct = 0.0;
1244  if (sz > 0.0 && cosi > 0.0 && dni > 0.0) {
1245  shadow = ibseb::ray_blocked(pxf[f], pyf[f], pzf[f], sx, sy, sz, col_top, nx, ny, ci0, cj0, cbw, cbh,
1246  x_lo, y_lo, dx, dy, dz, per_x, per_y, z_ground, z_max, max_path) ? 1.0 : 0.0;
1247  direct = dni * cosi * (1.0 - shadow);
1248  }
1249  const Real diffuse = pfs[f] * dif_h + pfg[f] * alb_g * (dir_h + dif_h);
1250  psh[f] = shadow;
1251  pdir[f] = direct;
1252  pdif[f] = diffuse;
1253  pabs[f] = (1.0 - palb[f]) * (direct + diffuse);
1254  });
1255  Gpu::streamSynchronize();
1256 
1257  if (m_params.debug) {
1258  Print() << "[IBSEB DEBUG] lev=" << m_lev << " sun: zenith=" << s.zenith * 180.0 / PI
1259  << " deg azimuth=" << s.azimuth * 180.0 / PI << " deg s=(" << s.sx << "," << s.sy << "," << s.sz
1260  << ") DNI=" << s.dni << " W/m2 diffuse_h=" << s.diffuse_h << " W/m2\n";
1261  }
1262 }
const Real dy
Definition: ERF_InitCustomPert_ABL.H:45
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:46
void compute_shortwave_two_stream(const TwoStreamCanopyView &canopy)
Definition: ERF_IBFaceSet.cpp:1286
SunState m_sun
Definition: ERF_IBFaceSet.H:560
TwoStreamSun m_ts_sun
Definition: ERF_IBFaceSet.H:563
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solar_zenith(amrex::Real latitude_deg, amrex::Real decl, amrex::Real hour_angle)
Definition: ERF_IBSEBSolar.H:91
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real clear_sky_dni(amrex::Real cos_zenith, amrex::Real S0, amrex::Real tau, amrex::Real distance_factor)
Definition: ERF_IBSEBSolar.H:154
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real earth_sun_distance_factor(amrex::Real day_of_year)
Definition: ERF_IBSEBSolar.H:130
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void sun_vector(amrex::Real zenith, amrex::Real azimuth, amrex::Real &sx, amrex::Real &sy, amrex::Real &sz)
Definition: ERF_IBSEBSolar.H:139
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE bool ray_blocked(amrex::Real x0, amrex::Real y0, amrex::Real z0, amrex::Real sx, amrex::Real sy, amrex::Real sz, const int *col_top, int nx, int ny, int i0, int j0, int bw, int bh, amrex::Real x_lo, amrex::Real y_lo, amrex::Real dx, amrex::Real dy, amrex::Real dz, bool per_x, bool per_y, amrex::Real z_ground, amrex::Real z_max, amrex::Real max_path)
Definition: ERF_IBSEBSolar.H:215
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solar_azimuth(amrex::Real latitude_deg, amrex::Real decl, amrex::Real hour_angle, amrex::Real zenith)
Definition: ERF_IBSEBSolar.H:106
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solar_declination(amrex::Real day_of_year)
Definition: ERF_IBSEBSolar.H:37
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solar_hour_angle(amrex::Real time_utc_s, amrex::Real longitude_deg, amrex::Real day_of_year)
Definition: ERF_IBSEBSolar.H:62
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real clear_sky_diffuse_h(amrex::Real cos_zenith, amrex::Real S0, amrex::Real tau, amrex::Real distance_factor, amrex::Real k_d)
Definition: ERF_IBSEBSolar.H:167
amrex::Real lat_deg
Definition: ERF_IBFaceSet.H:562
amrex::Real S0
Definition: ERF_IBFaceSet.H:562
amrex::Real decl
Definition: ERF_IBFaceSet.H:562
amrex::Real hour_angle
Definition: ERF_IBFaceSet.H:562
bool set
Definition: ERF_IBFaceSet.H:562
std::string sun_mode
Definition: ERF_IBSEBParams.H:56
amrex::Real day_of_year
solar: day of year
Definition: ERF_IBSEBParams.H:63
amrex::Real sw_transmission
solar, two_stream: bulk clear-sky transmission (Bird)
Definition: ERF_IBSEBParams.H:66
amrex::Real longitude_deg
solar: site longitude, east positive
Definition: ERF_IBSEBParams.H:62
amrex::Real albedo_ground
Ground albedo for the reflected diffuse term.
Definition: ERF_IBSEBParams.H:70
std::string radiation
Definition: ERF_IBSEBParams.H:46
amrex::Real solar_constant
solar: [W/m2]
Definition: ERF_IBSEBParams.H:65
amrex::Real sw_direct_normal
fixed: direct-normal irradiance [W/m2]
Definition: ERF_IBSEBParams.H:59
amrex::Real time_zero_utc_s
solar: seconds after 00:00 UTC at simulation time zero (the sun follows UTC and the longitude)
Definition: ERF_IBSEBParams.H:64
amrex::Real sun_zenith_deg
fixed: zenith angle [deg]
Definition: ERF_IBSEBParams.H:57
amrex::Real sw_diffuse
fixed: diffuse irradiance on a horizontal surface [W/m2]
Definition: ERF_IBSEBParams.H:60
amrex::Real sun_azimuth_deg
fixed: azimuth clockwise from north [deg]; 180 = sun in the south
Definition: ERF_IBSEBParams.H:58
amrex::Real latitude_deg
solar: site latitude, north positive
Definition: ERF_IBSEBParams.H:61
amrex::Real sw_diffuse_coeff
solar, two_stream: share of the attenuated beam that arrives as diffuse light
Definition: ERF_IBSEBParams.H:67
Here is the call graph for this function:

◆ compute_shortwave_two_stream()

void IBFaceSet::compute_shortwave_two_stream ( const TwoStreamCanopyView &  canopy)

Shortwave of the current step with radiation = two_stream, after compute_shortwave() has placed the sun (m_sun): per face, the direct-normal irradiance of its column at its height (two_stream_face_sky(): a roof's interface, a wall's mean of its cell's two), the beam over the column's own cosine of the zenith, on the face (shadowed by the same ray cast), the diffuse light of the sky there through f_sky, and the ground's reflection, the column's shortwave up at the face's height, through f_ground. One kernel per fab, reading the view's fields of the same local index.

The faces' sun (sun_mode) and the sweep's are required to be one sun (ERF::ibseb_check_sun_matches_two_stream()); the kernel checks it, column by column: a cosine of the zenith more than 1e-4 (1e-3 in single precision) from the faces' stops the run, since a direct beam divided by the wrong cosine and laid along the wrong direction would still look plausible.

Before the level's first sweep (an empty canopy, the initial report) every face gets no shortwave. The reported direct-normal and diffuse irradiances are the means over the level's faces of their columns'.

1287 {
1288  const Real sx = m_sun.sx, sy = m_sun.sy, sz = m_sun.sz;
1289  const Real* palb = d_albedo.data();
1290  const int* col_top = d_col_top.data();
1291  const int nx = m_nx, ny = m_ny;
1292  const int ci0 = m_col_i0, cj0 = m_col_j0, cbw = m_col_nx, cbh = m_col_ny;
1293  const Real x_lo = m_x_lo, y_lo = m_y_lo, dx = m_dx[0], dy = m_dx[1], dz = m_dx[2];
1294  const bool per_x = m_per_x, per_y = m_per_y;
1295  const Real z_ground = m_z_ground, z_max = m_col_top_max, max_path = m_max_path;
1296  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1297  const int k0 = m_dom_lo[2];
1298  const int* pd = d_dir.data(); const int* ps = d_side.data();
1299  const Real* pxf = d_xf.data(); const Real* pyf = d_yf.data(); const Real* pzf = d_zf.data();
1300  const Real* pfs = d_f_sky.data(); const Real* pfg = d_f_ground.data();
1301  Real* psh = d_shadow.data(); Real* pdir = d_SW_direct_in.data();
1302  Real* pdif = d_SW_diffuse_in.data(); Real* pabs = d_SW_abs.data();
1303 
1304  if (!canopy.valid()) {
1305  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
1306  psh[f] = 0.0; pdir[f] = 0.0; pdif[f] = 0.0; pabs[f] = 0.0;
1307  });
1308  Gpu::streamSynchronize();
1309  m_sun.dni = 0.0;
1310  m_sun.diffuse_h = 0.0;
1311  return;
1312  }
1313 
1314  // Sums of the columns' direct-normal and diffuse irradiance over the faces,
1315  // for the report, and the largest disagreement of the two suns.
1316  ReduceOps<ReduceOpSum, ReduceOpSum, ReduceOpMax> reduce_op;
1317  ReduceData<Real, Real, Real> reduce_data(reduce_op);
1318  using ReduceTuple = typename decltype(reduce_data)::Type;
1319  const Real sz_up = amrex::max(Real(0.0), sz);
1320  const int nfab = static_cast<int>(m_fab_start.size()) - 1;
1321  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(canopy.beam->local_size() == nfab,
1322  "erf.ibseb.radiation = two_stream: the two-stream beam holds another number of boxes than the faces' level");
1323  for (MFIter mfi(*canopy.beam); mfi.isValid(); ++mfi) {
1324  const int f0 = m_fab_start[mfi.LocalIndex()];
1325  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
1326  const auto beam = canopy.beam->const_array(mfi);
1327  const auto cosz = canopy.cos_zenith->const_array(mfi);
1328  const auto flux = canopy.fluxes->const_array(mfi);
1329  reduce_op.eval(f1 - f0, reduce_data, [=] AMREX_GPU_DEVICE (int m) noexcept -> ReduceTuple {
1330  const int f = f0 + m;
1331  const Real cz = cosz(pi[f], pj[f], 0);
1332  const TwoStreamFaceSky sky = two_stream_face_sky(pi[f], pj[f], pk[f], k0, pd[f], ps[f], beam, cosz, flux);
1333  const Real dni = sky.dni, dif_h = sky.diffuse, refl = sky.sw_up;
1334  // Outward normal: opposite to the side the solid is on.
1335  Real n[3] = {0.0, 0.0, 0.0};
1336  n[pd[f]] = -static_cast<Real>(ps[f]);
1337  const Real cosi = n[0] * sx + n[1] * sy + n[2] * sz;
1338  Real shadow = 0.0, direct = 0.0;
1339  if (sz > 0.0 && cosi > 0.0 && dni > 0.0) {
1340  shadow = ibseb::ray_blocked(pxf[f], pyf[f], pzf[f], sx, sy, sz, col_top, nx, ny, ci0, cj0, cbw, cbh,
1341  x_lo, y_lo, dx, dy, dz, per_x, per_y, z_ground, z_max, max_path) ? 1.0 : 0.0;
1342  direct = dni * cosi * (1.0 - shadow);
1343  }
1344  const Real diffuse = pfs[f] * dif_h + pfg[f] * refl;
1345  psh[f] = shadow;
1346  pdir[f] = direct;
1347  pdif[f] = diffuse;
1348  pabs[f] = (1.0 - palb[f]) * (direct + diffuse);
1349  return {dni, dif_h, std::abs(cz - sz_up)};
1350  });
1351  }
1352  Gpu::streamSynchronize();
1353  const auto rv = reduce_data.value(reduce_op);
1354  Real sums[2] = {amrex::get<0>(rv), amrex::get<1>(rv)};
1355  Real sun_gap = amrex::get<2>(rv);
1356  ParallelDescriptor::ReduceRealSum(sums, 2);
1357  ParallelDescriptor::ReduceRealMax(sun_gap);
1358  const Real nf_all = static_cast<Real>(m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2]);
1359  m_sun.dni = (nf_all > 0.0) ? sums[0] / nf_all : Real(0.0);
1360  m_sun.diffuse_h = (nf_all > 0.0) ? sums[1] / nf_all : Real(0.0);
1361  // The two suns are one formula evaluated twice; a float calendar day resolves
1362  // about a second of the hour angle, hence the looser single-precision bound.
1363  const Real sun_tol = (sizeof(Real) == 8) ? Real(1.e-4) : Real(1.e-3);
1364  if (sun_gap > sun_tol) {
1365  Abort("erf.ibseb.radiation = two_stream: on level " + std::to_string(m_lev) + " the faces' sun (zenith "
1366  + std::to_string(m_sun.zenith * 180.0 / PI) + " deg) and the two-stream sweep's differ by up to "
1367  + std::to_string(sun_gap) + " in the cosine of the zenith angle (more than " + std::to_string(sun_tol)
1368  + "); the faces must take the sweep's sun"
1369  " (erf.ibseb.sun_mode = two_stream, or fixed at erf.fixed_solar_zenith_angle)");
1370  }
1371  if (m_params.debug) {
1372  Print() << "[IBSEB DEBUG] lev=" << m_lev << " sun: zenith=" << m_sun.zenith * 180.0 / PI
1373  << " deg azimuth=" << m_sun.azimuth * 180.0 / PI << " deg s=(" << m_sun.sx << "," << m_sun.sy << ","
1374  << m_sun.sz << "); two-stream columns: mean DNI=" << m_sun.dni
1375  << " W/m2 mean diffuse_h=" << m_sun.diffuse_h << " W/m2 over the faces, largest sun gap "
1376  << sun_gap << " in cos z\n";
1377  }
1378 }
amrex::Real diffuse_h
Diffuse on a horizontal surface [W/m2].
Definition: ERF_IBFaceSet.H:502
amrex::Real dni
Direct-normal irradiance [W/m2].
Definition: ERF_IBFaceSet.H:501
amrex::Real zenith
Definition: ERF_IBFaceSet.H:499
amrex::Real sy
Definition: ERF_IBFaceSet.H:500
amrex::Real sx
Definition: ERF_IBFaceSet.H:500
amrex::Real azimuth
[rad]
Definition: ERF_IBFaceSet.H:499
amrex::Real sz
Unit vector toward the sun.
Definition: ERF_IBFaceSet.H:500
amrex::Real sw_up
Shortwave up at the face's height (the ground's reflection, seen from there)
Definition: ERF_TwoStreamCanopyForcing.H:116
amrex::Real diffuse
Diffuse shortwave down at the face's height.
Definition: ERF_TwoStreamCanopyForcing.H:114
amrex::Real dni
Direct-normal irradiance at the face's height.
Definition: ERF_TwoStreamCanopyForcing.H:113

Referenced by compute_shortwave().

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

◆ compute_view_fractions()

void IBFaceSet::compute_view_fractions ( )

View fractions: sample a cosine-weighted hemisphere around every face's outward normal with view_n_az x view_n_el rays through the column walk and count where they end: sky, ground or a building. The three fractions sum to one and are view factors. Called once at initialisation; the geometry is static.

Hemisphere sampling for the view fractions. One kernel over the faces, each looping over its rays; the count of rays ending on the sky, the ground and a building over the total gives the three fractions. Roofs point up and never see the ground; a wall on flat open ground sees half sky and half ground, which the regression test checks.

662 {
663  const int n_az = m_params.view_n_az, n_el = m_params.view_n_el;
664  const int* col_top = d_col_top.data();
665  const int nx = m_nx, ny = m_ny;
666  const int ci0 = m_col_i0, cj0 = m_col_j0, cbw = m_col_nx, cbh = m_col_ny;
667  const Real x_lo = m_x_lo, y_lo = m_y_lo, dx = m_dx[0], dy = m_dx[1], dz = m_dx[2];
668  const bool per_x = m_per_x, per_y = m_per_y;
669  const Real z_ground = m_z_ground, z_max = m_col_top_max, max_path = m_max_path;
670  const int* pd = d_dir.data(); const int* ps = d_side.data();
671  const Real* pxf = d_xf.data(); const Real* pyf = d_yf.data(); const Real* pzf = d_zf.data();
672  Real* pfs = d_f_sky.data(); Real* pfg = d_f_ground.data(); Real* pfb = d_f_bldg.data();
673  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
674  int n_sky = 0, n_gnd = 0, n_bld = 0;
675  for (int ie = 0; ie < n_el; ++ie) {
676  for (int ia = 0; ia < n_az; ++ia) {
677  Real sx, sy, sz;
678  ibseb::hemisphere_direction(pd[f], -ps[f], ia, ie, n_az, n_el, sx, sy, sz);
679  const int hit = ibseb::ray_hit(pxf[f], pyf[f], pzf[f], sx, sy, sz, col_top, nx, ny, ci0, cj0, cbw, cbh,
680  x_lo, y_lo, dx, dy, dz, per_x, per_y, z_ground, z_max, max_path);
681  if (hit == ibseb::RAY_SKY) ++n_sky; else if (hit == ibseb::RAY_GROUND) ++n_gnd; else ++n_bld;
682  }
683  }
684  const Real inv = 1.0 / static_cast<Real>(n_az * n_el);
685  pfs[f] = n_sky * inv; pfg[f] = n_gnd * inv; pfb[f] = n_bld * inv;
686  });
687  Gpu::streamSynchronize();
688 
689  if (m_params.debug) {
690  std::vector<Real> fs(m_nface), fg(m_nface), fb(m_nface);
691  Gpu::copy(Gpu::deviceToHost, d_f_sky.begin(), d_f_sky.end(), fs.begin());
692  Gpu::copy(Gpu::deviceToHost, d_f_ground.begin(), d_f_ground.end(), fg.begin());
693  Gpu::copy(Gpu::deviceToHost, d_f_bldg.begin(), d_f_bldg.end(), fb.begin());
694  Gpu::streamSynchronize();
695  Real s[3] = {0.0, 0.0, 0.0};
696  for (int n = 0; n < m_nface; ++n) { s[0] += fs[n]; s[1] += fg[n]; s[2] += fb[n]; }
697  ParallelDescriptor::ReduceRealSum(s, 3);
698  // A refined level can hold no building at all.
699  const Real nf_all = amrex::max(Real(1.0), static_cast<Real>(m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2]));
700  Print() << "[IBSEB DEBUG] lev=" << m_lev << " view fractions: " << n_az * n_el
701  << " rays per face, mean f_sky=" << s[0] / nf_all << " f_ground=" << s[1] / nf_all
702  << " f_bldg=" << s[2] / nf_all << "\n";
703  }
704 }
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void hemisphere_direction(int dir, int nsign, int ia, int ie, int n_az, int n_el, amrex::Real &sx, amrex::Real &sy, amrex::Real &sz)
Definition: ERF_IBSEBSolar.H:351
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE int ray_hit(amrex::Real x0, amrex::Real y0, amrex::Real z0, amrex::Real sx, amrex::Real sy, amrex::Real sz, const int *col_top, int nx, int ny, int i0, int j0, int bw, int bh, amrex::Real x_lo, amrex::Real y_lo, amrex::Real dx, amrex::Real dy, amrex::Real dz, bool per_x, bool per_y, amrex::Real z_ground, amrex::Real z_max, amrex::Real max_path)
Definition: ERF_IBSEBSolar.H:282
@ RAY_GROUND
Definition: ERF_IBSEBSolar.H:263
@ RAY_SKY
Definition: ERF_IBSEBSolar.H:263
int view_n_az
Definition: ERF_IBSEBParams.H:77
int view_n_el
Definition: ERF_IBSEBParams.H:78
Here is the call graph for this function:

◆ dump_faces()

void IBFaceSet::dump_faces ( const std::string &  prefix) const

Write every face of this rank to <prefix>.rank<N>.csv with its geometry, view fractions, shadow flag, shortwave terms and skin temperature, for the regression tests and for users who want the raw list.

Per-rank face dump. Every rank writes its own file so no gather is needed; readers concatenate <prefix>.rank*.csv.

1415 {
1416  const int nf = m_nface;
1417  auto host = [&](const Gpu::DeviceVector<Real>& d) {
1418  std::vector<Real> h(nf);
1419  Gpu::copy(Gpu::deviceToHost, d.begin(), d.end(), h.begin());
1420  return h;
1421  };
1422  auto hosti = [&](const Gpu::DeviceVector<int>& d) {
1423  std::vector<int> h(nf);
1424  Gpu::copy(Gpu::deviceToHost, d.begin(), d.end(), h.begin());
1425  return h;
1426  };
1427  const auto i = hosti(d_i), j = hosti(d_j), k = hosti(d_k), dir = hosti(d_dir), side = hosti(d_side), bid = hosti(d_bid);
1428  const auto xf = host(d_xf), yf = host(d_yf), zf = host(d_zf), area = host(d_area);
1429  const auto fs = host(d_f_sky), fg = host(d_f_ground), fb = host(d_f_bldg);
1430  const auto sh = host(d_shadow), sdir = host(d_SW_direct_in), sdif = host(d_SW_diffuse_in), sabs = host(d_SW_abs);
1431  const auto T = host(d_T_skin), Ta = host(d_T_air), lwin = host(d_LW_down_in), lwn = host(d_LW_net);
1432  const auto th = host(d_theta_air), rho = host(d_rho), Ut = host(d_U_tan), us = host(d_ustar), H = host(d_H);
1433  const auto mat = hosti(d_mat);
1434  const auto alb = host(d_albedo), emi = host(d_emis), kk = host(d_kth), rc = host(d_rhocp), thk = host(d_thick), G = host(d_G);
1435  const auto Hc = host(d_H_coeff), Qx = host(d_Q_ext), LE = host(d_LE), lwe = host(d_LW_ext), res = host(d_resid);
1436  const auto nit = hosti(d_niter);
1437  const auto ws = host(d_w_star), ol = host(d_olen), zi = host(d_z_i), hb = host(d_hbld);
1438  const int nl = n_layers();
1439  std::vector<Real> slab(static_cast<size_t>(nf) * nl);
1440  Gpu::copy(Gpu::deviceToHost, d_T_slab.begin(), d_T_slab.end(), slab.begin());
1441  Gpu::streamSynchronize();
1442  std::ofstream f(prefix + ".rank" + std::to_string(ParallelDescriptor::MyProc()) + ".csv");
1443  f << "i,j,k,dir,side,bid,x_m,y_m,z_m,area_m2,f_sky,f_ground,f_bldg,shadow,SW_direct_in,SW_diffuse_in,SW_abs,T_skin,T_air,LW_in,LW_net,"
1444  "theta_air,rho,U_tan,ustar,H,mat,albedo,emissivity,k_therm,rho_cp,thickness,G,T_slab_top,T_slab_bottom,"
1445  "H_coeff,Q_ext,LE,LW_ext,resid,n_iter,w_star,Olen,z_i,h_bld";
1446  for (int l = 0; l < nl; ++l) { f << ",T_slab_" << l; }
1447  f << "\n";
1448  f << std::setprecision(12);
1449  for (int n = 0; n < nf; ++n) {
1450  f << i[n] << "," << j[n] << "," << k[n] << "," << dir[n] << "," << side[n] << "," << bid[n] << ","
1451  << xf[n] << "," << yf[n] << "," << zf[n] << "," << area[n] << ","
1452  << fs[n] << "," << fg[n] << "," << fb[n] << "," << sh[n] << ","
1453  << sdir[n] << "," << sdif[n] << "," << sabs[n] << "," << T[n] << ","
1454  << Ta[n] << "," << lwin[n] << "," << lwn[n] << ","
1455  << th[n] << "," << rho[n] << "," << Ut[n] << "," << us[n] << "," << H[n] << ","
1456  << mat[n] << "," << alb[n] << "," << emi[n] << "," << kk[n] << "," << rc[n] << "," << thk[n] << ","
1457  << G[n] << "," << slab[static_cast<size_t>(n) * nl] << "," << slab[static_cast<size_t>(n) * nl + nl - 1] << ","
1458  << Hc[n] << "," << Qx[n] << "," << LE[n] << "," << lwe[n] << "," << res[n] << "," << nit[n]
1459  << "," << ws[n] << "," << ol[n] << "," << zi[n] << "," << hb[n];
1460  for (int l = 0; l < nl; ++l) { f << "," << slab[static_cast<size_t>(n) * nl + l]; }
1461  f << "\n";
1462  }
1463 }
Real H
Definition: ERF_InitCustomPert_MovingTerrain.H:7
constexpr std::array< amrex::Real, num_veg_params > rc
Definition: ERF_SLM_DefaultParams.H:22

Referenced by report().

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

◆ fab_start()

const std::vector<int>& IBFaceSet::fab_start ( ) const
inline

First face of each local fab in MFIter order; size number of fabs + 1. Faces fab_start()[n] to fab_start()[n+1]-1 belong to the fab with local index n, which is what the scatter kernels rely on.

524 { return m_fab_start; }

◆ has_faces()

bool IBFaceSet::has_faces ( ) const
inline

Whether the level has faces on any rank.

344 { return m_top_sample >= 0; }

◆ has_state()

bool IBFaceSet::has_state ( ) const
inline

Whether the level has a checkpoint field to write (any faces at all).

206 { return !m_state_ba.empty(); }

◆ load_state()

void IBFaceSet::load_state ( const amrex::MultiFab &  state)

Read the face state (skin, flux, slab) back from a field written by save_state(), after build() has recreated the list from the blanking: the reverse ParallelCopy onto the transfer field, then each face reads its slot. Only the slots that correspond to a face of this list are read.

Read the skin temperature, the sensible flux and the slab temperatures back from the state field, the inverse of save_state(), into a list that build() has just recreated with the same slot numbering.

1587 {
1588  AMREX_ALWAYS_ASSERT(state.nComp() == state_ncomp() && state.boxArray() == m_state_ba);
1589  const int nl = n_layers();
1590  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1591  const int* pslot = d_slot.data();
1592  Real* pT = d_T_skin.data(); Real* pS = d_T_slab.data(); Real* pH = d_H.data();
1593  MultiFab mine(m_xfer_ba, m_xfer_dm, state_ncomp(), 0);
1594  mine.ParallelCopy(state);
1595  for (MFIter mfi(mine); mfi.isValid(); ++mfi) {
1596  const int li = m_xfer_src[mfi.LocalIndex()];
1597  const int f0 = m_fab_start[li];
1598  const int f1 = m_fab_start[li + 1];
1599  const Box bx = mfi.validbox();
1600  auto const& st = mine.const_array(mfi);
1601  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1602  const int f = f0 + m;
1603  if (!bx.contains(IntVect(pi[f], pj[f], pk[f]))) { return; }
1604  const int c0 = pslot[f] * (2 + nl);
1605  pT[f] = st(pi[f], pj[f], pk[f], c0);
1606  pH[f] = st(pi[f], pj[f], pk[f], c0 + 1);
1607  for (int l = 0; l < nl; ++l) { pS[f * nl + l] = st(pi[f], pj[f], pk[f], c0 + 2 + l); }
1608  });
1609  }
1610  Gpu::streamSynchronize();
1611  if (m_params.debug) {
1612  std::vector<Real> h_T(m_nface);
1613  Gpu::copy(Gpu::deviceToHost, d_T_skin.begin(), d_T_skin.end(), h_T.begin());
1614  Gpu::streamSynchronize();
1615  Real tmin = 1.0e30, tmax = -1.0e30;
1616  for (int n = 0; n < m_nface; ++n) { tmin = std::min(tmin, h_T[n]); tmax = std::max(tmax, h_T[n]); }
1617  ParallelDescriptor::ReduceRealMin(tmin);
1618  ParallelDescriptor::ReduceRealMax(tmax);
1619  Print() << "[IBSEB DEBUG] lev=" << m_lev << " face state loaded: T_skin in ["
1620  << tmin << ", " << tmax << "] K\n";
1621  }
1622 }
int state_ncomp() const
Definition: ERF_IBFaceSet.H:189
Here is the call graph for this function:

◆ make_state()

MultiFab IBFaceSet::make_state ( ) const

A field on state_boxarray() with state_ncomp() components, for the checkpoint.

Write the skin temperature, the sensible flux and the slab temperatures into the state field. The record of a face starts at component slot * (2 + n_layers), the slot being the face's number within its own cell (see state_ncomp()); no two faces of a cell share a slot, so the writes are plain stores.

1540 {
1541  if (m_state_ba.empty()) { return MultiFab(); }
1542  return MultiFab(m_state_ba, DistributionMapping(m_state_ba), state_ncomp(), 0);
1543 }
Here is the call graph for this function:

◆ map_buildings_to_level0()

void IBFaceSet::map_buildings_to_level0 ( const IBFaceSet &  coarser,
const amrex::IntVect &  ratio 
)

Number this refined level's buildings as level 0 numbers them.

Each level labels its buildings from its own cells, so the ids of a refined level differ from level 0's wherever the levels see different buildings: a building outside the refined level, or two that level 0 merges into one. Inputs that go by building (erf.ibseb.material_by_building) and the report's building_level0 column use level 0's numbering, so every building of this level is looked up in the labels of the level below, and through that level's own map in level 0's: each of its columns votes with the id under it and the majority wins. A building only this level resolves maps to 0 (the default material), with a warning. Call after build() of both levels; the level below keeps its labels until release_labels().

Parameters
coarserFace set of the level below, already mapped.
ratioRefinement ratio from the level below to this one.

Map this level's buildings to level 0's numbering through the labels of the level below (see the header). Every column of a building votes with the level-0 id of the coarse column under it (through the level below's own map), unlabelled coarse columns abstaining, and the most frequent id wins (the lowest on a tie). No single column is trusted: the coarse blanking is a volume average, so a column the coarse level leaves unlabelled (a spire alone in its coarse cell) cannot unmap a building the level below resolves through its other columns. A building none of whose columns lies under a labelled coarse column is one only this level resolves; it maps to 0, and a warning names it when the level below has buildings at all.

608 {
609  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(coarser.m_lab.size() == static_cast<size_t>(coarser.m_lab_nx) * coarser.m_lab_ny,
610  "erf.ibseb: the level below released its labels before this level was mapped");
611  std::vector<std::map<int, Long>> votes(m_nbld + 1);
612  for (int ci = 0; ci < m_lab_nx; ++ci) {
613  for (int cj = 0; cj < m_lab_ny; ++cj) {
614  const int b = m_lab[static_cast<size_t>(ci) * m_lab_ny + cj];
615  if (b == 0) { continue; }
616  // amrex::coarsen, not a division: the column indices here are cell
617  // indices of the level, and integer division truncates towards zero,
618  // which would send a negative index to the coarse column above the
619  // one that covers it.
620  const int cc = amrex::coarsen(ci + m_lab_i0, ratio[0]) - coarser.m_lab_i0;
621  const int cd = amrex::coarsen(cj + m_lab_j0, ratio[1]) - coarser.m_lab_j0;
622  if (cc < 0 || cc >= coarser.m_lab_nx || cd < 0 || cd >= coarser.m_lab_ny) { continue; }
623  const int bc = coarser.m_lab[static_cast<size_t>(cc) * coarser.m_lab_ny + cd];
624  if (bc > 0 && coarser.m_to_lev0[bc] > 0) { ++votes[b][coarser.m_to_lev0[bc]]; }
625  }
626  }
627  std::vector<int> unmapped;
628  for (int b = 1; b <= m_nbld; ++b) {
629  int best = 0;
630  Long n_best = 0;
631  for (const auto& [id, n] : votes[b]) { // ascending id: the lowest wins a tie
632  if (n > n_best) { best = id; n_best = n; }
633  }
634  m_to_lev0[b] = best;
635  if (best == 0) { unmapped.push_back(b); }
636  }
637  if (!unmapped.empty() && coarser.m_nbld > 0) {
638  std::ostringstream os;
639  os << "erf.ibseb: level " << m_lev << " has " << unmapped.size()
640  << " building(s) that no labelled column of the level below lies under (level " << m_lev << " number";
641  for (int b : unmapped) { os << " " << b; }
642  os << "); they take erf.ibseb.material_default and building_level0 = 0 in the report";
643  // Every rank holds the same labels and reaches the same verdict.
644  if (ParallelDescriptor::IOProcessor()) { Warning(os.str().c_str()); }
645  }
646  if (m_params.debug) {
647  Print() << "[IBSEB DEBUG] lev=" << m_lev << " buildings as level 0 numbers them:";
648  for (int b = 1; b <= m_nbld; ++b) { Print() << " " << b << "->" << m_to_lev0[b]; }
649  Print() << "\n";
650  }
651 }
real(c_double), private bc
Definition: ERF_module_mp_morr_two_moment.F90:182
Here is the call graph for this function:

◆ n_buildings()

int IBFaceSet::n_buildings ( ) const
inline

Number of buildings of the level, the same on every rank.

168 { return m_nbld; }

◆ n_faces()

int IBFaceSet::n_faces ( ) const
inline

Number of faces owned by this rank (not the global count; see report()).

166 { return m_nface; }

◆ n_layers()

int IBFaceSet::n_layers ( ) const
inline

Conduction layers per face, from erf.ibseb.n_slab_layers.

170 { return m_params.n_slab_layers; }
int n_slab_layers
Conduction layers per face.
Definition: ERF_IBSEBParams.H:27

Referenced by dump_faces(), load_state(), print_debug_summary(), save_state(), and state_ncomp().

Here is the caller graph for this function:

◆ n_slots()

int IBFaceSet::n_slots ( ) const
inline

Slots per cell of the checkpoint field: the largest face count on a cell of the level.

192 { return m_nslots; }

◆ print_debug_summary()

void IBFaceSet::print_debug_summary ( ) const

Print the [IBSEB DEBUG] description of the set: the inputs, every rank's face count and per-fab ranges, the buildings with their footprint columns and bounding boxes, and the device memory of the arrays. Called once from build() when erf.ibseb.debug is set.

Debug description of the set, in the style of the fire module's [FIRE DEBUG] lines: what was read, what every rank holds, what the buildings look like, and what the arrays cost. Per-rank lines use AllPrint() so every rank reports; the rest is printed by the I/O rank.

1473 {
1474  const int nl = n_layers();
1475  Print() << "[IBSEB DEBUG] lev=" << m_lev << " inputs: n_slab_layers=" << nl
1476  << " T_skin_init=" << m_params.T_skin_init << " K"
1477  << " T_interior=" << m_params.T_interior << " K"
1478  << " csv_file=" << m_params.csv_file << " csv_int=" << m_params.csv_int << "\n";
1479  Print() << "[IBSEB DEBUG] lev=" << m_lev << " global faces=" << (m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2])
1480  << " (x=" << m_nface_dir[0] << " y=" << m_nface_dir[1] << " z=" << m_nface_dir[2] << ")"
1481  << " area=" << m_area_total << " m2 buildings=" << m_nbld << "\n";
1482  for (int b = 1; b <= m_nbld; ++b) {
1483  Print() << "[IBSEB DEBUG] building " << b << ": columns=" << m_bld_ncol[b]
1484  << " i=[" << m_bld_ilo[b] << "," << m_bld_ihi[b] << "]"
1485  << " j=[" << m_bld_jlo[b] << "," << m_bld_jhi[b] << "]"
1486  << " faces=" << m_bld_nface[b] << " area=" << m_bld_area[b] << " m2\n";
1487  }
1488  // Per-rank ownership: one line per rank with its fab ranges.
1489  const int nfab = static_cast<int>(m_fab_start.size()) - 1;
1490  const size_t bytes = static_cast<size_t>(m_nface) *
1491  (7 * sizeof(int) + (11 + static_cast<size_t>(nl)) * sizeof(Real));
1492  std::ostringstream os;
1493  os << "[IBSEB DEBUG] lev=" << m_lev << " rank " << ParallelDescriptor::MyProc()
1494  << ": faces=" << m_nface << " over " << nfab << " fab(s), device memory "
1495  << bytes / 1024.0 << " kB; fab ranges:";
1496  for (int f = 0; f < nfab; ++f) { os << " [" << m_fab_start[f] << "," << m_fab_start[f + 1] << ")"; }
1497  AllPrint() << os.str() << "\n";
1498 }
std::string csv_file
Per-building report.
Definition: ERF_IBSEBParams.H:30
int csv_int
Steps between report rows; <= 0 disables.
Definition: ERF_IBSEBParams.H:31
Here is the call graph for this function:

◆ release_labels()

void IBFaceSet::release_labels ( )
inline

Free the column labels once the level above has been mapped (map_buildings_to_level0()).

163 { std::vector<int>().swap(m_lab); }

◆ report()

void IBFaceSet::report ( amrex::Real  time,
int  step,
bool  write_csv 
) const

Print the [IBSEB] summary line and, when asked, append one row per building to erf.ibseb.csv_file.

The summary carries the global face counts per direction, the number of buildings, the total face area and the range of the skin temperature; the CSV rows carry per building the face count, the area, the area-weighted mean fluxes and skin temperature with its range, the largest balance residual, and the sun the step used, from its start (zenith, azimuth, direct-normal and diffuse irradiance), on a row stamped with the time at the end of the step. All numbers are reduced over the ranks; the file is written by the I/O rank only, with a header only when the file does not exist yet, so a restarted run appends. Every level appends to the same file, its rows marked by the level column. With erf.ibseb.dump_faces_file the rows come with a face dump, dump_faces() to <prefix>[.levN][.stepNNNNNN].rank<N>.csv: a refined level N adds .levN so it does not overwrite the level below, and erf.ibseb.dump_faces_tag_step the step.

Parameters
timeSimulation time [s] written to the rows.
stepStep number written to the rows.
write_csvWhether to append rows, or only print the summary.

Summary line and per-building CSV rows. The dynamic quantities (skin temperature range and area-weighted mean per building) are gathered on the host from the device arrays and reduced; the static counts and areas were reduced in build().

1632 {
1633  // Skin temperature: min, max and per-building mean over all ranks.
1634  std::vector<Real> h_T(m_nface), h_A(m_nface);
1635  std::vector<int> h_b(m_nface);
1636  Gpu::copy(Gpu::deviceToHost, d_T_skin.begin(), d_T_skin.end(), h_T.begin());
1637  Gpu::copy(Gpu::deviceToHost, d_area.begin(), d_area.end(), h_A.begin());
1638  Gpu::copy(Gpu::deviceToHost, d_bid.begin(), d_bid.end(), h_b.begin());
1639  Gpu::streamSynchronize();
1640 
1641  std::vector<Real> h_S(m_nface), h_sh(m_nface), h_L(m_nface), h_H(m_nface), h_G(m_nface), h_r(m_nface), h_Q(m_nface), h_w(m_nface);
1642  Gpu::copy(Gpu::deviceToHost, d_w_star.begin(), d_w_star.end(), h_w.begin());
1643  Gpu::copy(Gpu::deviceToHost, d_resid.begin(), d_resid.end(), h_r.begin());
1644  Gpu::copy(Gpu::deviceToHost, d_Q_ext.begin(), d_Q_ext.end(), h_Q.begin());
1645  Gpu::copy(Gpu::deviceToHost, d_SW_abs.begin(), d_SW_abs.end(), h_S.begin());
1646  Gpu::copy(Gpu::deviceToHost, d_shadow.begin(), d_shadow.end(), h_sh.begin());
1647  Gpu::copy(Gpu::deviceToHost, d_LW_net.begin(), d_LW_net.end(), h_L.begin());
1648  Gpu::copy(Gpu::deviceToHost, d_H.begin(), d_H.end(), h_H.begin());
1649  Gpu::copy(Gpu::deviceToHost, d_G.begin(), d_G.end(), h_G.begin());
1650  Gpu::streamSynchronize();
1651 
1652  std::vector<Real> bsum(m_nbld + 1, 0.0), bsw(m_nbld + 1, 0.0), bsh(m_nbld + 1, 0.0), blw(m_nbld + 1, 0.0), bH(m_nbld + 1, 0.0);
1653  std::vector<Real> bG(m_nbld + 1, 0.0), bQ(m_nbld + 1, 0.0), btmin(m_nbld + 1, 1.0e30), btmax(m_nbld + 1, -1.0e30), bres(m_nbld + 1, 0.0);
1654  Real tmin = 1.0e30, tmax = -1.0e30, sw_sum = 0.0, sw_max = 0.0, sh_area = 0.0, lw_sum = 0.0, H_sum = 0.0, G_sum = 0.0;
1655  Real res_max = 0.0, Q_sum = 0.0, w_max = 0.0;
1656  for (int n = 0; n < m_nface; ++n) {
1657  w_max = std::max(w_max, h_w[n]);
1658  G_sum += h_G[n] * h_A[n];
1659  Q_sum += h_Q[n] * h_A[n];
1660  res_max = std::max(res_max, h_r[n]);
1661  bG[h_b[n]] += h_G[n] * h_A[n];
1662  bQ[h_b[n]] += h_Q[n] * h_A[n];
1663  btmin[h_b[n]] = std::min(btmin[h_b[n]], h_T[n]);
1664  btmax[h_b[n]] = std::max(btmax[h_b[n]], h_T[n]);
1665  bres[h_b[n]] = std::max(bres[h_b[n]], h_r[n]);
1666  bsum[h_b[n]] += h_T[n] * h_A[n];
1667  bsw[h_b[n]] += h_S[n] * h_A[n];
1668  bsh[h_b[n]] += h_sh[n] * h_A[n];
1669  blw[h_b[n]] += h_L[n] * h_A[n];
1670  bH[h_b[n]] += h_H[n] * h_A[n];
1671  tmin = std::min(tmin, h_T[n]);
1672  tmax = std::max(tmax, h_T[n]);
1673  sw_sum += h_S[n] * h_A[n];
1674  sw_max = std::max(sw_max, h_S[n]);
1675  sh_area += h_sh[n] * h_A[n];
1676  lw_sum += h_L[n] * h_A[n];
1677  H_sum += h_H[n] * h_A[n];
1678  }
1679  ParallelDescriptor::ReduceRealSum(bsum.data(), m_nbld + 1);
1680  ParallelDescriptor::ReduceRealSum(bsw.data(), m_nbld + 1);
1681  ParallelDescriptor::ReduceRealSum(bsh.data(), m_nbld + 1);
1682  ParallelDescriptor::ReduceRealSum(blw.data(), m_nbld + 1);
1683  ParallelDescriptor::ReduceRealSum(bH.data(), m_nbld + 1);
1684  ParallelDescriptor::ReduceRealSum(bG.data(), m_nbld + 1);
1685  ParallelDescriptor::ReduceRealSum(bQ.data(), m_nbld + 1);
1686  ParallelDescriptor::ReduceRealMin(btmin.data(), m_nbld + 1);
1687  ParallelDescriptor::ReduceRealMax(btmax.data(), m_nbld + 1);
1688  ParallelDescriptor::ReduceRealMax(bres.data(), m_nbld + 1);
1689  ParallelDescriptor::ReduceRealMax(res_max);
1690  ParallelDescriptor::ReduceRealMax(w_max);
1691  ParallelDescriptor::ReduceRealSum(Q_sum);
1692  ParallelDescriptor::ReduceRealMin(tmin);
1693  ParallelDescriptor::ReduceRealMax(tmax);
1694  ParallelDescriptor::ReduceRealSum(sw_sum);
1695  ParallelDescriptor::ReduceRealMax(sw_max);
1696  ParallelDescriptor::ReduceRealSum(sh_area);
1697  ParallelDescriptor::ReduceRealSum(lw_sum);
1698  ParallelDescriptor::ReduceRealSum(H_sum);
1699  ParallelDescriptor::ReduceRealSum(G_sum);
1700  const Real G_mean = (m_area_total > 0.0) ? G_sum / m_area_total : 0.0;
1701  const Real sw_mean = (m_area_total > 0.0) ? sw_sum / m_area_total : 0.0;
1702  const Real sh_frac = (m_area_total > 0.0) ? sh_area / m_area_total : 0.0;
1703  const Real lw_mean = (m_area_total > 0.0) ? lw_sum / m_area_total : 0.0;
1704  const Real H_mean = (m_area_total > 0.0) ? H_sum / m_area_total : 0.0;
1705 
1706  const Long nf_all = m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2];
1707  Print() << "[IBSEB] lev=" << m_lev << " step=" << step << " t=" << time
1708  << " faces=" << nf_all
1709  << " (x=" << m_nface_dir[0] << " y=" << m_nface_dir[1] << " z=" << m_nface_dir[2] << ")"
1710  << " buildings=" << m_nbld
1711  << " area=" << std::setprecision(10) << m_area_total << " m2"
1712  << " T_skin_min=" << ((nf_all > 0) ? tmin : Real(0.0))
1713  << " T_skin_max=" << ((nf_all > 0) ? tmax : Real(0.0))
1714  << " SW_abs_mean=" << sw_mean << " SW_abs_max=" << sw_max
1715  << " shadow_frac=" << sh_frac << " LW_net_mean=" << lw_mean
1716  << " H_mean=" << H_mean << " G_mean=" << G_mean
1717  << " Q_ext_mean=" << ((m_area_total > 0.0) ? Q_sum / m_area_total : Real(0.0))
1718  << " resid_max=" << res_max
1719  << " w_star_max=" << w_max
1720  << " H_total_W=" << std::setprecision(12) << H_sum << "\n";
1721  // Cost line: the slowest rank's per-step time of the balance and its face
1722  // count, so the cost of the face list on a larger case can be estimated.
1723  {
1724  Real ms = (m_cost_n > 0) ? 1000.0 * m_cost_s / m_cost_n : 0.0;
1725  Real ms_max = ms; int nf_max = m_nface;
1726  ParallelDescriptor::ReduceRealMax(ms_max);
1727  ParallelDescriptor::ReduceIntMax(nf_max);
1728  Print() << "[IBSEB] cost: lev=" << m_lev << " advance_ms_per_step_max=" << std::setprecision(4) << ms_max
1729  << " faces_per_rank_max=" << nf_max << " ranks=" << ParallelDescriptor::NProcs()
1730  << " init_s=" << m_init_cost_s << "\n";
1731  }
1732 
1733  if (m_params.debug) {
1734  for (int b = 1; b <= m_nbld; ++b) {
1735  const Real tmean = (m_bld_area[b] > 0.0) ? bsum[b] / m_bld_area[b] : Real(0.0);
1736  const Real swm = (m_bld_area[b] > 0.0) ? bsw[b] / m_bld_area[b] : Real(0.0);
1737  const Real shf = (m_bld_area[b] > 0.0) ? bsh[b] / m_bld_area[b] : Real(0.0);
1738  const Real lwm = (m_bld_area[b] > 0.0) ? blw[b] / m_bld_area[b] : Real(0.0);
1739  const Real Hm = (m_bld_area[b] > 0.0) ? bH[b] / m_bld_area[b] : Real(0.0);
1740  const Real Gm = (m_bld_area[b] > 0.0) ? bG[b] / m_bld_area[b] : Real(0.0);
1741  Print() << "[IBSEB DEBUG] building " << b << ": faces=" << m_bld_nface[b]
1742  << " area=" << m_bld_area[b] << " m2 T_skin_mean=" << tmean << " K"
1743  << " [" << btmin[b] << ", " << btmax[b] << "]"
1744  << " SW_abs_mean=" << swm << " W/m2 shadow_frac=" << shf
1745  << " LW_net_mean=" << lwm << " W/m2 H_mean=" << Hm << " W/m2 G_mean=" << Gm
1746  << " W/m2 resid_max=" << bres[b] << " W/m2\n";
1747  }
1748  }
1749 
1750  if (write_csv && !m_params.dump_faces_file.empty()) {
1751  std::string prefix = m_params.dump_faces_file;
1752  // Each level has its own list; a refined level tags its files so it
1753  // does not overwrite the dump of the level below.
1754  if (m_lev > 0) { prefix += ".lev" + std::to_string(m_lev); }
1756  std::ostringstream os;
1757  os << prefix << ".step" << std::setw(6) << std::setfill('0') << step;
1758  prefix = os.str();
1759  }
1760  dump_faces(prefix);
1761  }
1762 
1763  if (!write_csv || !ParallelDescriptor::IOProcessor()) { return; }
1764  const std::string header =
1765  "time_s,step,level,building,n_faces,area_m2,T_skin_mean_K,SW_abs_mean_Wm2,shadow_frac,LW_net_mean_Wm2,H_mean_Wm2,"
1766  "G_mean_Wm2,Q_ext_mean_Wm2,T_skin_min_K,T_skin_max_K,resid_max_Wm2,"
1767  "sun_zenith_deg,sun_azimuth_deg,dni_Wm2,diffuse_h_Wm2,building_level0";
1768  bool need_header = true;
1769  {
1770  // A restarted run appends; a file another version of the report wrote
1771  // (other columns) would take rows its header does not describe.
1772  std::ifstream probe(m_params.csv_file);
1773  std::string first;
1774  if (probe.good() && std::getline(probe, first)) {
1775  if (!first.empty() && first.back() == '\r') { first.pop_back(); }
1776  if (first != header) {
1777  Abort("erf.ibseb: " + m_params.csv_file + " has another set of columns than this version writes ("
1778  + first.substr(0, 60) + "...); move it aside or set erf.ibseb.csv_file to a new name");
1779  }
1780  need_header = false;
1781  }
1782  }
1783  std::ofstream csv(m_params.csv_file, std::ios::app);
1784  if (need_header) { csv << header << "\n"; }
1785  for (int b = 1; b <= m_nbld; ++b) {
1786  const Real tmean = (m_bld_area[b] > 0.0) ? bsum[b] / m_bld_area[b] : Real(0.0);
1787  const Real swm = (m_bld_area[b] > 0.0) ? bsw[b] / m_bld_area[b] : Real(0.0);
1788  const Real shf = (m_bld_area[b] > 0.0) ? bsh[b] / m_bld_area[b] : Real(0.0);
1789  const Real lwm = (m_bld_area[b] > 0.0) ? blw[b] / m_bld_area[b] : Real(0.0);
1790  const Real Hm = (m_bld_area[b] > 0.0) ? bH[b] / m_bld_area[b] : Real(0.0);
1791  const Real Gm = (m_bld_area[b] > 0.0) ? bG[b] / m_bld_area[b] : Real(0.0);
1792  const Real Qm = (m_bld_area[b] > 0.0) ? bQ[b] / m_bld_area[b] : Real(0.0);
1793  csv << std::setprecision(10) << time << "," << step << "," << m_lev << "," << b << ","
1794  << m_bld_nface[b] << "," << m_bld_area[b] << "," << tmean << "," << swm << "," << shf << "," << lwm << "," << Hm << ","
1795  << Gm << "," << Qm << "," << btmin[b] << "," << btmax[b] << "," << bres[b] << ","
1796  << m_sun.zenith * 180.0 / PI << "," << m_sun.azimuth * 180.0 / PI << "," << m_sun.dni << "," << m_sun.diffuse_h << ","
1797  << m_to_lev0[b] << "\n";
1798  }
1799 }
Vector< Real > h_r(khi+2)
void dump_faces(const std::string &prefix) const
Definition: ERF_IBFaceSet.cpp:1414
double m_init_cost_s
Wall-clock seconds of build() and compute_view_fractions()
Definition: ERF_IBFaceSet.H:566
bool dump_faces_tag_step
Name each dump by its step (keeps every one) instead of overwriting.
Definition: ERF_IBSEBParams.H:34
std::string dump_faces_file
Per-rank face dump at report time (empty = off); a refined level N adds ".levN".
Definition: ERF_IBSEBParams.H:33
Here is the call graph for this function:

◆ save_state()

void IBFaceSet::save_state ( amrex::MultiFab &  state) const

Write the face state (skin temperature, sensible flux and slab temperatures) into a field from make_state(), for the checkpoint.

The faces of this rank are written into a transfer field on this rank's grids cut by the state boxes, which a ParallelCopy moves onto the state's own distribution. Each face writes its own slot (see state_ncomp()), so no two faces touch the same component of the same cell and no atomics are needed; cells without a face in a slot keep zero there.

1547 {
1548  AMREX_ALWAYS_ASSERT(state.nComp() == state_ncomp() && state.boxArray() == m_state_ba);
1549  const int nl = n_layers();
1550  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1551  const int* pslot = d_slot.data();
1552  const Real* pT = d_T_skin.data(); const Real* pS = d_T_slab.data(); const Real* pH = d_H.data();
1553  MultiFab mine(m_xfer_ba, m_xfer_dm, state_ncomp(), 0);
1554  mine.setVal(0.0);
1555  for (MFIter mfi(mine); mfi.isValid(); ++mfi) {
1556  const int li = m_xfer_src[mfi.LocalIndex()];
1557  const int f0 = m_fab_start[li];
1558  const int f1 = m_fab_start[li + 1];
1559  const Box bx = mfi.validbox();
1560  auto const& st = mine.array(mfi);
1561  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1562  const int f = f0 + m;
1563  if (!bx.contains(IntVect(pi[f], pj[f], pk[f]))) { return; }
1564  const int c0 = pslot[f] * (2 + nl);
1565  st(pi[f], pj[f], pk[f], c0) = pT[f];
1566  st(pi[f], pj[f], pk[f], c0 + 1) = pH[f];
1567  for (int l = 0; l < nl; ++l) { st(pi[f], pj[f], pk[f], c0 + 2 + l) = pS[f * nl + l]; }
1568  });
1569  }
1570  Gpu::streamSynchronize();
1571  state.setVal(0.0);
1572  state.ParallelCopy(mine);
1573  if (m_params.debug) {
1574  Print() << "[IBSEB DEBUG] lev=" << m_lev << " face state saved: " << state_ncomp()
1575  << " components (" << m_nslots << " slots x " << (2 + n_layers()) << ") on " << m_state_ba.size()
1576  << " boxes, " << m_state_ba.numPts() << " of the level's " << m_domain_cells << " cells\n";
1577  }
1578 }
Here is the call graph for this function:

◆ scatter_diagnostics()

void IBFaceSet::scatter_diagnostics ( amrex::MultiFab &  nfaces,
amrex::MultiFab &  tskin 
) const

Scatter the faces into two cell-centred diagnostics for the plotfile.

Parameters
[out]nfacesNumber of wall faces touching each fluid cell (0 away from buildings; up to 3 at an outside corner, 6 in a one-cell slot).
[out]tskinMean skin temperature of those faces, 0 where there are none.

Both fields must be on the level's box array with no ghost cells. Faces of one fab are accumulated with atomic adds because a corner cell receives several faces.

Scatter the face count and the mean skin temperature into cell-centred fields. One kernel per fab over that fab's faces (contiguous, see fab_start()), with atomic adds since a corner cell collects several faces, then a kernel over the box divides the temperature sum by the count.

1508 {
1509  nfaces.setVal(0.0);
1510  tskin.setVal(0.0);
1511  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1512  const Real* pT = d_T_skin.data();
1513  for (MFIter mfi(nfaces); mfi.isValid(); ++mfi) {
1514  const int f0 = m_fab_start[mfi.LocalIndex()];
1515  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
1516  auto const& n = nfaces.array(mfi);
1517  auto const& t = tskin.array(mfi);
1518  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1519  const int f = f0 + m;
1520  Gpu::Atomic::AddNoRet(&n(pi[f], pj[f], pk[f]), Real(1.0));
1521  Gpu::Atomic::AddNoRet(&t(pi[f], pj[f], pk[f]), pT[f]);
1522  });
1523  const Box& bx = mfi.validbox();
1524  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1525  if (n(i, j, k) > 0.0) { t(i, j, k) /= n(i, j, k); }
1526  });
1527  }
1528  Gpu::streamSynchronize();
1529 }
Here is the call graph for this function:

◆ scatter_field()

void IBFaceSet::scatter_field ( const amrex::Gpu::DeviceVector< amrex::Real > &  v,
amrex::MultiFab &  out 
) const

Scatter one per-face array into a cell-centred field as the mean over the faces touching each cell (zero where there are none).

Scatter one per-face array as a per-cell mean, the same accumulation as scatter_diagnostics() with the face count as the divisor.

1386 {
1387  MultiFab cnt(out.boxArray(), out.DistributionMap(), 1, 0);
1388  cnt.setVal(0.0);
1389  out.setVal(0.0);
1390  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1391  const Real* pv = v.data();
1392  for (MFIter mfi(out); mfi.isValid(); ++mfi) {
1393  const int f0 = m_fab_start[mfi.LocalIndex()];
1394  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
1395  auto const& n = cnt.array(mfi);
1396  auto const& o = out.array(mfi);
1397  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1398  const int f = f0 + m;
1399  Gpu::Atomic::AddNoRet(&n(pi[f], pj[f], pk[f]), Real(1.0));
1400  Gpu::Atomic::AddNoRet(&o(pi[f], pj[f], pk[f]), pv[f]);
1401  });
1402  ParallelFor(mfi.validbox(), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1403  if (n(i, j, k) > 0.0) { o(i, j, k) /= n(i, j, k); }
1404  });
1405  }
1406  Gpu::streamSynchronize();
1407 }
Here is the call graph for this function:

◆ set_init_cost()

void IBFaceSet::set_init_cost ( double  seconds)
inline

Record the wall-clock time of the initialisation (build and view fractions).

517 { m_init_cost_s = seconds; }

◆ set_two_stream_sun()

void IBFaceSet::set_two_stream_sun ( amrex::Real  declination,
amrex::Real  hour_angle,
amrex::Real  latitude_deg,
amrex::Real  toa_irradiance 
)
inline

Give the sun of the next compute_shortwave() with erf.ibseb.sun_mode = two_stream: the two-stream radiation's declination and hour angle at that time, its site latitude and its top-of-atmosphere irradiance (the date's distance factor included). ERF::ibseb_set_two_stream_sun() calls it before every call; the call uses it up, so a shortwave without a fresh sun stops rather than reusing an old one.

134  {
135  m_ts_sun = {true, declination, hour_angle, latitude_deg, toa_irradiance};
136  }

◆ solve_balance()

void IBFaceSet::solve_balance ( amrex::Real  dt)

Prognostic balance of the current step. Per face: the slab's linear response to the skin temperature for this step, the Newton solve of ERF_IBSEBBalance.H with the shortwave, the external flux, the sky and ground longwave, the frozen wall-function coefficient and the latent flux, the slab advanced with the solution, and the fluxes (LW_in, LW_net, H, G) rewritten at the solution. Fills d_resid (|balance| at the end, non-zero only at a bound or the iteration cap) and d_niter. Needs compute_shortwave(), compute_longwave() and compute_sensible() of the same step first.

Parameters
dtAtmospheric time step [s], the slab's step.

Prognostic balance on every face: one kernel over the faces, each with its own slab response, Newton solve and slab advance. Everything the solve needs was stored by the three flux routines of the step, so no fab data is touched here. The fluxes are rewritten at the solution so that the dump, the reports and the heat deposited into the air all describe the same closed balance; d_resid holds what is left of it.

928 {
929  const int nl = n_layers();
930  const Real Tint = m_params.T_interior;
931  const Real Tmin = m_params.T_skin_min, Tmax = m_params.T_skin_max;
932  const Real tol = m_params.newton_tol_K, max_step = m_params.newton_max_step_K;
933  const int max_iter = m_params.newton_max_iter;
934  const Real* pkt = d_kth.data(); const Real* prc = d_rhocp.data(); const Real* pth = d_thick.data();
935  const Real* peps = d_emis.data(); const Real* pfb = d_f_bldg.data();
936  const Real* psw = d_SW_abs.data(); const Real* pq = d_Q_ext.data(); const Real* pext = d_LW_ext.data();
937  const Real* pHc = d_H_coeff.data(); const Real* pTa = d_T_air.data(); const Real* ptha = d_theta_air.data();
938  const Real* pLE = d_LE.data();
939  Real* pT = d_T_skin.data(); Real* pS = d_T_slab.data(); Real* pG = d_G.data();
940  Real* pH = d_H.data(); Real* pin = d_LW_down_in.data(); Real* pnet = d_LW_net.data();
941  Real* pres = d_resid.data(); int* pnit = d_niter.data();
942  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
943  // Zero-initialized: only the first nl entries are ever used, but the
944  // compiler cannot see that through the inlined solve and warns otherwise.
946  for (int l = 0; l < nl; ++l) { T[l] = pS[f * nl + l]; }
947  const Real dz = pth[f] / nl;
948  // Conduction the implicit slab step will take for a skin temperature Ts: a Ts - b.
949  Real a = 0.0, b = 0.0;
950  ibseb::slab_skin_response(T, Tint, pkt[f], prc[f], dz, dt, nl, a, b);
951  const Real exner = pTa[f] / ptha[f];
952  const Real q_abs = peps[f] * pq[f];
953  int n_it = 0; Real res = 0.0;
954  const Real Ts = ibseb::solve_skin_balance(pT[f], psw[f], q_abs, pext[f], peps[f], pfb[f],
955  pHc[f], exner, ptha[f], pLE[f], a, b,
956  Tmin, Tmax, max_step, tol, max_iter, n_it, res);
957  // Advance the slab with the solution and store every term at it.
958  pG[f] = ibseb::advance_slab_dirichlet(T, Ts, Tint, pkt[f], prc[f], dz, dt, nl);
959  for (int l = 0; l < nl; ++l) { pS[f * nl + l] = T[l]; }
960  const Real Ts4 = Ts * Ts * Ts * Ts;
961  pin[f] = pext[f] + pfb[f] * ibseb::SIGMA_SB * Ts4;
962  pnet[f] = peps[f] * (pin[f] - ibseb::SIGMA_SB * Ts4);
963  pH[f] = pHc[f] * (Ts / exner - ptha[f]);
964  pT[f] = Ts;
965  // The residual with the stored terms (the slab's own G, not a Ts - b),
966  // so it reports the closure of what is written out.
967  pres[f] = std::abs(psw[f] + q_abs + pnet[f] - pH[f] - pLE[f] - pG[f]);
968  pnit[f] = n_it;
969  });
970  Gpu::streamSynchronize();
971 
972  if (m_params.debug) {
973  std::vector<Real> h_r(m_nface), h_T(m_nface);
974  std::vector<int> h_n(m_nface);
975  Gpu::copy(Gpu::deviceToHost, d_resid.begin(), d_resid.end(), h_r.begin());
976  Gpu::copy(Gpu::deviceToHost, d_T_skin.begin(), d_T_skin.end(), h_T.begin());
977  Gpu::copy(Gpu::deviceToHost, d_niter.begin(), d_niter.end(), h_n.begin());
978  Gpu::streamSynchronize();
979  Real rmax = 0.0; int nmax = 0; Long nclamp = 0;
980  for (int n = 0; n < m_nface; ++n) {
981  rmax = std::max(rmax, h_r[n]); nmax = std::max(nmax, h_n[n]);
982  if (h_T[n] <= Tmin || h_T[n] >= Tmax) { ++nclamp; }
983  }
984  ParallelDescriptor::ReduceRealMax(rmax);
985  ParallelDescriptor::ReduceIntMax(nmax);
986  ParallelDescriptor::ReduceLongSum(nclamp);
987  Print() << "[IBSEB DEBUG] lev=" << m_lev << " balance: dt=" << dt << " s newton iterations max=" << nmax
988  << " residual max=" << rmax << " W/m2 faces at a bound=" << nclamp << "\n";
989  }
990 }
@ pres
Definition: ERF_Kessler.H:28
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE void slab_skin_response(const amrex::Real *T, amrex::Real T_int, amrex::Real k, amrex::Real rho_cp, amrex::Real dz, amrex::Real dt, int N, amrex::Real &a, amrex::Real &b)
Definition: ERF_IBSEBSlab.H:105
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solve_skin_balance(amrex::Real T_start, amrex::Real SW_abs, amrex::Real Q_abs, amrex::Real LW_ext, amrex::Real emis, amrex::Real f_bldg, amrex::Real C_H, amrex::Real exner, amrex::Real theta_air, amrex::Real LE, amrex::Real slab_a, amrex::Real slab_b, amrex::Real T_min, amrex::Real T_max, amrex::Real max_step, amrex::Real tol, int max_iter, int &n_iter, amrex::Real &resid)
Definition: ERF_IBSEBBalance.H:80
amrex::Real newton_max_step_K
Largest Newton step [K].
Definition: ERF_IBSEBParams.H:134
amrex::Real newton_tol_K
Newton stops when a step changes T_skin by less than this [K].
Definition: ERF_IBSEBParams.H:133
amrex::Real T_skin_min
Lower bound of the skin temperature [K].
Definition: ERF_IBSEBParams.H:130
amrex::Real T_skin_max
Upper bound [K]; raise it for faces under a fire.
Definition: ERF_IBSEBParams.H:131
int newton_max_iter
Newton iteration cap per face and step.
Definition: ERF_IBSEBParams.H:132
Here is the call graph for this function:

◆ state_boxarray()

const amrex::BoxArray& IBFaceSet::state_boxarray ( ) const
inline

Boxes of the checkpoint field: 4 x 4 column blocks clipped to the k-range that actually owns faces, so the field follows the shell of face-owning cells rather than the built volume. A block over the interior of a wide footprint keeps only the layer above the roof, while a block on a wall still spans its height. Built identically on every rank by build() from a reduced block k-range map; the same buildings give the same boxes on restart, whatever the rank count. Empty when the level has no faces.

204 { return m_state_ba; }

◆ state_ncomp()

int IBFaceSet::state_ncomp ( ) const
inline

Number of components of the checkpoint state field.

The field carries n_slots() slots of 2 + n_layers() values per cell: component slot * (2 + n_layers()) is the skin temperature of that slot, the next its sensible flux (read as the previous step's by the convective velocity scale), and the following n_layers() components are its slab layers.

A cell carries one slot per face it owns, numbered in the (dir, side) order in which build() finds them, so the width is the largest face count on any one cell of the level rather than the six a cell could hold in principle. Around ordinary buildings that maximum is two or three (an outside edge or corner); six needs a one-cell slot. build() rebuilds the numbering from the same blanking on restart, so the slots line up whatever the rank count.

189 { return m_nslots * (2 + n_layers()); }

Referenced by load_state(), make_state(), and save_state().

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

◆ sun()

const SunState& IBFaceSet::sun ( ) const
inline
504 { return m_sun; }

◆ top_sample_interface()

int IBFaceSet::top_sample_interface ( ) const
inline

The highest interface of the level's vertical grid that any of its faces samples with radiation = two_stream (a roof the bottom of its fluid cell, a ceiling and a wall the top of its cell too), counted from the domain bottom, over all ranks; -1 when the level has no faces. The two-stream beam the faces read holds the interfaces up to there.

341 { return m_top_sample; }

Member Data Documentation

◆ d_albedo

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_albedo

◆ d_area

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_area

Face area [m2].

Referenced by add_heat_flux_to_source(), dump_faces(), and report().

◆ d_bid

amrex::Gpu::DeviceVector<int> IBFaceSet::d_bid

Building id (1-based)

Referenced by dump_faces(), and report().

◆ d_col_top

amrex::Gpu::DeviceVector<int> IBFaceSet::d_col_top

◆ d_dir

amrex::Gpu::DeviceVector<int> IBFaceSet::d_dir

Face direction: 0 x, 1 y, 2 z.

Referenced by compute_shortwave(), compute_shortwave_two_stream(), and dump_faces().

◆ d_emis

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_emis

Optical properties of the face.

Referenced by dump_faces().

◆ d_f_bldg

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_f_bldg

View fractions, sum to 1.

Referenced by dump_faces().

◆ d_f_ground

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_f_ground

◆ d_f_sky

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_f_sky

◆ d_G

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_G

Referenced by dump_faces(), and report().

◆ d_H

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_H

◆ d_H_coeff

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_H_coeff

H = H_coeff (theta_skin - theta_air) [W/m2/K].

Referenced by dump_faces().

◆ d_hbld

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_hbld

Height of the face's building column [m].

Referenced by dump_faces().

◆ d_i

◆ d_j

◆ d_k

amrex::Gpu::DeviceVector<int> IBFaceSet::d_k

◆ d_kth

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_kth

Referenced by dump_faces().

◆ d_LE

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_LE

Referenced by dump_faces().

◆ d_LW_down_in

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_LW_down_in

Incoming longwave on the face (sky, ground and walls)

Referenced by dump_faces().

◆ d_LW_ext

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_LW_ext

Incoming longwave from sky and ground only [W/m2].

Referenced by dump_faces().

◆ d_LW_net

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_LW_net

Referenced by dump_faces(), and report().

◆ d_mat

amrex::Gpu::DeviceVector<int> IBFaceSet::d_mat

Material id (0 = uniform inputs)

Referenced by dump_faces().

◆ d_niter

amrex::Gpu::DeviceVector<int> IBFaceSet::d_niter

Newton iterations of the last solve.

Referenced by dump_faces().

◆ d_olen

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_olen

Obukhov length used [m] (1e30 when neutral)

Referenced by dump_faces().

◆ d_Q_ext

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_Q_ext

External incident flux [W/m2], e.g. fire radiation.

Referenced by dump_faces(), and report().

◆ d_resid

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_resid

|balance| after the solve [W/m2]

Referenced by dump_faces(), and report().

◆ d_rho

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_rho

Density of the fluid cell [kg/m3].

Referenced by dump_faces().

◆ d_rhocp

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_rhocp

Referenced by dump_faces().

◆ d_shadow

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_shadow

1 when the ray to the sun hits a building, else 0

Referenced by compute_shortwave(), compute_shortwave_two_stream(), dump_faces(), and report().

◆ d_side

amrex::Gpu::DeviceVector<int> IBFaceSet::d_side

Solid neighbour at -1 (low) or +1 (high) side.

Referenced by compute_shortwave(), compute_shortwave_two_stream(), and dump_faces().

◆ d_slot

amrex::Gpu::DeviceVector<int> IBFaceSet::d_slot

Slot of the face within its cell's checkpoint record.

Referenced by load_state(), and save_state().

◆ d_SW_abs

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_SW_abs

◆ d_SW_diffuse_in

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_SW_diffuse_in

Sky and ground-reflected diffuse incident.

Referenced by compute_shortwave(), compute_shortwave_two_stream(), and dump_faces().

◆ d_SW_direct_in

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_SW_direct_in

Direct beam incident on the face.

Referenced by compute_shortwave(), compute_shortwave_two_stream(), and dump_faces().

◆ d_T_air

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_T_air

Air temperature of the fluid cell.

Referenced by dump_faces().

◆ d_T_skin

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_T_skin

Skin temperature [K].

Referenced by dump_faces(), load_state(), report(), save_state(), and scatter_diagnostics().

◆ d_T_slab

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_T_slab

Slab layers [K], face-major: [face * n_layers + layer].

Referenced by dump_faces(), load_state(), and save_state().

◆ d_theta_air

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_theta_air

Potential temperature of the fluid cell [K].

Referenced by dump_faces().

◆ d_thick

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_thick

Slab conductivity [W/m/K], heat capacity [J/m3/K], thickness [m].

Referenced by dump_faces().

◆ d_U_tan

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_U_tan

Tangential wind speed at the fluid cell [m/s].

Referenced by dump_faces().

◆ d_ustar

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_ustar

Friction velocity of the wall function [m/s].

Referenced by dump_faces().

◆ d_w_star

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_w_star

Convective velocity scale of the step [m/s].

Referenced by dump_faces(), and report().

◆ d_xf

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_xf

◆ d_yf

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_yf

◆ d_z_i

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_z_i

Depth in w* [m]: mixed layer above a roof, building height for a wall.

Referenced by dump_faces().

◆ d_zf

amrex::Gpu::DeviceVector<amrex::Real> IBFaceSet::d_zf

Face centre [m].

Referenced by compute_shortwave(), compute_shortwave_two_stream(), and dump_faces().

◆ m_area_total

amrex::Real IBFaceSet::m_area_total = 0.0
private

Total face area [m2].

Referenced by print_debug_summary(), and report().

◆ m_bld_area

std::vector<amrex::Real> IBFaceSet::m_bld_area
private

Face area per building [m2].

Referenced by print_debug_summary(), and report().

◆ m_bld_ihi

std::vector<int> IBFaceSet::m_bld_ihi
private

Referenced by print_debug_summary().

◆ m_bld_ilo

std::vector<int> IBFaceSet::m_bld_ilo
private

Referenced by print_debug_summary().

◆ m_bld_jhi

std::vector<int> IBFaceSet::m_bld_jhi
private

Referenced by print_debug_summary().

◆ m_bld_jlo

std::vector<int> IBFaceSet::m_bld_jlo
private

Referenced by print_debug_summary().

◆ m_bld_ncol

std::vector<amrex::Long> IBFaceSet::m_bld_ncol
private

Referenced by print_debug_summary().

◆ m_bld_nface

std::vector<amrex::Long> IBFaceSet::m_bld_nface
private

Faces per building.

Referenced by print_debug_summary(), and report().

◆ m_col_i0

int IBFaceSet::m_col_i0 = 0

◆ m_col_j0

int IBFaceSet::m_col_j0 = 0

◆ m_col_nx

int IBFaceSet::m_col_nx = 0

◆ m_col_ny

int IBFaceSet::m_col_ny = 0

◆ m_col_top_max

amrex::Real IBFaceSet::m_col_top_max = 0.0
private

Tallest column [m].

Referenced by compute_shortwave(), and compute_shortwave_two_stream().

◆ m_cost_n

amrex::Long IBFaceSet::m_cost_n = 0
private

Number of per-step updates timed.

Referenced by add_cost(), and report().

◆ m_cost_s

double IBFaceSet::m_cost_s = 0.0
private

Wall-clock seconds spent in the per-step update on this rank.

Referenced by add_cost(), and report().

◆ m_dom_lo

amrex::IntVect IBFaceSet::m_dom_lo {AMREX_D_DECL(0, 0, 0)}
private

Low corner of the level's domain.

Referenced by add_outside_occluders(), and compute_shortwave_two_stream().

◆ m_domain_cells

amrex::Long IBFaceSet::m_domain_cells = 0

Cells of the level, for the debug summary of the field.

Referenced by save_state().

◆ m_dx

amrex::Real IBFaceSet::m_dx[3] = {0.0, 0.0, 0.0}
private

◆ m_fab_start

◆ m_init_cost_s

double IBFaceSet::m_init_cost_s = 0.0
private

Wall-clock seconds of build() and compute_view_fractions()

Referenced by report(), and set_init_cost().

◆ m_lab

std::vector<int> IBFaceSet::m_lab
private

◆ m_lab_i0

int IBFaceSet::m_lab_i0 = 0
private

Referenced by map_buildings_to_level0().

◆ m_lab_j0

int IBFaceSet::m_lab_j0 = 0
private

Referenced by map_buildings_to_level0().

◆ m_lab_nx

int IBFaceSet::m_lab_nx = 0
private

Referenced by map_buildings_to_level0().

◆ m_lab_ny

int IBFaceSet::m_lab_ny = 0
private

Referenced by map_buildings_to_level0().

◆ m_lev

int IBFaceSet::m_lev = 0
private

◆ m_max_path

amrex::Real IBFaceSet::m_max_path = 0.0
private

Ray path cap [m].

Referenced by compute_shortwave(), and compute_shortwave_two_stream().

◆ m_nbld

int IBFaceSet::m_nbld = 0
private

Buildings of the level (global)

Referenced by map_buildings_to_level0(), n_buildings(), print_debug_summary(), and report().

◆ m_nface

int IBFaceSet::m_nface = 0
private

◆ m_nface_dir

amrex::Long IBFaceSet::m_nface_dir[3] = {0, 0, 0}
private

Faces per direction.

Referenced by compute_shortwave_two_stream(), print_debug_summary(), and report().

◆ m_nslots

int IBFaceSet::m_nslots = 0
private

Slots per cell of the checkpoint field (largest face count on a cell)

Referenced by n_slots(), save_state(), and state_ncomp().

◆ m_nx

int IBFaceSet::m_nx = 0
private

◆ m_ny

int IBFaceSet::m_ny = 0
private

Columns of the domain.

Referenced by compute_shortwave(), and compute_shortwave_two_stream().

◆ m_params

IBSEBParams IBFaceSet::m_params
private

◆ m_per_x

bool IBFaceSet::m_per_x = false
private

◆ m_per_y

bool IBFaceSet::m_per_y = false
private

Periodicity in x and y.

Referenced by compute_shortwave(), and compute_shortwave_two_stream().

◆ m_state_ba

amrex::BoxArray IBFaceSet::m_state_ba

◆ m_sun

SunState IBFaceSet::m_sun
private

◆ m_to_lev0

std::vector<int> IBFaceSet::m_to_lev0
private

◆ m_top_sample

int IBFaceSet::m_top_sample = -1
private

Highest interface a face samples, over all ranks (-1: no faces)

Referenced by has_faces(), and top_sample_interface().

◆ m_ts_sun

TwoStreamSun IBFaceSet::m_ts_sun
private

◆ m_x_lo

amrex::Real IBFaceSet::m_x_lo = 0.0
private

◆ m_xfer_ba

amrex::BoxArray IBFaceSet::m_xfer_ba

Referenced by load_state(), and save_state().

◆ m_xfer_dm

amrex::DistributionMapping IBFaceSet::m_xfer_dm

Referenced by load_state(), and save_state().

◆ m_xfer_src

std::vector<int> IBFaceSet::m_xfer_src

Referenced by load_state(), and save_state().

◆ m_y_lo

amrex::Real IBFaceSet::m_y_lo = 0.0
private

Domain origin [m].

Referenced by compute_shortwave(), and compute_shortwave_two_stream().

◆ m_z_ground

amrex::Real IBFaceSet::m_z_ground = 0.0
private

Ground height [m] (flat, the domain bottom)

Referenced by compute_shortwave(), and compute_shortwave_two_stream().


The documentation for this class was generated from the following files: