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...
 

Public Member Functions

 IBFaceSet (const IBSEBParams &params, int lev)
 
void build (const amrex::MultiFab &blanking, const amrex::Geometry &geom)
 
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)
 
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)
 
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 SunStatesun () 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::Reald_albedo
 
amrex::Gpu::DeviceVector< amrex::Reald_emis
 Optical properties of the face. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_kth
 
amrex::Gpu::DeviceVector< amrex::Reald_rhocp
 
amrex::Gpu::DeviceVector< amrex::Reald_thick
 Slab conductivity [W/m/K], heat capacity [J/m3/K], thickness [m]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_area
 Face area [m2]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_xf
 
amrex::Gpu::DeviceVector< amrex::Reald_yf
 
amrex::Gpu::DeviceVector< amrex::Reald_zf
 Face centre [m]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_T_skin
 Skin temperature [K]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_T_slab
 Slab layers [K], face-major: [face * n_layers + layer]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_f_sky
 
amrex::Gpu::DeviceVector< amrex::Reald_f_ground
 
amrex::Gpu::DeviceVector< amrex::Reald_f_bldg
 View fractions, sum to 1. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_SW_abs
 
amrex::Gpu::DeviceVector< amrex::Reald_LW_net
 
amrex::Gpu::DeviceVector< amrex::Reald_H
 
amrex::Gpu::DeviceVector< amrex::Reald_LE
 
amrex::Gpu::DeviceVector< amrex::Reald_G
 
amrex::Gpu::DeviceVector< amrex::Reald_Q_ext
 External incident flux [W/m2], e.g. fire radiation. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_shadow
 1 when the ray to the sun hits a building, else 0 More...
 
amrex::Gpu::DeviceVector< amrex::Reald_SW_direct_in
 Direct beam incident on the face. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_SW_diffuse_in
 Sky and ground-reflected diffuse incident. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_LW_down_in
 Incoming longwave on the face (sky, ground and walls) More...
 
amrex::Gpu::DeviceVector< amrex::Reald_T_air
 Air temperature of the fluid cell. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_theta_air
 Potential temperature of the fluid cell [K]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_rho
 Density of the fluid cell [kg/m3]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_U_tan
 Tangential wind speed at the fluid cell [m/s]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_ustar
 Friction velocity of the wall function [m/s]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_H_coeff
 H = H_coeff (theta_skin - theta_air) [W/m2/K]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_hbld
 Height of the face's building column [m]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_w_star
 Convective velocity scale of the step [m/s]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_olen
 Obukhov length used [m] (1e30 when neutral) More...
 
amrex::Gpu::DeviceVector< amrex::Reald_z_i
 Depth in w* [m]: mixed layer above a roof, building height for a wall. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_LW_ext
 Incoming longwave from sky and ground only [W/m2]. More...
 
amrex::Gpu::DeviceVector< amrex::Reald_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_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::Realm_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
 
int m_nx = 0
 
int m_ny = 0
 Columns of the 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
 
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.
76 : m_params(params), m_lev(lev) {}
int m_lev
AMR level, for reports.
Definition: ERF_IBFaceSet.H:412
IBSEBParams m_params
Copy of the inputs.
Definition: ERF_IBFaceSet.H:411

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().

399 { 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:436
amrex::Long m_cost_n
Number of per-step updates timed.
Definition: ERF_IBFaceSet.H:437

◆ 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.

811 {
812  const auto dx = geom.CellSizeArray();
813  const Real vol = dx[0] * dx[1] * dx[2];
814  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
815  const Real* pH = d_H.data(); const Real* pA = d_area.data();
816  for (MFIter mfi(source); mfi.isValid(); ++mfi) {
817  const int f0 = m_fab_start[mfi.LocalIndex()];
818  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
819  auto const& src = source.array(mfi);
820  auto const& c = cons.const_array(mfi);
821  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
822  const int f = f0 + m;
823  const int i = pi[f], j = pj[f], k = pk[f];
824  const Real exner = getExnergivenRTh(c(i, j, k, RhoTheta_comp), rdOcp);
825  const Real tend = pH[f] * pA[f] / (c_p * vol * exner);
826  Gpu::Atomic::AddNoRet(&src(i, j, k, RhoTheta_comp), tend);
827  });
828  }
829  Gpu::streamSynchronize();
830 }
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:40
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:320
amrex::Gpu::DeviceVector< amrex::Real > d_H
Definition: ERF_IBFaceSet.H:337
amrex::Gpu::DeviceVector< amrex::Real > d_area
Face area [m2].
Definition: ERF_IBFaceSet.H:328
amrex::Gpu::DeviceVector< int > d_j
Definition: ERF_IBFaceSet.H:320
std::vector< int > m_fab_start
See fab_start()
Definition: ERF_IBFaceSet.H:416
amrex::Gpu::DeviceVector< int > d_i
Definition: ERF_IBFaceSet.H:320
@ cons
Definition: ERF_IndexDefines.H:214
real(c_double), parameter, private pi
Definition: ERF_module_mp_morr_two_moment.F90:100
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.

533 {
534  if (m_params.material_file.empty()) { return; }
535  const Vector<IBSEBMaterial> table = read_ibseb_materials(m_params.material_file);
536  auto find = [&](int id) -> const IBSEBMaterial& {
537  for (const auto& m : table) { if (m.mat_id == id) return m; }
538  Abort("erf.ibseb: material id " + std::to_string(id) + " is not in " + m_params.material_file);
539  return table[0];
540  };
541  std::vector<int> h_b(m_nface);
542  Gpu::copy(Gpu::deviceToHost, d_bid.begin(), d_bid.end(), h_b.begin());
543  Gpu::streamSynchronize();
544  std::vector<int> h_mat(m_nface);
545  std::vector<Real> h_alb(m_nface), h_emi(m_nface), h_k(m_nface), h_rc(m_nface), h_th(m_nface);
546  for (int n = 0; n < m_nface; ++n) {
547  const int b = h_b[n];
548  int id = m_params.material_default;
549  if (b >= 1 && b <= static_cast<int>(m_params.material_by_building.size())) {
550  id = m_params.material_by_building[b - 1];
551  }
552  const IBSEBMaterial& m = find(id);
553  h_mat[n] = m.mat_id; h_alb[n] = m.albedo; h_emi[n] = m.emissivity;
554  h_k[n] = m.k_therm; h_rc[n] = m.rho_cp; h_th[n] = m.thickness;
555  }
556  upload(d_mat, h_mat); upload(d_albedo, h_alb); upload(d_emis, h_emi);
557  upload(d_kth, h_k); upload(d_rhocp, h_rc); upload(d_thick, h_th);
558  Gpu::streamSynchronize();
559  if (m_params.debug) {
560  Print() << "[IBSEB DEBUG] lev=" << m_lev << " materials: " << table.size() << " in "
561  << m_params.material_file << ", default id " << m_params.material_default;
562  for (size_t b = 0; b < m_params.material_by_building.size(); ++b) {
563  Print() << ", building " << b + 1 << " -> " << m_params.material_by_building[b];
564  }
565  Print() << "\n";
566  for (const auto& m : table) {
567  Print() << "[IBSEB DEBUG] material " << m.mat_id << " (" << m.name << "): albedo=" << m.albedo
568  << " emissivity=" << m.emissivity << " k=" << m.k_therm << " W/m/K rho_cp=" << m.rho_cp
569  << " J/m3/K thickness=" << m.thickness << " m\n";
570  }
571  }
572 }
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:327
amrex::Gpu::DeviceVector< amrex::Real > d_albedo
Definition: ERF_IBFaceSet.H:326
amrex::Gpu::DeviceVector< int > d_mat
Material id (0 = uniform inputs)
Definition: ERF_IBFaceSet.H:325
amrex::Gpu::DeviceVector< amrex::Real > d_emis
Optical properties of the face.
Definition: ERF_IBFaceSet.H:326
int m_nface
Faces owned by this rank.
Definition: ERF_IBFaceSet.H:413
amrex::Gpu::DeviceVector< int > d_bid
Building id (1-based)
Definition: ERF_IBFaceSet.H:324
amrex::Gpu::DeviceVector< amrex::Real > d_rhocp
Definition: ERF_IBFaceSet.H:327
amrex::Gpu::DeviceVector< amrex::Real > d_kth
Definition: ERF_IBFaceSet.H:327
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:99
std::string material_file
Definition: ERF_IBSEBParams.H:98
std::vector< int > material_by_building
Definition: ERF_IBSEBParams.H:100
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:

◆ 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.

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 nothing but the column mask used for the building ids.

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.

89 {
90  const Box& domain = geom.Domain();
91  const int nx = domain.length(0);
92  const int ny = domain.length(1);
93  const int ilo = domain.smallEnd(0);
94  const int jlo = domain.smallEnd(1);
95  const auto dx = geom.CellSizeArray();
96  const auto plo = geom.ProbLoArray();
97  const Real face_area[3] = { dx[1] * dx[2], dx[0] * dx[2], dx[0] * dx[1] };
98  m_nx = nx; m_ny = ny;
99  m_x_lo = plo[0]; m_y_lo = plo[1];
100  for (int d = 0; d < 3; ++d) { m_dx[d] = dx[d]; }
101  m_per_x = geom.isPeriodic(0); m_per_y = geom.isPeriodic(1);
102  m_max_path = 4.0 * (geom.ProbLength(0) + geom.ProbLength(1));
103  m_z_ground = plo[2];
104 
105  // The face list of a cell, and with it the slot numbering of the
106  // checkpoint record (see state_ncomp()), must not depend on the
107  // decomposition: every valid cell has to see its six neighbours, which
108  // needs a filled ghost layer on the blanking.
109  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(blanking.nGrowVect().min() >= 1,
110  "erf.ibseb: the blanking needs at least one ghost cell");
111 
112  std::vector<int> h_i, h_j, h_k, h_dir, h_side, h_nbi, h_nbj, h_slot;
113  std::vector<Real> h_area, h_xf, h_yf, h_zf;
114  m_domain_cells = domain.numPts();
115 
116  // Bounding box of the solid columns (global), so the column arrays below
117  // cover the built area only: 8 bytes per built column per rank.
118  int bi0 = std::numeric_limits<int>::max(), bi1 = std::numeric_limits<int>::min();
119  int bj0 = bi0, bj1 = bi1;
120  for (MFIter mfi(blanking); mfi.isValid(); ++mfi) {
121  const Box& bx = mfi.validbox();
122  const HostFab hf(blanking[mfi]);
123  auto const& b = hf.array();
124  const auto lo = lbound(bx);
125  const auto hi = ubound(bx);
126  for (int k = lo.z; k <= hi.z; ++k) {
127  for (int j = lo.y; j <= hi.y; ++j) {
128  for (int i = lo.x; i <= hi.x; ++i) {
129  if (b(i, j, k) >= 0.5) {
130  bi0 = std::min(bi0, i); bi1 = std::max(bi1, i);
131  bj0 = std::min(bj0, j); bj1 = std::max(bj1, j);
132  }
133  }}}
134  }
135  ParallelDescriptor::ReduceIntMin(bi0); ParallelDescriptor::ReduceIntMin(bj0);
136  ParallelDescriptor::ReduceIntMax(bi1); ParallelDescriptor::ReduceIntMax(bj1);
137  const bool any_solid = (bi1 >= bi0) && (bj1 >= bj0);
138  m_col_i0 = any_solid ? bi0 - ilo : 0; m_col_j0 = any_solid ? bj0 - jlo : 0;
139  m_col_nx = any_solid ? bi1 - bi0 + 1 : 0; m_col_ny = any_solid ? bj1 - bj0 + 1 : 0;
140  const int bw = m_col_nx, bh = m_col_ny;
141  const size_t ncol = static_cast<size_t>(bw) * bh;
142  // Highest solid cell of each built column of this rank, as a domain-
143  // relative k index, -1 where the column carries no solid cell; reduced
144  // below so every rank sees the same columns. This one array is both the
145  // solid-column mask of the labelling (a column is solid exactly when its
146  // top is not -1) and the column top of the ray cast, and it is an index
147  // rather than a height so that it costs 4 bytes per column instead of the
148  // 24 a separate mask, index and height in Real took.
149  std::vector<int> coltop_k(ncol, -1);
150 
151  m_fab_start.clear();
152  for (MFIter mfi(blanking); mfi.isValid(); ++mfi) {
153  m_fab_start.push_back(static_cast<int>(h_i.size()));
154  const Box& bx = mfi.validbox();
155  const HostFab hf(blanking[mfi]);
156  auto const& b = hf.array();
157  const Box& fbox = Box(b);
158  const auto lo = lbound(bx);
159  const auto hi = ubound(bx);
160  for (int k = lo.z; k <= hi.z; ++k) {
161  for (int j = lo.y; j <= hi.y; ++j) {
162  for (int i = lo.x; i <= hi.x; ++i) {
163  if (b(i, j, k) >= 0.5) {
164  const size_t c = static_cast<size_t>(i - bi0) * bh + (j - bj0);
165  coltop_k[c] = std::max(coltop_k[c], k - domain.smallEnd(2));
166  continue;
167  }
168  // Slot of each face within its cell's checkpoint record: the
169  // faces of a cell are numbered in the (dir, side) order of the
170  // loops below, which the rebuild from the same blanking
171  // reproduces on restart whatever the rank count.
172  int cell_slot = 0;
173  for (int d = 0; d < 3; ++d) {
174  for (int s = -1; s <= 1; s += 2) {
175  IntVect nb(i, j, k);
176  nb[d] += s;
177  if (!fbox.contains(nb)) { continue; }
178  if (!geom.isPeriodic(d) && !domain.contains(nb)) { continue; }
179  if (b(nb) < 0.5) { continue; }
180  h_i.push_back(i); h_j.push_back(j); h_k.push_back(k);
181  h_dir.push_back(d); h_side.push_back(s);
182  h_slot.push_back(cell_slot++);
183  // Face centre: the fluid cell centre moved half a cell toward the solid.
184  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]) };
185  c[d] += 0.5 * s * dx[d];
186  h_xf.push_back(c[0]); h_yf.push_back(c[1]); h_zf.push_back(c[2]);
187  // Column of the solid neighbour, wrapped for periodic directions.
188  int ci = nb[0] - ilo, cj = nb[1] - jlo;
189  ci = (ci % nx + nx) % nx;
190  cj = (cj % ny + ny) % ny;
191  h_nbi.push_back(ci); h_nbj.push_back(cj);
192  h_area.push_back(face_area[d]);
193  }
194  }
195  }}}
196  }
197  m_fab_start.push_back(static_cast<int>(h_i.size()));
198  m_nface = static_cast<int>(h_i.size());
199 
200  // Width of the checkpoint record: the largest number of faces on one cell
201  // anywhere on the level (see state_ncomp()), reduced so every rank lays
202  // the field out the same way.
203  m_nslots = 0;
204  for (int n = 0; n < m_nface; ++n) { m_nslots = std::max(m_nslots, h_slot[n] + 1); }
205  ParallelDescriptor::ReduceIntMax(m_nslots);
206 
207  // Checkpoint layout: 4 x 4 column blocks clipped to the k-range that owns
208  // faces (see state_boxarray()), the same on every rank from a reduced
209  // block k-range map; and the transfer layer, this rank's grids cut by
210  // those blocks, gathered into one global BoxArray owned by the rank whose
211  // grid each piece came from.
212  {
213  constexpr int BS = 4;
214  const int klo = domain.smallEnd(2);
215  const int nbx = (nx + BS - 1) / BS, nby = (ny + BS - 1) / BS;
216  // Lowest and highest face-owning cell of each block. Face-owning cells
217  // are the fluid cells against solid, a one-cell shell, so a block over
218  // the interior of a wide footprint owns faces only in the layer above
219  // the roof and its box collapses to that layer instead of carrying the
220  // whole column from the ground.
221  std::vector<int> blk_lo(static_cast<size_t>(nbx) * nby, std::numeric_limits<int>::max());
222  std::vector<int> blk_hi(static_cast<size_t>(nbx) * nby, -1);
223  for (int n = 0; n < m_nface; ++n) {
224  const size_t p = static_cast<size_t>((h_i[n] - ilo) / BS) * nby + (h_j[n] - jlo) / BS;
225  blk_lo[p] = std::min(blk_lo[p], h_k[n] - klo);
226  blk_hi[p] = std::max(blk_hi[p], h_k[n] - klo);
227  }
228  ParallelDescriptor::ReduceIntMin(blk_lo.data(), static_cast<int>(blk_lo.size()));
229  ParallelDescriptor::ReduceIntMax(blk_hi.data(), static_cast<int>(blk_hi.size()));
230  BoxList bl;
231  for (int bi = 0; bi < nbx; ++bi) {
232  for (int bj = 0; bj < nby; ++bj) {
233  const size_t p = static_cast<size_t>(bi) * nby + bj;
234  const int kmax = blk_hi[p];
235  if (kmax < 0) { continue; }
236  const int kmin = blk_lo[p];
237  const IntVect blo(ilo + bi * BS, jlo + bj * BS, klo + kmin);
238  const IntVect bhi(std::min(ilo + (bi + 1) * BS - 1, domain.bigEnd(0)),
239  std::min(jlo + (bj + 1) * BS - 1, domain.bigEnd(1)), klo + kmax);
240  bl.push_back(Box(blo, bhi));
241  }
242  }
243  m_state_ba = BoxArray(bl);
244  Vector<Box> mybx;
245  m_xfer_src.clear();
246  for (MFIter mfi(blanking); mfi.isValid(); ++mfi) {
247  for (const auto& pr : m_state_ba.intersections(mfi.validbox())) {
248  mybx.push_back(pr.second);
249  m_xfer_src.push_back(mfi.LocalIndex());
250  }
251  }
252  const int nranks = ParallelDescriptor::NProcs();
253  const int nmine = static_cast<int>(mybx.size());
254  Vector<int> counts(nranks, 0);
255  if (nranks == 1) {
256  // The non-MPI implementation of AllGather leaves its output
257  // untouched, so populate the single-rank count explicitly.
258  counts[0] = nmine;
259  } else {
260  ParallelAllGather::AllGather(nmine, counts.data(), ParallelDescriptor::Communicator());
261  }
262  Vector<Box> allbx(mybx);
263  AllGatherBoxes(allbx);
264  Vector<int> owner;
265  owner.reserve(allbx.size());
266  for (int r = 0; r < nranks; ++r) { for (int n = 0; n < counts[r]; ++n) { owner.push_back(r); } }
267  AMREX_ALWAYS_ASSERT(owner.size() == allbx.size());
268  BoxList xl;
269  for (const Box& b : allbx) { xl.push_back(b); }
270  m_xfer_ba = BoxArray(xl);
271  m_xfer_dm = DistributionMapping(owner);
272  }
273 
274  // Buildings: 4-connected solid columns, numbered in scan order. The column
275  // mask is reduced so every rank labels the same columns with the same ids;
276  // the labelling itself is a plain depth-first flood fill on the host.
277  if (ncol > 0) {
278  ParallelDescriptor::ReduceIntMax(coltop_k.data(), static_cast<int>(ncol));
279  }
280  // Column index in the built box of a face's solid neighbour (a 0-based,
281  // wrapped domain column; the neighbour is solid, so it lies in the box).
282  auto col_index = [&] (int n) {
283  const size_t c = static_cast<size_t>(h_nbi[n] - m_col_i0) * bh + (h_nbj[n] - m_col_j0);
284  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);
285  return c;
286  };
287  // Column tops for the ray cast: the index map itself, which
288  // ibseb::column_top() turns into a height one cell above the highest
289  // solid cell, the ground where the column is fluid.
290  upload(d_col_top, coltop_k);
291  m_col_top_max = plo[2];
292  for (size_t c = 0; c < ncol; ++c) {
293  if (coltop_k[c] >= 0) {
294  m_col_top_max = std::max(m_col_top_max, Real(plo[2] + (coltop_k[c] + 1.0) * dx[2]));
295  }
296  }
297  // Height of each face's building column, for the wall-function depth.
298  {
299  std::vector<Real> h_hb(h_i.size());
300  for (size_t n = 0; n < h_i.size(); ++n) {
301  const int kt = coltop_k[col_index(n)];
302  h_hb[n] = (kt >= 0) ? (kt + 1.0) * dx[2] : 0.0;
303  }
304  upload(d_hbld, h_hb);
305  }
306  std::vector<int> h_bid(m_nface);
307  {
308  std::vector<int> label(ncol, 0);
309  std::vector<int> stack;
310  m_nbld = 0;
311  for (int ci = 0; ci < bw; ++ci) {
312  for (int cj = 0; cj < bh; ++cj) {
313  const size_t p = static_cast<size_t>(ci) * bh + cj;
314  if (coltop_k[p] < 0 || label[p] > 0) { continue; }
315  ++m_nbld;
316  label[p] = m_nbld;
317  stack.clear();
318  stack.push_back(static_cast<int>(p));
319  while (!stack.empty()) {
320  const int q = stack.back(); stack.pop_back();
321  const int qi = q / bh, qj = q % bh;
322  const int ni[4] = {qi - 1, qi + 1, qi, qi};
323  const int nj[4] = {qj, qj, qj - 1, qj + 1};
324  for (int n = 0; n < 4; ++n) {
325  if (ni[n] < 0 || ni[n] >= bw || nj[n] < 0 || nj[n] >= bh) { continue; }
326  const size_t r = static_cast<size_t>(ni[n]) * bh + nj[n];
327  if (coltop_k[r] >= 0 && label[r] == 0) {
328  label[r] = m_nbld;
329  stack.push_back(static_cast<int>(r));
330  }
331  }
332  }
333  }
334  }
335  for (int n = 0; n < m_nface; ++n) {
336  h_bid[n] = label[col_index(n)];
337  }
338  // Footprints, for the debug summary: columns and bounding box per building.
339  m_bld_ncol.assign(m_nbld + 1, 0);
340  m_bld_ilo.assign(m_nbld + 1, nx); m_bld_ihi.assign(m_nbld + 1, -1);
341  m_bld_jlo.assign(m_nbld + 1, ny); m_bld_jhi.assign(m_nbld + 1, -1);
342  for (int ci = 0; ci < bw; ++ci) {
343  for (int cj = 0; cj < bh; ++cj) {
344  const int b = label[static_cast<size_t>(ci) * bh + cj];
345  if (b == 0) { continue; }
346  const int gi = ci + m_col_i0 + ilo, gj = cj + m_col_j0 + jlo;
347  m_bld_ncol[b] += 1;
348  m_bld_ilo[b] = std::min(m_bld_ilo[b], gi); m_bld_ihi[b] = std::max(m_bld_ihi[b], gi);
349  m_bld_jlo[b] = std::min(m_bld_jlo[b], gj); m_bld_jhi[b] = std::max(m_bld_jhi[b], gj);
350  }
351  }
352  } // label and stack, one int per column each, are freed here
353  // The column map has done its work too; free it before the per-face
354  // device uploads below.
355  std::vector<int>().swap(coltop_k);
356 
357  // Static totals: per direction, per building, and the summed area.
358  std::vector<Long> nd(3, 0);
359  std::vector<Long> bn(m_nbld + 1, 0);
360  std::vector<Real> ba(m_nbld + 1, 0.0);
361  Real area = 0.0;
362  for (int n = 0; n < m_nface; ++n) {
363  nd[h_dir[n]] += 1;
364  bn[h_bid[n]] += 1;
365  ba[h_bid[n]] += h_area[n];
366  area += h_area[n];
367  }
368  ParallelDescriptor::ReduceLongSum(nd.data(), 3);
369  ParallelDescriptor::ReduceLongSum(bn.data(), m_nbld + 1);
370  ParallelDescriptor::ReduceRealSum(ba.data(), m_nbld + 1);
371  ParallelDescriptor::ReduceRealSum(area);
372  for (int d = 0; d < 3; ++d) { m_nface_dir[d] = nd[d]; }
373  m_bld_nface = bn;
374  m_bld_area = ba;
375  m_area_total = area;
376 
377  // Device arrays. The state starts uniform at T_skin_init (the slab too;
378  // the interior boundary is applied when the slab is advanced); the view
379  // fractions and fluxes start at zero and are filled by
380  // compute_view_fractions() and the per-step routines.
381  const size_t nf = static_cast<size_t>(m_nface);
382  upload(d_i, h_i); upload(d_j, h_j); upload(d_k, h_k);
383  upload(d_dir, h_dir); upload(d_side, h_side); upload(d_bid, h_bid);
384  upload(d_slot, h_slot);
385  upload(d_area, h_area);
386  upload(d_xf, h_xf); upload(d_yf, h_yf); upload(d_zf, h_zf);
387  fill(d_mat, nf, 0);
390  fill(d_kth, nf, m_params.k_therm);
391  fill(d_rhocp, nf, m_params.rho_cp);
394  fill(d_T_slab, nf * static_cast<size_t>(n_layers()), m_params.T_skin_init);
395  // View fractions: placeholders (a roof sees the whole sky, a wall half sky
396  // and half ground) that compute_view_fractions() replaces at initialisation.
397  {
398  std::vector<Real> fs(nf), fg(nf), fb(nf, 0.0);
399  for (size_t n = 0; n < nf; ++n) {
400  const bool roof = (h_dir[n] == 2);
401  fs[n] = roof ? 1.0 : 0.5;
402  fg[n] = roof ? 0.0 : 0.5;
403  }
404  upload(d_f_sky, fs); upload(d_f_ground, fg); upload(d_f_bldg, fb);
405  }
406  fill(d_shadow, nf, Real(0.0));
407  fill(d_SW_direct_in, nf, Real(0.0));
408  fill(d_SW_diffuse_in, nf, Real(0.0));
409  fill(d_LW_down_in, nf, Real(0.0));
412  fill(d_rho, nf, Real(1.0));
413  fill(d_U_tan, nf, Real(0.0));
414  fill(d_ustar, nf, Real(0.0));
415  fill(d_SW_abs, nf, Real(0.0));
416  fill(d_LW_net, nf, Real(0.0));
417  fill(d_H, nf, Real(0.0));
418  fill(d_LE, nf, Real(0.0));
419  fill(d_G, nf, Real(0.0));
421  fill(d_H_coeff, nf, Real(0.0));
422  fill(d_w_star, nf, Real(0.0));
423  fill(d_olen, nf, Real(1.0e30));
424  fill(d_z_i, nf, Real(0.0));
425  fill(d_LW_ext, nf, Real(0.0));
426  fill(d_resid, nf, Real(0.0));
427  fill(d_niter, nf, 0);
428  Gpu::streamSynchronize();
429 
431 }
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:359
amrex::Gpu::DeviceVector< amrex::Real > d_theta_air
Potential temperature of the fluid cell [K].
Definition: ERF_IBFaceSet.H:347
std::vector< int > m_bld_jhi
Definition: ERF_IBFaceSet.H:425
amrex::Gpu::DeviceVector< amrex::Real > d_hbld
Height of the face's building column [m].
Definition: ERF_IBFaceSet.H:353
int m_nslots
Slots per cell of the checkpoint field (largest face count on a cell)
Definition: ERF_IBFaceSet.H:414
int m_ny
Columns of the domain.
Definition: ERF_IBFaceSet.H:428
amrex::Gpu::DeviceVector< amrex::Real > d_shadow
1 when the ray to the sun hits a building, else 0
Definition: ERF_IBFaceSet.H:340
amrex::Gpu::DeviceVector< amrex::Real > d_yf
Definition: ERF_IBFaceSet.H:329
std::vector< int > m_xfer_src
Definition: ERF_IBFaceSet.H:378
amrex::Gpu::DeviceVector< int > d_col_top
Definition: ERF_IBFaceSet.H:368
amrex::Real m_max_path
Ray path cap [m].
Definition: ERF_IBFaceSet.H:434
std::vector< amrex::Long > m_bld_ncol
Definition: ERF_IBFaceSet.H:424
amrex::Gpu::DeviceVector< amrex::Real > d_SW_abs
Definition: ERF_IBFaceSet.H:337
amrex::Real m_col_top_max
Tallest column [m].
Definition: ERF_IBFaceSet.H:432
amrex::DistributionMapping m_xfer_dm
Definition: ERF_IBFaceSet.H:377
void print_debug_summary() const
Definition: ERF_IBFaceSet.cpp:1011
amrex::Gpu::DeviceVector< amrex::Real > d_LW_ext
Incoming longwave from sky and ground only [W/m2].
Definition: ERF_IBFaceSet.H:358
int m_col_i0
Definition: ERF_IBFaceSet.H:369
bool m_per_x
Definition: ERF_IBFaceSet.H:431
amrex::Long m_nface_dir[3]
Faces per direction.
Definition: ERF_IBFaceSet.H:421
std::vector< int > m_bld_jlo
Definition: ERF_IBFaceSet.H:425
amrex::Gpu::DeviceVector< amrex::Real > d_w_star
Convective velocity scale of the step [m/s].
Definition: ERF_IBFaceSet.H:354
amrex::Gpu::DeviceVector< amrex::Real > d_rho
Density of the fluid cell [kg/m3].
Definition: ERF_IBFaceSet.H:348
amrex::Gpu::DeviceVector< amrex::Real > d_T_skin
Skin temperature [K].
Definition: ERF_IBFaceSet.H:331
amrex::Gpu::DeviceVector< amrex::Real > d_LW_net
Definition: ERF_IBFaceSet.H:337
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:356
amrex::BoxArray m_xfer_ba
Definition: ERF_IBFaceSet.H:376
amrex::Gpu::DeviceVector< int > d_side
Solid neighbour at -1 (low) or +1 (high) side.
Definition: ERF_IBFaceSet.H:322
std::vector< amrex::Real > m_bld_area
Face area per building [m2].
Definition: ERF_IBFaceSet.H:420
amrex::Gpu::DeviceVector< int > d_niter
Newton iterations of the last solve.
Definition: ERF_IBFaceSet.H:360
amrex::Gpu::DeviceVector< amrex::Real > d_SW_diffuse_in
Sky and ground-reflected diffuse incident.
Definition: ERF_IBFaceSet.H:342
amrex::Gpu::DeviceVector< amrex::Real > d_f_sky
Definition: ERF_IBFaceSet.H:334
amrex::Gpu::DeviceVector< amrex::Real > d_olen
Obukhov length used [m] (1e30 when neutral)
Definition: ERF_IBFaceSet.H:355
amrex::Real m_z_ground
Ground height [m] (flat, the domain bottom)
Definition: ERF_IBFaceSet.H:433
amrex::Gpu::DeviceVector< int > d_dir
Face direction: 0 x, 1 y, 2 z.
Definition: ERF_IBFaceSet.H:321
amrex::Gpu::DeviceVector< amrex::Real > d_f_bldg
View fractions, sum to 1.
Definition: ERF_IBFaceSet.H:334
std::vector< int > m_bld_ilo
Definition: ERF_IBFaceSet.H:425
amrex::Gpu::DeviceVector< int > d_slot
Slot of the face within its cell's checkpoint record.
Definition: ERF_IBFaceSet.H:323
amrex::Gpu::DeviceVector< amrex::Real > d_T_slab
Slab layers [K], face-major: [face * n_layers + layer].
Definition: ERF_IBFaceSet.H:332
int m_col_ny
Definition: ERF_IBFaceSet.H:369
int n_layers() const
Definition: ERF_IBFaceSet.H:100
amrex::Real m_y_lo
Domain origin [m].
Definition: ERF_IBFaceSet.H:429
amrex::Gpu::DeviceVector< amrex::Real > d_SW_direct_in
Direct beam incident on the face.
Definition: ERF_IBFaceSet.H:341
amrex::BoxArray m_state_ba
Definition: ERF_IBFaceSet.H:375
amrex::Long m_domain_cells
Cells of the level, for the debug summary of the field.
Definition: ERF_IBFaceSet.H:379
amrex::Gpu::DeviceVector< amrex::Real > d_G
Definition: ERF_IBFaceSet.H:337
amrex::Gpu::DeviceVector< amrex::Real > d_T_air
Air temperature of the fluid cell.
Definition: ERF_IBFaceSet.H:345
amrex::Gpu::DeviceVector< amrex::Real > d_Q_ext
External incident flux [W/m2], e.g. fire radiation.
Definition: ERF_IBFaceSet.H:338
amrex::Real m_area_total
Total face area [m2].
Definition: ERF_IBFaceSet.H:422
bool m_per_y
Periodicity in x and y.
Definition: ERF_IBFaceSet.H:431
amrex::Gpu::DeviceVector< amrex::Real > d_f_ground
Definition: ERF_IBFaceSet.H:334
int m_nx
Definition: ERF_IBFaceSet.H:428
amrex::Gpu::DeviceVector< amrex::Real > d_LE
Definition: ERF_IBFaceSet.H:337
amrex::Gpu::DeviceVector< amrex::Real > d_H_coeff
H = H_coeff (theta_skin - theta_air) [W/m2/K].
Definition: ERF_IBFaceSet.H:351
int m_nbld
Buildings of the level (global)
Definition: ERF_IBFaceSet.H:415
amrex::Real m_dx[3]
Cell sizes [m].
Definition: ERF_IBFaceSet.H:430
amrex::Gpu::DeviceVector< amrex::Real > d_xf
Definition: ERF_IBFaceSet.H:329
amrex::Gpu::DeviceVector< amrex::Real > d_zf
Face centre [m].
Definition: ERF_IBFaceSet.H:329
std::vector< amrex::Long > m_bld_nface
Faces per building.
Definition: ERF_IBFaceSet.H:419
amrex::Gpu::DeviceVector< amrex::Real > d_LW_down_in
Incoming longwave on the face (sky, ground and walls)
Definition: ERF_IBFaceSet.H:344
int m_col_j0
Definition: ERF_IBFaceSet.H:369
std::vector< int > m_bld_ihi
Definition: ERF_IBFaceSet.H:425
amrex::Real m_x_lo
Definition: ERF_IBFaceSet.H:429
int m_col_nx
Definition: ERF_IBFaceSet.H:369
amrex::Gpu::DeviceVector< amrex::Real > d_ustar
Friction velocity of the wall function [m/s].
Definition: ERF_IBFaceSet.H:350
amrex::Gpu::DeviceVector< amrex::Real > d_U_tan
Tangential wind speed at the fluid cell [m/s].
Definition: ERF_IBFaceSet.H:349
@ 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
real(c_double), private ci
Definition: ERF_module_mp_morr_two_moment.F90:203
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:119
amrex::Real k_therm
Uniform conductivity [W/m/K].
Definition: ERF_IBSEBParams.H:101
amrex::Real rho_cp
Uniform volumetric heat capacity [J/m3/K].
Definition: ERF_IBSEBParams.H:102
amrex::Real thickness
Uniform slab thickness [m].
Definition: ERF_IBSEBParams.H:103
amrex::Real albedo
Face shortwave albedo.
Definition: ERF_IBSEBParams.H:59
amrex::Real T_skin_init
Initial skin temperature [K].
Definition: ERF_IBSEBParams.H:28
amrex::Real emissivity
Face longwave emissivity.
Definition: ERF_IBSEBParams.H:61
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.

581 {
582  const int nl = n_layers();
583  const Real Tint = m_params.T_interior;
584  const Real* pT = d_T_skin.data();
585  const Real* pkt = d_kth.data(); const Real* prc = d_rhocp.data(); const Real* pth = d_thick.data();
586  Real* pS = d_T_slab.data(); Real* pG = d_G.data();
587  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
589  for (int l = 0; l < nl; ++l) { T[l] = pS[f * nl + l]; }
590  const Real dz = pth[f] / nl;
591  pG[f] = ibseb::advance_slab_dirichlet(T, pT[f], Tint, pkt[f], prc[f], dz, dt, nl);
592  for (int l = 0; l < nl; ++l) { pS[f * nl + l] = T[l]; }
593  });
594  Gpu::streamSynchronize();
595 }
@ T
Definition: ERF_IndexDefines.H:128
@ 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)

Longwave of the current step. Incoming on a face: f_sky LW_sky + f_ground eps_g sigma T_g^4 + 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), the building term 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.

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).

492 {
493  const Real sigma = ibseb::SIGMA_SB;
494  const bool gray = (m_params.lw_mode == "gray");
495  const Real lw_fix = m_params.lw_down, eps_sky = m_params.sky_emissivity;
496  const Real eps_g = m_params.emissivity_ground;
497  const Real* peps = d_emis.data();
498  const Real Tg4 = std::pow(m_params.T_ground, 4);
499  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
500  const Real* pfs = d_f_sky.data(); const Real* pfg = d_f_ground.data(); const Real* pfb = d_f_bldg.data();
501  const Real* pT = d_T_skin.data();
502  Real* pTa = d_T_air.data(); Real* pin = d_LW_down_in.data(); Real* pnet = d_LW_net.data();
503  Real* pext = d_LW_ext.data();
504  for (MFIter mfi(cons); mfi.isValid(); ++mfi) {
505  const int f0 = m_fab_start[mfi.LocalIndex()];
506  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
507  auto const& c = cons.const_array(mfi);
508  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
509  const int f = f0 + m;
510  const Real rho = c(pi[f], pj[f], pk[f], Rho_comp);
511  const Real Ta = getTgivenRandRTh(rho, c(pi[f], pj[f], pk[f], RhoTheta_comp));
512  const Real lw_sky = gray ? eps_sky * sigma * Ta * Ta * Ta * Ta : lw_fix;
513  const Real Ts4 = pT[f] * pT[f] * pT[f] * pT[f];
514  // Sky and ground do not depend on the face; the wall term does
515  // (isothermal surroundings), so the balance keeps them apart.
516  const Real lw_ext = pfs[f] * lw_sky + pfg[f] * eps_g * sigma * Tg4;
517  const Real lw_in = lw_ext + pfb[f] * sigma * Ts4;
518  pTa[f] = Ta;
519  pext[f] = lw_ext;
520  pin[f] = lw_in;
521  pnet[f] = peps[f] * (lw_in - sigma * Ts4);
522  });
523  }
524  Gpu::streamSynchronize();
525 }
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:39
amrex::Real sigma
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:11
@ 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:75
amrex::Real sky_emissivity
gray: effective clear-sky emissivity
Definition: ERF_IBSEBParams.H:74
std::string lw_mode
Definition: ERF_IBSEBParams.H:72
amrex::Real emissivity_ground
Ground emissivity for the ground term.
Definition: ERF_IBSEBParams.H:62
amrex::Real lw_down
fixed: sky longwave [W/m2]
Definition: ERF_IBSEBParams.H:73
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.

690 {
691  const Real z0 = m_params.z0_wall, z0h = m_params.z0h_wall;
692  const bool stab = m_params.stability_correction;
693  const bool conv = (m_params.convective_velocity == "deardorff");
694  const bool seed = stab && (olen_ground != nullptr) && (m_params.obukhov_seed == "ground");
695  const bool use_pblh = conv && (pblh_ground != nullptr) && (m_params.z_i_mode == "pblh");
696  const Real beta = m_params.beta_conv;
697  const Real relax = m_params.obukhov_relax;
698  const Real zi_fallback = (z_i_bulk > 0.0) ? z_i_bulk : m_params.z_i;
699  const Real dxa[3] = {m_dx[0], m_dx[1], m_dx[2]};
700  const Real z_lo = m_z_ground;
701  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
702  const int* pd = d_dir.data();
703  const Real* pT = d_T_skin.data(); const Real* phb = d_hbld.data(); const Real* pzf = d_zf.data();
704  Real* pth = d_theta_air.data(); Real* prho = d_rho.data(); Real* pU = d_U_tan.data();
705  Real* pus = d_ustar.data(); Real* pH = d_H.data(); Real* pTa = d_T_air.data();
706  Real* pHc = d_H_coeff.data(); Real* pws = d_w_star.data(); Real* pol = d_olen.data(); Real* pzi = d_z_i.data();
707  for (MFIter mfi(cons); mfi.isValid(); ++mfi) {
708  const int f0 = m_fab_start[mfi.LocalIndex()];
709  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
710  auto const& c = cons.const_array(mfi);
711  auto const& u = xvel.const_array(mfi);
712  auto const& v = yvel.const_array(mfi);
713  auto const& w = zvel.const_array(mfi);
714  // The surface layer's 2D fields share the level's distribution with
715  // the boxes collapsed to k = 0, so the same iterator indexes them.
716  Array4<const Real> ol = seed ? olen_ground->const_array(mfi) : Array4<const Real>{};
717  Array4<const Real> pb = use_pblh ? pblh_ground->const_array(mfi) : Array4<const Real>{};
718  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
719  const int f = f0 + m;
720  const int i = pi[f], j = pj[f], k = pk[f], d = pd[f];
721  const Real rho = c(i, j, k, Rho_comp);
722  const Real rth = c(i, j, k, RhoTheta_comp);
723  const Real th = rth / rho;
724  const Real Ta = getTgivenRandRTh(rho, rth);
725  const Real th_skin = pT[f] * th / Ta; // skin temperature as a potential temperature
726  // Cell-centred velocity and its tangential part with respect to the wall.
727  Real U[3] = { Real(0.5) * (u(i, j, k) + u(i + 1, j, k)),
728  Real(0.5) * (v(i, j, k) + v(i, j + 1, k)),
729  Real(0.5) * (w(i, j, k) + w(i, j, k + 1)) };
730  U[d] = 0.0;
731  const Real Ut = amrex::max(std::sqrt(U[0] * U[0] + U[1] * U[1] + U[2] * U[2]), Real(1.0e-3));
732  // Convective velocity scale from the previous step's flux out of the face.
733  Real wstar = 0.0, depth = 0.0;
734  if (conv && pH[f] > 0.0) {
735  if (d == 2) {
736  Real zi = use_pblh ? pb(i, j, 0) : zi_fallback;
737  if (!(zi > 0.0 && zi < 1.0e5)) { zi = zi_fallback; }
738  depth = amrex::max(zi - (pzf[f] - z_lo), phb[f]);
739  } else {
740  depth = phb[f];
741  }
742  wstar = std::cbrt(CONST_GRAV / th * pH[f] / (rho * c_p) * depth);
743  }
744  const Real Ut_eff = std::sqrt(Ut * Ut + beta * beta * wstar * wstar);
745  const Real delta = 0.5 * dxa[d];
746  const Real lnm = std::log(delta / z0), lnh = std::log(delta / z0h);
747  Real ustar = KAPPA * Ut_eff / lnm;
748  Real thstar = KAPPA * (th - th_skin) / lnh;
749  Real lnh_eff = lnh;
750  Real olen = 1.0e30;
751  if (stab && d == 2) {
752  // Roofs: the surface layer's similarity functions on the
753  // face's own Obukhov length, a few fixed-point passes, seeded
754  // from the ground's field at this column when available.
755  similarity_funs sf;
756  Real L = 1.0e30;
757  if (seed) {
758  const Real Lg = ol(i, j, 0);
759  if (std::abs(Lg) > 0.0 && std::abs(Lg) < 1.0e5) { L = Lg; }
760  }
761  if (!(std::abs(L) < 1.0e5)) {
762  L = ustar * ustar * th / (KAPPA * CONST_GRAV * thstar + 1.0e-20);
763  }
764  // Fixed point on L until u*, theta* and L agree to 1e-6, so
765  // the stored values are mutually consistent (a fixed number
766  // of passes leaves L one pass behind). u* is under-relaxed
767  // between passes, as the surface layer's iteration is in
768  // erf-model #3486, which keeps a strongly unstable roof from
769  // oscillating at low wind; the converged values do not
770  // depend on the factor.
771  Real us_it = ustar;
772  for (int it = 0; it < 50; ++it) {
773  const Real zeta = delta / L;
774  const Real psi_m = sf.calc_psi_m(zeta), psi_h = sf.calc_psi_h(zeta);
775  const Real us_new = KAPPA * Ut_eff / amrex::max(lnm - psi_m, Real(0.1));
776  us_it = (it == 0) ? us_new : (1.0 - relax) * us_it + relax * us_new;
777  lnh_eff = amrex::max(lnh - psi_h, Real(0.1));
778  thstar = KAPPA * (th - th_skin) / lnh_eff;
779  const Real L_new = us_it * us_it * th / (KAPPA * CONST_GRAV * thstar + 1.0e-20);
780  const bool done = std::abs(L_new - L) <= 1.0e-6 * std::abs(L_new) &&
781  std::abs(us_new - us_it) <= 1.0e-6 * us_new;
782  L = L_new;
783  if (done) { us_it = us_new; break; }
784  }
785  ustar = us_it;
786  olen = L;
787  }
788  pth[f] = th;
789  pTa[f] = Ta;
790  prho[f] = rho;
791  pU[f] = Ut;
792  pus[f] = ustar;
793  pws[f] = wstar;
794  pol[f] = olen;
795  pzi[f] = depth;
796  pHc[f] = rho * c_p * KAPPA * ustar / lnh_eff; // H = H_coeff (theta_skin - theta_air)
797  pH[f] = -rho * c_p * ustar * thstar; // positive out of the face
798  });
799  }
800  Gpu::streamSynchronize();
801 }
constexpr amrex::Real KAPPA
Definition: ERF_Constants.H:55
constexpr amrex::Real CONST_GRAV
Definition: ERF_Constants.H:56
Real z0
Definition: ERF_InitCustomPertVels_ScalarAdvDiff.H:8
amrex::Real beta
Definition: ERF_InitCustomPert_DataAssimilation_ISV.H:10
Real w
Definition: ERF_Plotfile2DInterpolator.cpp:22
@ U
Definition: ERF_IndexDefines.H:126
@ xvel
Definition: ERF_IndexDefines.H:215
@ zvel
Definition: ERF_IndexDefines.H:217
@ yvel
Definition: ERF_IndexDefines.H:216
@ zi
Definition: ERF_AdvanceWDM6.cpp:276
std::string convective_velocity
"none" or "deardorff"
Definition: ERF_IBSEBParams.H:135
amrex::Real obukhov_relax
Under-relaxation of the roof's u*, theta* iteration (1 = none), as erf-model #3486.
Definition: ERF_IBSEBParams.H:140
amrex::Real z0_wall
Momentum roughness of the faces [m].
Definition: ERF_IBSEBParams.H:85
std::string z_i_mode
"bulk_ri", "pblh" or "fixed"
Definition: ERF_IBSEBParams.H:137
amrex::Real beta_conv
Gustiness factor on w*.
Definition: ERF_IBSEBParams.H:136
amrex::Real z_i
Fixed mixed-layer depth [m] (z_i_mode = fixed, and the fallback)
Definition: ERF_IBSEBParams.H:138
amrex::Real z0h_wall
Heat roughness of the faces [m].
Definition: ERF_IBSEBParams.H:86
bool stability_correction
Definition: ERF_IBSEBParams.H:87
std::string obukhov_seed
"ground" or "neutral"
Definition: ERF_IBSEBParams.H:134
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)

Shortwave of the current step: sun position and irradiances from the prescribed provider, 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.

Parameters
timeSimulation time [s]; with sun_mode = solar it is added to time_zero_utc_s.

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; 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.

851 {
852  // ---- Sun and irradiances (host scalars) ----
853  SunState s;
854  if (m_params.sun_mode == "fixed") {
855  s.zenith = m_params.sun_zenith_deg * PI / 180.0;
856  s.azimuth = m_params.sun_azimuth_deg * PI / 180.0;
857  const Real cz = std::cos(s.zenith);
858  s.dni = (cz > 0.0) ? m_params.sw_direct_normal : 0.0;
859  s.diffuse_h = (cz > 0.0) ? m_params.sw_diffuse : 0.0;
860  } else {
861  const Real t_utc = m_params.time_zero_utc_s + time;
863  // Hour angle from UTC and the longitude (a time-zone offset would cancel).
865  s.zenith = ibseb::solar_zenith(m_params.latitude_deg, decl, ha);
866  s.azimuth = ibseb::solar_azimuth(m_params.latitude_deg, decl, ha, s.zenith);
867  const Real cz = std::cos(s.zenith);
872  }
873  ibseb::sun_vector(s.zenith, s.azimuth, s.sx, s.sy, s.sz);
874  m_sun = s;
875 
876  // ---- Per face ----
877  const Real sx = s.sx, sy = s.sy, sz = s.sz;
878  const Real dni = s.dni, dif_h = s.diffuse_h;
879  const Real dir_h = dni * amrex::max(Real(0.0), sz);
880  const Real alb_g = m_params.albedo_ground;
881  const Real* palb = d_albedo.data();
882  const int* col_top = d_col_top.data();
883  const int nx = m_nx, ny = m_ny;
884  const int ci0 = m_col_i0, cj0 = m_col_j0, cbw = m_col_nx, cbh = m_col_ny;
885  const Real x_lo = m_x_lo, y_lo = m_y_lo, dx = m_dx[0], dy = m_dx[1], dz = m_dx[2];
886  const bool per_x = m_per_x, per_y = m_per_y;
887  const Real z_ground = m_z_ground, z_max = m_col_top_max, max_path = m_max_path;
888  const int* pd = d_dir.data(); const int* ps = d_side.data();
889  const Real* pxf = d_xf.data(); const Real* pyf = d_yf.data(); const Real* pzf = d_zf.data();
890  const Real* pfs = d_f_sky.data(); const Real* pfg = d_f_ground.data();
891  Real* psh = d_shadow.data(); Real* pdir = d_SW_direct_in.data();
892  Real* pdif = d_SW_diffuse_in.data(); Real* pabs = d_SW_abs.data();
893  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
894  // Outward normal: opposite to the side the solid is on.
895  Real n[3] = {0.0, 0.0, 0.0};
896  n[pd[f]] = -static_cast<Real>(ps[f]);
897  const Real cosi = n[0] * sx + n[1] * sy + n[2] * sz;
898  Real shadow = 0.0, direct = 0.0;
899  if (sz > 0.0 && cosi > 0.0 && dni > 0.0) {
900  shadow = ibseb::ray_blocked(pxf[f], pyf[f], pzf[f], sx, sy, sz, col_top, nx, ny, ci0, cj0, cbw, cbh,
901  x_lo, y_lo, dx, dy, dz, per_x, per_y, z_ground, z_max, max_path) ? 1.0 : 0.0;
902  direct = dni * cosi * (1.0 - shadow);
903  }
904  const Real diffuse = pfs[f] * dif_h + pfg[f] * alb_g * (dir_h + dif_h);
905  psh[f] = shadow;
906  pdir[f] = direct;
907  pdif[f] = diffuse;
908  pabs[f] = (1.0 - palb[f]) * (direct + diffuse);
909  });
910  Gpu::streamSynchronize();
911 
912  if (m_params.debug) {
913  Print() << "[IBSEB DEBUG] lev=" << m_lev << " sun: zenith=" << s.zenith * 180.0 / PI
914  << " deg azimuth=" << s.azimuth * 180.0 / PI << " deg s=(" << s.sx << "," << s.sy << "," << s.sz
915  << ") DNI=" << s.dni << " W/m2 diffuse_h=" << s.diffuse_h << " W/m2\n";
916  }
917 }
const Real dy
Definition: ERF_InitCustomPert_ABL.H:45
constexpr amrex::Real PI
Definition: ERF_NumericalConstants.H:39
SunState m_sun
Definition: ERF_IBFaceSet.H:435
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:70
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:133
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real earth_sun_distance_factor(amrex::Real day_of_year)
Definition: ERF_IBSEBSolar.H:109
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:118
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:194
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:85
AMREX_GPU_HOST_DEVICE AMREX_FORCE_INLINE amrex::Real solar_declination(amrex::Real day_of_year)
Definition: ERF_IBSEBSolar.H:35
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:60
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:146
amrex::Real sx
Definition: ERF_IBFaceSet.H:384
std::string sun_mode
Definition: ERF_IBSEBParams.H:46
amrex::Real day_of_year
solar: day of year
Definition: ERF_IBSEBParams.H:53
amrex::Real sw_transmission
solar: bulk clear-sky transmission (Bird)
Definition: ERF_IBSEBParams.H:56
amrex::Real longitude_deg
solar: site longitude, east positive
Definition: ERF_IBSEBParams.H:52
amrex::Real albedo_ground
Ground albedo for the reflected diffuse term.
Definition: ERF_IBSEBParams.H:60
amrex::Real solar_constant
solar: [W/m2]
Definition: ERF_IBSEBParams.H:55
amrex::Real sw_direct_normal
fixed: direct-normal irradiance [W/m2]
Definition: ERF_IBSEBParams.H:49
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:54
amrex::Real sun_zenith_deg
fixed: zenith angle [deg]
Definition: ERF_IBSEBParams.H:47
amrex::Real sw_diffuse
fixed: diffuse irradiance on a horizontal surface [W/m2]
Definition: ERF_IBSEBParams.H:50
amrex::Real sun_azimuth_deg
fixed: azimuth clockwise from north [deg]; 180 = sun in the south
Definition: ERF_IBSEBParams.H:48
amrex::Real latitude_deg
solar: site latitude, north positive
Definition: ERF_IBSEBParams.H:51
amrex::Real sw_diffuse_coeff
solar: share of the attenuated beam that arrives as diffuse light
Definition: ERF_IBSEBParams.H:57
Here is the call 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.

442 {
443  const int n_az = m_params.view_n_az, n_el = m_params.view_n_el;
444  const int* col_top = d_col_top.data();
445  const int nx = m_nx, ny = m_ny;
446  const int ci0 = m_col_i0, cj0 = m_col_j0, cbw = m_col_nx, cbh = m_col_ny;
447  const Real x_lo = m_x_lo, y_lo = m_y_lo, dx = m_dx[0], dy = m_dx[1], dz = m_dx[2];
448  const bool per_x = m_per_x, per_y = m_per_y;
449  const Real z_ground = m_z_ground, z_max = m_col_top_max, max_path = m_max_path;
450  const int* pd = d_dir.data(); const int* ps = d_side.data();
451  const Real* pxf = d_xf.data(); const Real* pyf = d_yf.data(); const Real* pzf = d_zf.data();
452  Real* pfs = d_f_sky.data(); Real* pfg = d_f_ground.data(); Real* pfb = d_f_bldg.data();
453  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
454  int n_sky = 0, n_gnd = 0, n_bld = 0;
455  for (int ie = 0; ie < n_el; ++ie) {
456  for (int ia = 0; ia < n_az; ++ia) {
457  Real sx, sy, sz;
458  ibseb::hemisphere_direction(pd[f], -ps[f], ia, ie, n_az, n_el, sx, sy, sz);
459  const int hit = ibseb::ray_hit(pxf[f], pyf[f], pzf[f], sx, sy, sz, col_top, nx, ny, ci0, cj0, cbw, cbh,
460  x_lo, y_lo, dx, dy, dz, per_x, per_y, z_ground, z_max, max_path);
461  if (hit == ibseb::RAY_SKY) ++n_sky; else if (hit == ibseb::RAY_GROUND) ++n_gnd; else ++n_bld;
462  }
463  }
464  const Real inv = 1.0 / static_cast<Real>(n_az * n_el);
465  pfs[f] = n_sky * inv; pfg[f] = n_gnd * inv; pfb[f] = n_bld * inv;
466  });
467  Gpu::streamSynchronize();
468 
469  if (m_params.debug) {
470  std::vector<Real> fs(m_nface), fg(m_nface), fb(m_nface);
471  Gpu::copy(Gpu::deviceToHost, d_f_sky.begin(), d_f_sky.end(), fs.begin());
472  Gpu::copy(Gpu::deviceToHost, d_f_ground.begin(), d_f_ground.end(), fg.begin());
473  Gpu::copy(Gpu::deviceToHost, d_f_bldg.begin(), d_f_bldg.end(), fb.begin());
474  Gpu::streamSynchronize();
475  Real s[3] = {0.0, 0.0, 0.0};
476  for (int n = 0; n < m_nface; ++n) { s[0] += fs[n]; s[1] += fg[n]; s[2] += fb[n]; }
477  ParallelDescriptor::ReduceRealSum(s, 3);
478  const Real nf_all = static_cast<Real>(m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2]);
479  Print() << "[IBSEB DEBUG] lev=" << m_lev << " view fractions: " << n_az * n_el
480  << " rays per face, mean f_sky=" << s[0] / nf_all << " f_ground=" << s[1] / nf_all
481  << " f_bldg=" << s[2] / nf_all << "\n";
482  }
483 }
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:330
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:261
@ RAY_GROUND
Definition: ERF_IBSEBSolar.H:242
@ RAY_SKY
Definition: ERF_IBSEBSolar.H:242
int view_n_az
Definition: ERF_IBSEBParams.H:67
int view_n_el
Definition: ERF_IBSEBParams.H:68
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.

954 {
955  const int nf = m_nface;
956  auto host = [&](const Gpu::DeviceVector<Real>& d) {
957  std::vector<Real> h(nf);
958  Gpu::copy(Gpu::deviceToHost, d.begin(), d.end(), h.begin());
959  return h;
960  };
961  auto hosti = [&](const Gpu::DeviceVector<int>& d) {
962  std::vector<int> h(nf);
963  Gpu::copy(Gpu::deviceToHost, d.begin(), d.end(), h.begin());
964  return h;
965  };
966  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);
967  const auto xf = host(d_xf), yf = host(d_yf), zf = host(d_zf), area = host(d_area);
968  const auto fs = host(d_f_sky), fg = host(d_f_ground), fb = host(d_f_bldg);
969  const auto sh = host(d_shadow), sdir = host(d_SW_direct_in), sdif = host(d_SW_diffuse_in), sabs = host(d_SW_abs);
970  const auto T = host(d_T_skin), Ta = host(d_T_air), lwin = host(d_LW_down_in), lwn = host(d_LW_net);
971  const auto th = host(d_theta_air), rho = host(d_rho), Ut = host(d_U_tan), us = host(d_ustar), H = host(d_H);
972  const auto mat = hosti(d_mat);
973  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);
974  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);
975  const auto nit = hosti(d_niter);
976  const auto ws = host(d_w_star), ol = host(d_olen), zi = host(d_z_i), hb = host(d_hbld);
977  const int nl = n_layers();
978  std::vector<Real> slab(static_cast<size_t>(nf) * nl);
979  Gpu::copy(Gpu::deviceToHost, d_T_slab.begin(), d_T_slab.end(), slab.begin());
980  Gpu::streamSynchronize();
981  std::ofstream f(prefix + ".rank" + std::to_string(ParallelDescriptor::MyProc()) + ".csv");
982  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,"
983  "theta_air,rho,U_tan,ustar,H,mat,albedo,emissivity,k_therm,rho_cp,thickness,G,T_slab_top,T_slab_bottom,"
984  "H_coeff,Q_ext,LE,LW_ext,resid,n_iter,w_star,Olen,z_i,h_bld";
985  for (int l = 0; l < nl; ++l) { f << ",T_slab_" << l; }
986  f << "\n";
987  f << std::setprecision(12);
988  for (int n = 0; n < nf; ++n) {
989  f << i[n] << "," << j[n] << "," << k[n] << "," << dir[n] << "," << side[n] << "," << bid[n] << ","
990  << xf[n] << "," << yf[n] << "," << zf[n] << "," << area[n] << ","
991  << fs[n] << "," << fg[n] << "," << fb[n] << "," << sh[n] << ","
992  << sdir[n] << "," << sdif[n] << "," << sabs[n] << "," << T[n] << ","
993  << Ta[n] << "," << lwin[n] << "," << lwn[n] << ","
994  << th[n] << "," << rho[n] << "," << Ut[n] << "," << us[n] << "," << H[n] << ","
995  << mat[n] << "," << alb[n] << "," << emi[n] << "," << kk[n] << "," << rc[n] << "," << thk[n] << ","
996  << G[n] << "," << slab[static_cast<size_t>(n) * nl] << "," << slab[static_cast<size_t>(n) * nl + nl - 1] << ","
997  << Hc[n] << "," << Qx[n] << "," << LE[n] << "," << lwe[n] << "," << res[n] << "," << nit[n]
998  << "," << ws[n] << "," << ol[n] << "," << zi[n] << "," << hb[n];
999  for (int l = 0; l < nl; ++l) { f << "," << slab[static_cast<size_t>(n) * nl + l]; }
1000  f << "\n";
1001  }
1002 }
Real H
Definition: ERF_InitCustomPert_MovingTerrain.H:7

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.

408 { return m_fab_start; }

◆ has_state()

bool IBFaceSet::has_state ( ) const
inline

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

136 { 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.

1126 {
1127  AMREX_ALWAYS_ASSERT(state.nComp() == state_ncomp() && state.boxArray() == m_state_ba);
1128  const int nl = n_layers();
1129  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1130  const int* pslot = d_slot.data();
1131  Real* pT = d_T_skin.data(); Real* pS = d_T_slab.data(); Real* pH = d_H.data();
1132  MultiFab mine(m_xfer_ba, m_xfer_dm, state_ncomp(), 0);
1133  mine.ParallelCopy(state);
1134  for (MFIter mfi(mine); mfi.isValid(); ++mfi) {
1135  const int li = m_xfer_src[mfi.LocalIndex()];
1136  const int f0 = m_fab_start[li];
1137  const int f1 = m_fab_start[li + 1];
1138  const Box bx = mfi.validbox();
1139  auto const& st = mine.const_array(mfi);
1140  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1141  const int f = f0 + m;
1142  if (!bx.contains(IntVect(pi[f], pj[f], pk[f]))) { return; }
1143  const int c0 = pslot[f] * (2 + nl);
1144  pT[f] = st(pi[f], pj[f], pk[f], c0);
1145  pH[f] = st(pi[f], pj[f], pk[f], c0 + 1);
1146  for (int l = 0; l < nl; ++l) { pS[f * nl + l] = st(pi[f], pj[f], pk[f], c0 + 2 + l); }
1147  });
1148  }
1149  Gpu::streamSynchronize();
1150  if (m_params.debug) {
1151  std::vector<Real> h_T(m_nface);
1152  Gpu::copy(Gpu::deviceToHost, d_T_skin.begin(), d_T_skin.end(), h_T.begin());
1153  Gpu::streamSynchronize();
1154  Real tmin = 1.0e30, tmax = -1.0e30;
1155  for (int n = 0; n < m_nface; ++n) { tmin = std::min(tmin, h_T[n]); tmax = std::max(tmax, h_T[n]); }
1156  ParallelDescriptor::ReduceRealMin(tmin);
1157  ParallelDescriptor::ReduceRealMax(tmax);
1158  Print() << "[IBSEB DEBUG] lev=" << m_lev << " face state loaded: T_skin in ["
1159  << tmin << ", " << tmax << "] K\n";
1160  }
1161 }
int state_ncomp() const
Definition: ERF_IBFaceSet.H:119
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.

1079 {
1080  if (m_state_ba.empty()) { return MultiFab(); }
1081  return MultiFab(m_state_ba, DistributionMapping(m_state_ba), state_ncomp(), 0);
1082 }
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.

98 { return m_nbld; }

◆ n_faces()

int IBFaceSet::n_faces ( ) const
inline

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

96 { return m_nface; }

◆ n_layers()

int IBFaceSet::n_layers ( ) const
inline

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

100 { 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.

122 { 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.

1012 {
1013  const int nl = n_layers();
1014  Print() << "[IBSEB DEBUG] lev=" << m_lev << " inputs: n_slab_layers=" << nl
1015  << " T_skin_init=" << m_params.T_skin_init << " K"
1016  << " T_interior=" << m_params.T_interior << " K"
1017  << " csv_file=" << m_params.csv_file << " csv_int=" << m_params.csv_int << "\n";
1018  Print() << "[IBSEB DEBUG] lev=" << m_lev << " global faces=" << (m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2])
1019  << " (x=" << m_nface_dir[0] << " y=" << m_nface_dir[1] << " z=" << m_nface_dir[2] << ")"
1020  << " area=" << m_area_total << " m2 buildings=" << m_nbld << "\n";
1021  for (int b = 1; b <= m_nbld; ++b) {
1022  Print() << "[IBSEB DEBUG] building " << b << ": columns=" << m_bld_ncol[b]
1023  << " i=[" << m_bld_ilo[b] << "," << m_bld_ihi[b] << "]"
1024  << " j=[" << m_bld_jlo[b] << "," << m_bld_jhi[b] << "]"
1025  << " faces=" << m_bld_nface[b] << " area=" << m_bld_area[b] << " m2\n";
1026  }
1027  // Per-rank ownership: one line per rank with its fab ranges.
1028  const int nfab = static_cast<int>(m_fab_start.size()) - 1;
1029  const size_t bytes = static_cast<size_t>(m_nface) *
1030  (7 * sizeof(int) + (11 + static_cast<size_t>(nl)) * sizeof(Real));
1031  std::ostringstream os;
1032  os << "[IBSEB DEBUG] lev=" << m_lev << " rank " << ParallelDescriptor::MyProc()
1033  << ": faces=" << m_nface << " over " << nfab << " fab(s), device memory "
1034  << bytes / 1024.0 << " kB; fab ranges:";
1035  for (int f = 0; f < nfab; ++f) { os << " [" << m_fab_start[f] << "," << m_fab_start[f + 1] << ")"; }
1036  AllPrint() << os.str() << "\n";
1037 }
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:

◆ 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 of the step (zenith, azimuth, direct-normal and diffuse irradiance). 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.

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().

1171 {
1172  // Skin temperature: min, max and per-building mean over all ranks.
1173  std::vector<Real> h_T(m_nface), h_A(m_nface);
1174  std::vector<int> h_b(m_nface);
1175  Gpu::copy(Gpu::deviceToHost, d_T_skin.begin(), d_T_skin.end(), h_T.begin());
1176  Gpu::copy(Gpu::deviceToHost, d_area.begin(), d_area.end(), h_A.begin());
1177  Gpu::copy(Gpu::deviceToHost, d_bid.begin(), d_bid.end(), h_b.begin());
1178  Gpu::streamSynchronize();
1179 
1180  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);
1181  Gpu::copy(Gpu::deviceToHost, d_w_star.begin(), d_w_star.end(), h_w.begin());
1182  Gpu::copy(Gpu::deviceToHost, d_resid.begin(), d_resid.end(), h_r.begin());
1183  Gpu::copy(Gpu::deviceToHost, d_Q_ext.begin(), d_Q_ext.end(), h_Q.begin());
1184  Gpu::copy(Gpu::deviceToHost, d_SW_abs.begin(), d_SW_abs.end(), h_S.begin());
1185  Gpu::copy(Gpu::deviceToHost, d_shadow.begin(), d_shadow.end(), h_sh.begin());
1186  Gpu::copy(Gpu::deviceToHost, d_LW_net.begin(), d_LW_net.end(), h_L.begin());
1187  Gpu::copy(Gpu::deviceToHost, d_H.begin(), d_H.end(), h_H.begin());
1188  Gpu::copy(Gpu::deviceToHost, d_G.begin(), d_G.end(), h_G.begin());
1189  Gpu::streamSynchronize();
1190 
1191  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);
1192  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);
1193  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;
1194  Real res_max = 0.0, Q_sum = 0.0, w_max = 0.0;
1195  for (int n = 0; n < m_nface; ++n) {
1196  w_max = std::max(w_max, h_w[n]);
1197  G_sum += h_G[n] * h_A[n];
1198  Q_sum += h_Q[n] * h_A[n];
1199  res_max = std::max(res_max, h_r[n]);
1200  bG[h_b[n]] += h_G[n] * h_A[n];
1201  bQ[h_b[n]] += h_Q[n] * h_A[n];
1202  btmin[h_b[n]] = std::min(btmin[h_b[n]], h_T[n]);
1203  btmax[h_b[n]] = std::max(btmax[h_b[n]], h_T[n]);
1204  bres[h_b[n]] = std::max(bres[h_b[n]], h_r[n]);
1205  bsum[h_b[n]] += h_T[n] * h_A[n];
1206  bsw[h_b[n]] += h_S[n] * h_A[n];
1207  bsh[h_b[n]] += h_sh[n] * h_A[n];
1208  blw[h_b[n]] += h_L[n] * h_A[n];
1209  bH[h_b[n]] += h_H[n] * h_A[n];
1210  tmin = std::min(tmin, h_T[n]);
1211  tmax = std::max(tmax, h_T[n]);
1212  sw_sum += h_S[n] * h_A[n];
1213  sw_max = std::max(sw_max, h_S[n]);
1214  sh_area += h_sh[n] * h_A[n];
1215  lw_sum += h_L[n] * h_A[n];
1216  H_sum += h_H[n] * h_A[n];
1217  }
1218  ParallelDescriptor::ReduceRealSum(bsum.data(), m_nbld + 1);
1219  ParallelDescriptor::ReduceRealSum(bsw.data(), m_nbld + 1);
1220  ParallelDescriptor::ReduceRealSum(bsh.data(), m_nbld + 1);
1221  ParallelDescriptor::ReduceRealSum(blw.data(), m_nbld + 1);
1222  ParallelDescriptor::ReduceRealSum(bH.data(), m_nbld + 1);
1223  ParallelDescriptor::ReduceRealSum(bG.data(), m_nbld + 1);
1224  ParallelDescriptor::ReduceRealSum(bQ.data(), m_nbld + 1);
1225  ParallelDescriptor::ReduceRealMin(btmin.data(), m_nbld + 1);
1226  ParallelDescriptor::ReduceRealMax(btmax.data(), m_nbld + 1);
1227  ParallelDescriptor::ReduceRealMax(bres.data(), m_nbld + 1);
1228  ParallelDescriptor::ReduceRealMax(res_max);
1229  ParallelDescriptor::ReduceRealMax(w_max);
1230  ParallelDescriptor::ReduceRealSum(Q_sum);
1231  ParallelDescriptor::ReduceRealMin(tmin);
1232  ParallelDescriptor::ReduceRealMax(tmax);
1233  ParallelDescriptor::ReduceRealSum(sw_sum);
1234  ParallelDescriptor::ReduceRealMax(sw_max);
1235  ParallelDescriptor::ReduceRealSum(sh_area);
1236  ParallelDescriptor::ReduceRealSum(lw_sum);
1237  ParallelDescriptor::ReduceRealSum(H_sum);
1238  ParallelDescriptor::ReduceRealSum(G_sum);
1239  const Real G_mean = (m_area_total > 0.0) ? G_sum / m_area_total : 0.0;
1240  const Real sw_mean = (m_area_total > 0.0) ? sw_sum / m_area_total : 0.0;
1241  const Real sh_frac = (m_area_total > 0.0) ? sh_area / m_area_total : 0.0;
1242  const Real lw_mean = (m_area_total > 0.0) ? lw_sum / m_area_total : 0.0;
1243  const Real H_mean = (m_area_total > 0.0) ? H_sum / m_area_total : 0.0;
1244 
1245  const Long nf_all = m_nface_dir[0] + m_nface_dir[1] + m_nface_dir[2];
1246  Print() << "[IBSEB] lev=" << m_lev << " step=" << step << " t=" << time
1247  << " faces=" << nf_all
1248  << " (x=" << m_nface_dir[0] << " y=" << m_nface_dir[1] << " z=" << m_nface_dir[2] << ")"
1249  << " buildings=" << m_nbld
1250  << " area=" << std::setprecision(10) << m_area_total << " m2"
1251  << " T_skin_min=" << ((nf_all > 0) ? tmin : Real(0.0))
1252  << " T_skin_max=" << ((nf_all > 0) ? tmax : Real(0.0))
1253  << " SW_abs_mean=" << sw_mean << " SW_abs_max=" << sw_max
1254  << " shadow_frac=" << sh_frac << " LW_net_mean=" << lw_mean
1255  << " H_mean=" << H_mean << " G_mean=" << G_mean
1256  << " Q_ext_mean=" << ((m_area_total > 0.0) ? Q_sum / m_area_total : Real(0.0))
1257  << " resid_max=" << res_max
1258  << " w_star_max=" << w_max
1259  << " H_total_W=" << std::setprecision(12) << H_sum << "\n";
1260  // Cost line: the slowest rank's per-step time of the balance and its face
1261  // count, so the cost of the face list on a larger case can be estimated.
1262  {
1263  Real ms = (m_cost_n > 0) ? 1000.0 * m_cost_s / m_cost_n : 0.0;
1264  Real ms_max = ms; int nf_max = m_nface;
1265  ParallelDescriptor::ReduceRealMax(ms_max);
1266  ParallelDescriptor::ReduceIntMax(nf_max);
1267  Print() << "[IBSEB] cost: lev=" << m_lev << " advance_ms_per_step_max=" << std::setprecision(4) << ms_max
1268  << " faces_per_rank_max=" << nf_max << " ranks=" << ParallelDescriptor::NProcs()
1269  << " init_s=" << m_init_cost_s << "\n";
1270  }
1271 
1272  if (m_params.debug) {
1273  for (int b = 1; b <= m_nbld; ++b) {
1274  const Real tmean = (m_bld_area[b] > 0.0) ? bsum[b] / m_bld_area[b] : Real(0.0);
1275  const Real swm = (m_bld_area[b] > 0.0) ? bsw[b] / m_bld_area[b] : Real(0.0);
1276  const Real shf = (m_bld_area[b] > 0.0) ? bsh[b] / m_bld_area[b] : Real(0.0);
1277  const Real lwm = (m_bld_area[b] > 0.0) ? blw[b] / m_bld_area[b] : Real(0.0);
1278  const Real Hm = (m_bld_area[b] > 0.0) ? bH[b] / m_bld_area[b] : Real(0.0);
1279  const Real Gm = (m_bld_area[b] > 0.0) ? bG[b] / m_bld_area[b] : Real(0.0);
1280  Print() << "[IBSEB DEBUG] building " << b << ": faces=" << m_bld_nface[b]
1281  << " area=" << m_bld_area[b] << " m2 T_skin_mean=" << tmean << " K"
1282  << " [" << btmin[b] << ", " << btmax[b] << "]"
1283  << " SW_abs_mean=" << swm << " W/m2 shadow_frac=" << shf
1284  << " LW_net_mean=" << lwm << " W/m2 H_mean=" << Hm << " W/m2 G_mean=" << Gm
1285  << " W/m2 resid_max=" << bres[b] << " W/m2\n";
1286  }
1287  }
1288 
1289  if (write_csv && !m_params.dump_faces_file.empty()) {
1290  std::string prefix = m_params.dump_faces_file;
1292  std::ostringstream os;
1293  os << prefix << ".step" << std::setw(6) << std::setfill('0') << step;
1294  prefix = os.str();
1295  }
1296  dump_faces(prefix);
1297  }
1298 
1299  if (!write_csv || !ParallelDescriptor::IOProcessor()) { return; }
1300  bool need_header = true;
1301  {
1302  std::ifstream probe(m_params.csv_file, std::ios::ate);
1303  if (probe.good() && probe.tellg() > 0) { need_header = false; }
1304  }
1305  std::ofstream csv(m_params.csv_file, std::ios::app);
1306  if (need_header) {
1307  csv << "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,"
1308  "G_mean_Wm2,Q_ext_mean_Wm2,T_skin_min_K,T_skin_max_K,resid_max_Wm2,"
1309  "sun_zenith_deg,sun_azimuth_deg,dni_Wm2,diffuse_h_Wm2\n";
1310  }
1311  for (int b = 1; b <= m_nbld; ++b) {
1312  const Real tmean = (m_bld_area[b] > 0.0) ? bsum[b] / m_bld_area[b] : Real(0.0);
1313  const Real swm = (m_bld_area[b] > 0.0) ? bsw[b] / m_bld_area[b] : Real(0.0);
1314  const Real shf = (m_bld_area[b] > 0.0) ? bsh[b] / m_bld_area[b] : Real(0.0);
1315  const Real lwm = (m_bld_area[b] > 0.0) ? blw[b] / m_bld_area[b] : Real(0.0);
1316  const Real Hm = (m_bld_area[b] > 0.0) ? bH[b] / m_bld_area[b] : Real(0.0);
1317  const Real Gm = (m_bld_area[b] > 0.0) ? bG[b] / m_bld_area[b] : Real(0.0);
1318  const Real Qm = (m_bld_area[b] > 0.0) ? bQ[b] / m_bld_area[b] : Real(0.0);
1319  csv << std::setprecision(10) << time << "," << step << "," << m_lev << "," << b << ","
1320  << m_bld_nface[b] << "," << m_bld_area[b] << "," << tmean << "," << swm << "," << shf << "," << lwm << "," << Hm << ","
1321  << Gm << "," << Qm << "," << btmin[b] << "," << btmax[b] << "," << bres[b] << ","
1322  << m_sun.zenith * 180.0 / PI << "," << m_sun.azimuth * 180.0 / PI << "," << m_sun.dni << "," << m_sun.diffuse_h << "\n";
1323  }
1324 }
Vector< Real > h_r(khi+2)
void dump_faces(const std::string &prefix) const
Definition: ERF_IBFaceSet.cpp:953
double m_init_cost_s
Wall-clock seconds of build() and compute_view_fractions()
Definition: ERF_IBFaceSet.H:438
amrex::Real diffuse_h
Diffuse on a horizontal surface [W/m2].
Definition: ERF_IBFaceSet.H:386
amrex::Real dni
Direct-normal irradiance [W/m2].
Definition: ERF_IBFaceSet.H:385
amrex::Real zenith
Definition: ERF_IBFaceSet.H:383
amrex::Real azimuth
[rad]
Definition: ERF_IBFaceSet.H:383
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)
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.

1086 {
1087  AMREX_ALWAYS_ASSERT(state.nComp() == state_ncomp() && state.boxArray() == m_state_ba);
1088  const int nl = n_layers();
1089  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1090  const int* pslot = d_slot.data();
1091  const Real* pT = d_T_skin.data(); const Real* pS = d_T_slab.data(); const Real* pH = d_H.data();
1092  MultiFab mine(m_xfer_ba, m_xfer_dm, state_ncomp(), 0);
1093  mine.setVal(0.0);
1094  for (MFIter mfi(mine); mfi.isValid(); ++mfi) {
1095  const int li = m_xfer_src[mfi.LocalIndex()];
1096  const int f0 = m_fab_start[li];
1097  const int f1 = m_fab_start[li + 1];
1098  const Box bx = mfi.validbox();
1099  auto const& st = mine.array(mfi);
1100  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1101  const int f = f0 + m;
1102  if (!bx.contains(IntVect(pi[f], pj[f], pk[f]))) { return; }
1103  const int c0 = pslot[f] * (2 + nl);
1104  st(pi[f], pj[f], pk[f], c0) = pT[f];
1105  st(pi[f], pj[f], pk[f], c0 + 1) = pH[f];
1106  for (int l = 0; l < nl; ++l) { st(pi[f], pj[f], pk[f], c0 + 2 + l) = pS[f * nl + l]; }
1107  });
1108  }
1109  Gpu::streamSynchronize();
1110  state.setVal(0.0);
1111  state.ParallelCopy(mine);
1112  if (m_params.debug) {
1113  Print() << "[IBSEB DEBUG] lev=" << m_lev << " face state saved: " << state_ncomp()
1114  << " components (" << m_nslots << " slots x " << (2 + n_layers()) << ") on " << m_state_ba.size()
1115  << " boxes, " << m_state_ba.numPts() << " of the level's " << m_domain_cells << " cells\n";
1116  }
1117 }
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.

1047 {
1048  nfaces.setVal(0.0);
1049  tskin.setVal(0.0);
1050  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
1051  const Real* pT = d_T_skin.data();
1052  for (MFIter mfi(nfaces); mfi.isValid(); ++mfi) {
1053  const int f0 = m_fab_start[mfi.LocalIndex()];
1054  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
1055  auto const& n = nfaces.array(mfi);
1056  auto const& t = tskin.array(mfi);
1057  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
1058  const int f = f0 + m;
1059  Gpu::Atomic::AddNoRet(&n(pi[f], pj[f], pk[f]), Real(1.0));
1060  Gpu::Atomic::AddNoRet(&t(pi[f], pj[f], pk[f]), pT[f]);
1061  });
1062  const Box& bx = mfi.validbox();
1063  ParallelFor(bx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
1064  if (n(i, j, k) > 0.0) { t(i, j, k) /= n(i, j, k); }
1065  });
1066  }
1067  Gpu::streamSynchronize();
1068 }
@ t
Definition: ERF_WSM6.H:272
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.

925 {
926  MultiFab cnt(out.boxArray(), out.DistributionMap(), 1, 0);
927  cnt.setVal(0.0);
928  out.setVal(0.0);
929  const int* pi = d_i.data(); const int* pj = d_j.data(); const int* pk = d_k.data();
930  const Real* pv = v.data();
931  for (MFIter mfi(out); mfi.isValid(); ++mfi) {
932  const int f0 = m_fab_start[mfi.LocalIndex()];
933  const int f1 = m_fab_start[mfi.LocalIndex() + 1];
934  auto const& n = cnt.array(mfi);
935  auto const& o = out.array(mfi);
936  ParallelFor(f1 - f0, [=] AMREX_GPU_DEVICE (int m) noexcept {
937  const int f = f0 + m;
938  Gpu::Atomic::AddNoRet(&n(pi[f], pj[f], pk[f]), Real(1.0));
939  Gpu::Atomic::AddNoRet(&o(pi[f], pj[f], pk[f]), pv[f]);
940  });
941  ParallelFor(mfi.validbox(), [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept {
942  if (n(i, j, k) > 0.0) { o(i, j, k) /= n(i, j, k); }
943  });
944  }
945  Gpu::streamSynchronize();
946 }
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).

401 { m_init_cost_s = seconds; }

◆ 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.

607 {
608  const int nl = n_layers();
609  const Real Tint = m_params.T_interior;
610  const Real Tmin = m_params.T_skin_min, Tmax = m_params.T_skin_max;
611  const Real tol = m_params.newton_tol_K, max_step = m_params.newton_max_step_K;
612  const int max_iter = m_params.newton_max_iter;
613  const Real* pkt = d_kth.data(); const Real* prc = d_rhocp.data(); const Real* pth = d_thick.data();
614  const Real* peps = d_emis.data(); const Real* pfb = d_f_bldg.data();
615  const Real* psw = d_SW_abs.data(); const Real* pq = d_Q_ext.data(); const Real* pext = d_LW_ext.data();
616  const Real* pHc = d_H_coeff.data(); const Real* pTa = d_T_air.data(); const Real* ptha = d_theta_air.data();
617  const Real* pLE = d_LE.data();
618  Real* pT = d_T_skin.data(); Real* pS = d_T_slab.data(); Real* pG = d_G.data();
619  Real* pH = d_H.data(); Real* pin = d_LW_down_in.data(); Real* pnet = d_LW_net.data();
620  Real* pres = d_resid.data(); int* pnit = d_niter.data();
621  ParallelFor(m_nface, [=] AMREX_GPU_DEVICE (int f) noexcept {
623  for (int l = 0; l < nl; ++l) { T[l] = pS[f * nl + l]; }
624  const Real dz = pth[f] / nl;
625  // Conduction the implicit slab step will take for a skin temperature Ts: a Ts - b.
626  Real a = 0.0, b = 0.0;
627  ibseb::slab_skin_response(T, Tint, pkt[f], prc[f], dz, dt, nl, a, b);
628  const Real exner = pTa[f] / ptha[f];
629  const Real q_abs = peps[f] * pq[f];
630  int n_it = 0; Real res = 0.0;
631  const Real Ts = ibseb::solve_skin_balance(pT[f], psw[f], q_abs, pext[f], peps[f], pfb[f],
632  pHc[f], exner, ptha[f], pLE[f], a, b,
633  Tmin, Tmax, max_step, tol, max_iter, n_it, res);
634  // Advance the slab with the solution and store every term at it.
635  pG[f] = ibseb::advance_slab_dirichlet(T, Ts, Tint, pkt[f], prc[f], dz, dt, nl);
636  for (int l = 0; l < nl; ++l) { pS[f * nl + l] = T[l]; }
637  const Real Ts4 = Ts * Ts * Ts * Ts;
638  pin[f] = pext[f] + pfb[f] * ibseb::SIGMA_SB * Ts4;
639  pnet[f] = peps[f] * (pin[f] - ibseb::SIGMA_SB * Ts4);
640  pH[f] = pHc[f] * (Ts / exner - ptha[f]);
641  pT[f] = Ts;
642  // The residual with the stored terms (the slab's own G, not a Ts - b),
643  // so it reports the closure of what is written out.
644  pres[f] = std::abs(psw[f] + q_abs + pnet[f] - pH[f] - pLE[f] - pG[f]);
645  pnit[f] = n_it;
646  });
647  Gpu::streamSynchronize();
648 
649  if (m_params.debug) {
650  std::vector<Real> h_r(m_nface), h_T(m_nface);
651  std::vector<int> h_n(m_nface);
652  Gpu::copy(Gpu::deviceToHost, d_resid.begin(), d_resid.end(), h_r.begin());
653  Gpu::copy(Gpu::deviceToHost, d_T_skin.begin(), d_T_skin.end(), h_T.begin());
654  Gpu::copy(Gpu::deviceToHost, d_niter.begin(), d_niter.end(), h_n.begin());
655  Gpu::streamSynchronize();
656  Real rmax = 0.0; int nmax = 0; Long nclamp = 0;
657  for (int n = 0; n < m_nface; ++n) {
658  rmax = std::max(rmax, h_r[n]); nmax = std::max(nmax, h_n[n]);
659  if (h_T[n] <= Tmin || h_T[n] >= Tmax) { ++nclamp; }
660  }
661  ParallelDescriptor::ReduceRealMax(rmax);
662  ParallelDescriptor::ReduceIntMax(nmax);
663  ParallelDescriptor::ReduceLongSum(nclamp);
664  Print() << "[IBSEB DEBUG] lev=" << m_lev << " balance: dt=" << dt << " s newton iterations max=" << nmax
665  << " residual max=" << rmax << " W/m2 faces at a bound=" << nclamp << "\n";
666  }
667 }
@ 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:118
amrex::Real newton_tol_K
Newton stops when a step changes T_skin by less than this [K].
Definition: ERF_IBSEBParams.H:117
amrex::Real T_skin_min
Lower bound of the skin temperature [K].
Definition: ERF_IBSEBParams.H:114
amrex::Real T_skin_max
Upper bound [K]; raise it for faces under a fire.
Definition: ERF_IBSEBParams.H:115
int newton_max_iter
Newton iteration cap per face and step.
Definition: ERF_IBSEBParams.H:116
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.

134 { 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.

119 { 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
388 { return m_sun; }

Member Data Documentation

◆ d_albedo

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

Referenced by compute_shortwave(), and dump_faces().

◆ 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

Referenced by compute_shortwave().

◆ d_dir

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

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

Referenced by compute_shortwave(), 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

Referenced by compute_shortwave(), and dump_faces().

◆ d_f_sky

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

Referenced by compute_shortwave(), and dump_faces().

◆ 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

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

◆ d_j

amrex::Gpu::DeviceVector<int> IBFaceSet::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(), 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(), 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(), 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(), 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

Referenced by compute_shortwave(), and dump_faces().

◆ d_yf

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

Referenced by compute_shortwave(), and dump_faces().

◆ 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(), 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

Referenced by compute_shortwave().

◆ m_col_j0

int IBFaceSet::m_col_j0 = 0

Referenced by compute_shortwave().

◆ m_col_nx

int IBFaceSet::m_col_nx = 0

Referenced by compute_shortwave().

◆ m_col_ny

int IBFaceSet::m_col_ny = 0

Referenced by compute_shortwave().

◆ m_col_top_max

amrex::Real IBFaceSet::m_col_top_max = 0.0
private

Tallest column [m].

Referenced by compute_shortwave().

◆ 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_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

Cell sizes [m].

Referenced by compute_shortwave().

◆ m_fab_start

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

◆ 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_lev

int IBFaceSet::m_lev = 0
private

AMR level, for reports.

Referenced by compute_shortwave(), load_state(), print_debug_summary(), report(), and save_state().

◆ m_max_path

amrex::Real IBFaceSet::m_max_path = 0.0
private

Ray path cap [m].

Referenced by compute_shortwave().

◆ m_nbld

int IBFaceSet::m_nbld = 0
private

Buildings of the level (global)

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

◆ m_nface

int IBFaceSet::m_nface = 0
private

Faces owned by this rank.

Referenced by compute_shortwave(), dump_faces(), load_state(), n_faces(), print_debug_summary(), and report().

◆ m_nface_dir

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

Faces per direction.

Referenced by 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

Referenced by compute_shortwave().

◆ m_ny

int IBFaceSet::m_ny = 0
private

Columns of the domain.

Referenced by compute_shortwave().

◆ m_params

IBSEBParams IBFaceSet::m_params
private

◆ m_per_x

bool IBFaceSet::m_per_x = false
private

Referenced by compute_shortwave().

◆ m_per_y

bool IBFaceSet::m_per_y = false
private

Periodicity in x and y.

Referenced by compute_shortwave().

◆ m_state_ba

amrex::BoxArray IBFaceSet::m_state_ba

◆ m_sun

SunState IBFaceSet::m_sun
private

Referenced by compute_shortwave(), report(), and sun().

◆ m_x_lo

amrex::Real IBFaceSet::m_x_lo = 0.0
private

Referenced by compute_shortwave().

◆ 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().

◆ m_z_ground

amrex::Real IBFaceSet::m_z_ground = 0.0
private

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

Referenced by compute_shortwave().


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