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

#include <ERF_MOSTAverage.H>

Collaboration diagram for MOSTAverage:

Public Member Functions

 MOSTAverage (amrex::Vector< amrex::Geometry > geom, const bool &has_zphys, std::string a_pp_prefix, const MeshType &m_mesh_type, const TerrainType &m_terrain_type, const amrex::Vector< const eb_ * > &eb_vec={})
 
 ~MOSTAverage ()
 
 MOSTAverage (MOSTAverage &&) noexcept=default
 
MOSTAverageoperator= (MOSTAverage &&other) noexcept=delete
 
 MOSTAverage (const MOSTAverage &other)=delete
 
MOSTAverageoperator= (const MOSTAverage &other)=delete
 
void make_MOSTAverage_at_level (const int &lev, const amrex::Vector< amrex::MultiFab * > &vars_old, std::unique_ptr< amrex::MultiFab > &Theta_prim, std::unique_ptr< amrex::MultiFab > &Qv_prim, std::unique_ptr< amrex::MultiFab > &Qr_prim, std::unique_ptr< amrex::MultiFab > &z_phys_nd)
 
void update_field_ptrs (const int &lev, amrex::Vector< amrex::Vector< amrex::MultiFab >> &vars_old, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Theta_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qv_prim, amrex::Vector< std::unique_ptr< amrex::MultiFab >> &Qr_prim)
 
void set_rotated_fields (const int &lev)
 
void set_plane_normalization (const int &lev)
 
void set_eb_normalization (const int &lev)
 
void set_region_normalization (const int &)
 
void set_k_indices_N (const int &lev)
 
void set_k_indices_T (const int &lev)
 
void set_norm_indices_T (const int &lev)
 
void set_z_positions_T (const int &lev)
 
void set_z_positions_EB (const int &lev)
 
void set_norm_positions_T (const int &lev)
 
void compute_averages (const int &lev)
 
void compute_plane_averages (const int &lev)
 
void compute_region_averages (const int &lev)
 
void compute_eb_averages (const int &lev)
 
void write_k_indices (const int &lev)
 
void write_norm_indices (const int &lev)
 
void write_xz_positions (const int &lev, const int &j)
 
void write_averages (const int &lev)
 
const amrex::MultiFab * get_average (const int &lev, const int &comp) const
 
amrex::MultiFab * get_zref (const int &lev) const
 
const amrex::iMultiFab * get_k_indices (const int &lev) const
 

Static Public Member Functions

AMREX_GPU_HOST_DEVICE static AMREX_INLINE void trilinear_interp_T (const amrex::Real &xp, const amrex::Real &yp, const amrex::Real &zp, amrex::Real *interp_vals, amrex::Array4< amrex::Real const > const &interp_array, amrex::Array4< amrex::Real const > const &z_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &plo, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxi, const int interp_comp)
 

Protected Attributes

const amrex::Vector< amrex::Geometry > m_geom
 
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_fields
 
amrex::Vector< amrex::MultiFab * > m_z_phys_nd
 
std::string m_pp_prefix
 
MeshType m_mesh_type
 
TerrainType m_terrain_type
 
int m_nvar {6}
 
int m_navg {6}
 
int m_maxlev {0}
 
int m_policy {0}
 
bool m_rotate {false}
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_zref
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_x_pos
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_y_pos
 
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_z_pos
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > m_i_indx
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > m_j_indx
 
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > m_k_indx
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > m_averages
 
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > m_rot_fields
 
amrex::Vector< amrex::Vector< int > > m_ncell_plane
 
amrex::Vector< amrex::Vector< amrex::Real > > m_plane_average
 
int m_radius {0}
 
int m_ncell_region {1}
 
amrex::Vector< int > m_k_in
 
bool m_interp {false}
 
bool m_norm_vec {false}
 
amrex::Vector< const eb_ * > m_eb_vec
 
amrex::Vector< amrex::Vector< amrex::Real > > m_total_bndry_area
 
bool m_t_avg {false}
 
amrex::Vector< int > m_t_init
 
double m_time_window {1.0e-16}
 
amrex::Real m_fact_new
 
amrex::Real m_fact_old
 
bool include_subgrid_vel = false
 
amrex::Vector< amrex::Realm_Vsg
 
const amrex::Real zref_default = amrex::Real(10.0)
 

Constructor & Destructor Documentation

◆ MOSTAverage() [1/3]

MOSTAverage::MOSTAverage ( amrex::Vector< amrex::Geometry >  geom,
const bool &  has_zphys,
std::string  a_pp_prefix,
const MeshType &  m_mesh_type,
const TerrainType &  m_terrain_type,
const amrex::Vector< const eb_ * > &  eb_vec = {} 
)
explicit

Construct the MOST averaging helper.

Parameters
[in]geomgeometry for all AMR levels
[in]has_zphyswhether nodal physical-height data are available
[in]a_pp_prefixParmParse prefix
[in]m_mesh_typemesh type
[in]m_terrain_typeterrain representation
[in]eb_vecoptional embedded-boundary geometry data

◆ ~MOSTAverage()

MOSTAverage::~MOSTAverage ( )
inline

Destroy the MOST averaging helper.

40  {}

◆ MOSTAverage() [2/3]

MOSTAverage::MOSTAverage ( MOSTAverage &&  )
defaultnoexcept

Default move constructor.

◆ MOSTAverage() [3/3]

MOSTAverage::MOSTAverage ( const MOSTAverage other)
delete

Deleted copy constructor.

Parameters
[in]othersource object

Member Function Documentation

◆ compute_averages()

void MOSTAverage::compute_averages ( const int &  lev)

Driver for the selected average policy.

Parameters
[in]levlevel index

Function to call the type of average computation.

Parameters
[in]levCurrent level
926 {
927  if (m_rotate) set_rotated_fields(lev);
928 
929  switch(m_policy) {
930  case 0: // Standard plane average
932  break;
933  case 1: // Local region/point
935  break;
936  case 2: // EB Terrain average
937  compute_eb_averages(lev);
938  break;
939  default:
940  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(false, "Unknown policy for MOSTAverage!");
941  }
942 
943  // We have initialized the averages
944  if (m_t_avg) m_t_init[lev] = 1;
945 }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
bool m_t_avg
Definition: ERF_MOSTAverage.H:381
void compute_plane_averages(const int &lev)
Definition: ERF_MOSTAverage.cpp:954
int m_policy
Definition: ERF_MOSTAverage.H:346
void compute_region_averages(const int &lev)
Definition: ERF_MOSTAverage.cpp:1253
amrex::Vector< int > m_t_init
Definition: ERF_MOSTAverage.H:382
void set_rotated_fields(const int &lev)
Definition: ERF_MOSTAverage.cpp:313
bool m_rotate
Definition: ERF_MOSTAverage.H:347
void compute_eb_averages(const int &lev)
Definition: ERF_MOSTAverage.cpp:1630
Here is the call graph for this function:

◆ compute_eb_averages()

void MOSTAverage::compute_eb_averages ( const int &  lev)

Fill averages for the embedded-boundary policy.

Parameters
[in]levlevel index

Function to compute average over an EB surface.

Parameters
[in]levCurrent level
1631 {
1632  AMREX_ALWAYS_ASSERT(m_eb_vec[lev] != nullptr);
1633 
1634  // Peel back the level
1635  auto& fields = m_fields[lev];
1636  auto& averages = m_averages[lev];
1637  auto& plane_average = m_plane_average[lev];
1638 
1639  // Get EB data
1640  const auto& cc_flags = m_eb_vec[lev]->get_const_factory()->getMultiEBCellFlagFab();
1641  auto cc_afrac = m_eb_vec[lev]->get_const_factory()->getAreaFrac();
1642  const auto& cc_bnorm = m_eb_vec[lev]->get_const_factory()->getBndryNormal();
1643  const auto& u_vfrac = m_eb_vec[lev]->get_u_const_factory()->getVolFrac();
1644  const auto& v_vfrac = m_eb_vec[lev]->get_v_const_factory()->getVolFrac();
1645  const auto& w_vfrac = m_eb_vec[lev]->get_w_const_factory()->getVolFrac();
1646 
1647  // Get geometry for cell sizes
1648  auto const& dx_arr = m_geom[lev].CellSizeArray();
1649  Real dx = dx_arr[0];
1650  Real dy = dx_arr[1];
1651  Real dz = dx_arr[2];
1652 
1653  // Set factors for time averaging
1654  Real d_fact_new, d_fact_old;
1655  if (m_t_avg && m_t_init[lev]) {
1656  d_fact_new = m_fact_new;
1657  d_fact_old = m_fact_old;
1658  } else {
1659  d_fact_new = one;
1660  d_fact_old = zero;
1661  }
1662 
1663  // GPU array to accumulate averages into
1664  Gpu::DeviceVector<Real> pavg(plane_average.size(), zero);
1665  Real* plane_avg = pavg.data();
1666 
1667  // Vectors for normalization and buffer storage
1668  Vector<Real> denom(plane_average.size(),zero);
1669  Vector<Real> val_old(plane_average.size(),zero);
1670 
1671  //
1672  //----------------------------------------------------------
1673  // Averages for U, V, and tangential velocity (in local coordinate)
1674  //----------------------------------------------------------
1675  //
1676  {
1677  denom[0] = one / m_total_bndry_area[lev][0];
1678  val_old[0] = plane_average[0]*d_fact_old;
1679  denom[1] = one / m_total_bndry_area[lev][1];
1680  val_old[1] = plane_average[1]*d_fact_old;
1681 
1682  int iavg = m_navg - 1; // Tangential velocity magnitude
1683  denom[iavg] = one / m_total_bndry_area[lev][iavg];
1684  val_old[iavg] = plane_average[iavg]*d_fact_old;
1685 
1686  const Real Vsg = m_Vsg[lev]; // Subgrid scale velocity
1687 
1688 #ifdef _OPENMP
1689 #pragma omp parallel if (Gpu::notInLaunchRegion())
1690 #endif
1691  for (MFIter mfi(*fields[2], TileNoZ()); mfi.isValid(); ++mfi) {
1692  const auto& flag = cc_flags[mfi];
1693 
1694  // Skip boxes that are not singlevalued (MultiCutFab only has data for singlevalued boxes)
1695  if (flag.getType() != FabType::singlevalued) continue;
1696 
1697  Box bx = mfi.tilebox(); // Full 3D box
1698 
1699  // Get EB arrays
1700  auto const flag_arr = flag.const_array();
1701  auto const afrac_x = cc_afrac[0]->const_array(mfi);
1702  auto const afrac_y = cc_afrac[1]->const_array(mfi);
1703  auto const afrac_z = cc_afrac[2]->const_array(mfi);
1704  auto const bnorm_arr = cc_bnorm.const_array(mfi);
1705  auto const u_vf_arr = u_vfrac.const_array(mfi);
1706  auto const v_vf_arr = v_vfrac.const_array(mfi);
1707  auto const w_vf_arr = w_vfrac.const_array(mfi);
1708 
1709  // Get velocity arrays
1710  auto const u_arr = fields[0]->const_array(mfi);
1711  auto const v_arr = fields[1]->const_array(mfi);
1712  auto const w_arr = fields[5]->const_array(mfi);
1713 
1714  ParallelFor(Gpu::KernelInfo().setReduction(true), bx, [=]
1715  AMREX_GPU_DEVICE(int i, int j, int k, Gpu::Handler const& handler) noexcept
1716  {
1717  // Area-weighted averaging over cut cells at any k
1718  if (flag_arr(i,j,k).isSingleValued()) {
1719  // Compute area from face-centered area fractions
1720  Real axm = afrac_x(i ,j ,k );
1721  Real axp = afrac_x(i+1,j ,k );
1722  Real aym = afrac_y(i ,j ,k );
1723  Real ayp = afrac_y(i ,j+1,k );
1724  Real azm = afrac_z(i ,j ,k );
1725  Real azp = afrac_z(i ,j ,k+1);
1726 
1727  Real adx = (axm - axp) * dy * dz;
1728  Real ady = (aym - ayp) * dx * dz;
1729  Real adz = (azm - azp) * dx * dy;
1730 
1731  Real area = std::sqrt(adx*adx + ady*ady + adz*adz);
1732 
1733  // Volume-weighted interpolation of velocities to cell center
1734  Real vf_u_lo = u_vf_arr(i,j,k);
1735  Real vf_u_hi = u_vf_arr(i+1,j,k);
1736  Real sum_vf_u = vf_u_lo + vf_u_hi;
1737  Real u_cc = (sum_vf_u > zero) ? (u_arr(i,j,k)*vf_u_lo + u_arr(i+1,j,k)*vf_u_hi) / sum_vf_u : zero;
1738 
1739  Real vf_v_lo = v_vf_arr(i,j,k);
1740  Real vf_v_hi = v_vf_arr(i,j+1,k);
1741  Real sum_vf_v = vf_v_lo + vf_v_hi;
1742  Real v_cc = (sum_vf_v > zero) ? (v_arr(i,j,k)*vf_v_lo + v_arr(i,j+1,k)*vf_v_hi) / sum_vf_v : zero;
1743 
1744  Real vf_w_lo = w_vf_arr(i,j,k);
1745  Real vf_w_hi = w_vf_arr(i,j,k+1);
1746  Real sum_vf_w = vf_w_lo + vf_w_hi;
1747  Real w_cc = (sum_vf_w > zero) ? (w_arr(i,j,k)*vf_w_lo + w_arr(i,j,k+1)*vf_w_hi) / sum_vf_w : zero;
1748 
1749  // Get normal vector components
1750  Real nx = bnorm_arr(i,j,k,0);
1751  Real ny = bnorm_arr(i,j,k,1);
1752  Real nz = bnorm_arr(i,j,k,2);
1753 
1754  // Compute tangential velocity components
1755  Real v_dot_n = u_cc*nx + v_cc*ny + w_cc*nz;
1756  Real u_tangent = u_cc - v_dot_n * nx;
1757  Real v_tangent = v_cc - v_dot_n * ny;
1758  Real mag = std::sqrt(u_tangent*u_tangent + v_tangent*v_tangent + Vsg*Vsg);
1759 
1760  // Area-weighted sum
1761  Real val_u = u_tangent * area;
1762  Real val_v = v_tangent * area;
1763  Real val_mag = mag * area;
1764 
1765  Gpu::deviceReduceSum(&plane_avg[0], val_u, handler);
1766  Gpu::deviceReduceSum(&plane_avg[1], val_v, handler);
1767  Gpu::deviceReduceSum(&plane_avg[iavg], val_mag, handler);
1768  }
1769  });
1770  }
1771  }
1772 
1773  //
1774  //----------------------------------------------------------
1775  // Averages for T,Qv (cell-centered scalars)
1776  //----------------------------------------------------------
1777  //
1778  for (int imf(2); imf < 4; ++imf) {
1779 
1780  // Continue if no valid Qv pointer
1781  if (!fields[imf]) continue;
1782 
1783  denom[imf] = one / m_total_bndry_area[lev][imf];
1784  val_old[imf] = plane_average[imf]*d_fact_old;
1785 
1786 #ifdef _OPENMP
1787 #pragma omp parallel if (Gpu::notInLaunchRegion())
1788 #endif
1789  for (MFIter mfi(*fields[imf], TileNoZ()); mfi.isValid(); ++mfi) {
1790  const auto& flag = cc_flags[mfi];
1791 
1792  // Skip boxes that are not singlevalued (MultiCutFab only has data for singlevalued boxes)
1793  if (flag.getType() != FabType::singlevalued) continue;
1794 
1795  Box bx = mfi.tilebox(); // Full 3D box
1796  auto const flag_arr = flag.const_array();
1797  auto const afrac_x = cc_afrac[0]->const_array(mfi);
1798  auto const afrac_y = cc_afrac[1]->const_array(mfi);
1799  auto const afrac_z = cc_afrac[2]->const_array(mfi);
1800  auto const mf_arr = fields[imf]->const_array(mfi);
1801 
1802  ParallelFor(Gpu::KernelInfo().setReduction(true), bx, [=]
1803  AMREX_GPU_DEVICE(int i, int j, int k, Gpu::Handler const& handler) noexcept
1804  {
1805  // Area-weighted averaging over cut cells at any k
1806  if (flag_arr(i,j,k).isSingleValued()) {
1807  // Compute area from face-centered area fractions
1808  Real axm = afrac_x(i ,j ,k );
1809  Real axp = afrac_x(i+1,j ,k );
1810  Real aym = afrac_y(i ,j ,k );
1811  Real ayp = afrac_y(i ,j+1,k );
1812  Real azm = afrac_z(i ,j ,k );
1813  Real azp = afrac_z(i ,j ,k+1);
1814 
1815  Real adx = (axm - axp) * dy * dz;
1816  Real ady = (aym - ayp) * dx * dz;
1817  Real adz = (azm - azp) * dx * dy;
1818 
1819  Real area = std::sqrt(adx*adx + ady*ady + adz*adz);
1820 
1821  Real val = mf_arr(i,j,k) * area;
1822  Gpu::deviceReduceSum(&plane_avg[imf], val, handler);
1823  }
1824  });
1825  }
1826  }
1827 
1828  //
1829  //------------------------------------------------------------------------
1830  // Averages for virtual potential temperature
1831  //------------------------------------------------------------------------
1832  //
1833  if (fields[3]) // We have water vapor
1834  {
1835  int iavg = 4;
1836  denom[iavg] = one / m_total_bndry_area[lev][iavg];
1837  val_old[iavg] = plane_average[iavg]*d_fact_old;
1838 
1839 #ifdef _OPENMP
1840 #pragma omp parallel if (Gpu::notInLaunchRegion())
1841 #endif
1842  for (MFIter mfi(*fields[3], TileNoZ()); mfi.isValid(); ++mfi)
1843  {
1844  const auto& flag = cc_flags[mfi];
1845 
1846  // Skip boxes that are not singlevalued (MultiCutFab only has data for singlevalued boxes)
1847  if (flag.getType() != FabType::singlevalued) continue;
1848 
1849  Box bx = mfi.tilebox(); // Full 3D box
1850  auto const flag_arr = flag.const_array();
1851  auto const afrac_x = cc_afrac[0]->const_array(mfi);
1852  auto const afrac_y = cc_afrac[1]->const_array(mfi);
1853  auto const afrac_z = cc_afrac[2]->const_array(mfi);
1854 
1855  const Array4<Real const> T_mf_arr = fields[2]->const_array(mfi);
1856  const Array4<Real const> qv_mf_arr = fields[3]->const_array(mfi);
1857  const Array4<Real const> qr_mf_arr = (fields[4]) ? fields[4]->const_array(mfi) :
1858  Array4<const Real> {};
1859 
1860  ParallelFor(Gpu::KernelInfo().setReduction(true), bx, [=]
1861  AMREX_GPU_DEVICE(int i, int j, int k, Gpu::Handler const& handler) noexcept
1862  {
1863  if (flag_arr(i,j,k).isSingleValued()) {
1864  // Compute area from face-centered area fractions
1865  Real axm = afrac_x(i ,j ,k );
1866  Real axp = afrac_x(i+1,j ,k );
1867  Real aym = afrac_y(i ,j ,k );
1868  Real ayp = afrac_y(i ,j+1,k );
1869  Real azm = afrac_z(i ,j ,k );
1870  Real azp = afrac_z(i ,j ,k+1);
1871 
1872  Real adx = (axm - axp) * dy * dz;
1873  Real ady = (aym - ayp) * dx * dz;
1874  Real adz = (azm - azp) * dx * dy;
1875 
1876  Real area = std::sqrt(adx*adx + ady*ady + adz*adz);
1877 
1878  Real vfac;
1879  if (qr_mf_arr) {
1880  // We also have liquid water
1881  vfac = one + epsv*qv_mf_arr(i,j,k) - qr_mf_arr(i,j,k);
1882  } else {
1883  vfac = one + epsv*qv_mf_arr(i,j,k);
1884  }
1885  const Real val = T_mf_arr(i,j,k) * vfac * area;
1886  Gpu::deviceReduceSum(&plane_avg[iavg], val, handler);
1887  }
1888  });
1889  }
1890  }
1891  else // copy temperature
1892  {
1893  int iavg = m_navg - 2;
1894  denom[iavg] = one / m_total_bndry_area[lev][iavg];
1895  // plane_avg[iavg] = plane_avg[2]
1896  Gpu::copy(Gpu::deviceToDevice, pavg.begin() + 2, pavg.begin() + 3,
1897  pavg.begin() + iavg);
1898  }
1899 
1900  // Copy to host and sum across procs
1901  Gpu::copy(Gpu::deviceToHost, pavg.begin(), pavg.end(), plane_average.begin());
1902  ParallelDescriptor::ReduceRealSum(plane_average.data(), plane_average.size());
1903 
1904  // Normalize by total area and apply time averaging
1905  for (int iavg(0); iavg < m_navg; ++iavg){
1906  plane_average[iavg] *= denom[iavg]*d_fact_new;
1907  plane_average[iavg] += val_old[iavg];
1908  averages[iavg]->setVal(plane_average[iavg]);
1909  }
1910 }
constexpr amrex::Real epsv
Definition: ERF_Constants.H:53
constexpr amrex::Real one
Definition: ERF_Constants.H:9
constexpr amrex::Real zero
Definition: ERF_Constants.H:8
Real vfac
Definition: ERF_InitCustomPertVels_ABL.H:26
const Real dy
Definition: ERF_InitCustomPert_ABL.H:24
const Real dx
Definition: ERF_InitCustomPert_ABL.H:23
AMREX_ALWAYS_ASSERT(bx.length()[2]==khi+1)
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_FORCE_INLINE amrex::IntVect TileNoZ()
Definition: ERF_TileNoZ.H:11
int m_navg
Definition: ERF_MOSTAverage.H:344
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > m_averages
Definition: ERF_MOSTAverage.H:355
amrex::Vector< amrex::Vector< amrex::Real > > m_total_bndry_area
Definition: ERF_MOSTAverage.H:377
amrex::Vector< amrex::Real > m_Vsg
Definition: ERF_MOSTAverage.H:389
amrex::Vector< amrex::Vector< amrex::Real > > m_plane_average
Definition: ERF_MOSTAverage.H:361
amrex::Real m_fact_new
Definition: ERF_MOSTAverage.H:384
amrex::Vector< amrex::Vector< amrex::MultiFab * > > m_fields
Definition: ERF_MOSTAverage.H:335
amrex::Vector< const eb_ * > m_eb_vec
Definition: ERF_MOSTAverage.H:376
amrex::Real m_fact_old
Definition: ERF_MOSTAverage.H:384
const amrex::Vector< amrex::Geometry > m_geom
Definition: ERF_MOSTAverage.H:334
@ dz
Definition: ERF_AdvanceWSM6.cpp:104

Referenced by compute_averages().

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

◆ compute_plane_averages()

void MOSTAverage::compute_plane_averages ( const int &  lev)

Fill averages for the plane policy.

Parameters
[in]levlevel index

Function to compute average over a plane.

Parameters
[in]levCurrent level
955 {
956  // Peel back the level
957  auto& fields = m_fields[lev];
958  auto& rot_fields = m_rot_fields[lev];
959  auto& averages = m_averages[lev];
960  const auto & geom = m_geom[lev];
961 
962  auto& z_phys = m_z_phys_nd[lev];
963  auto& x_pos = m_x_pos[lev];
964  auto& y_pos = m_y_pos[lev];
965  auto& z_pos = m_z_pos[lev];
966 
967  auto& i_indx = m_i_indx[lev];
968  auto& j_indx = m_j_indx[lev];
969  auto& k_indx = m_k_indx[lev];
970 
971  auto& ncell_plane = m_ncell_plane[lev];
972  auto& plane_average = m_plane_average[lev];
973 
974  // Set factors for time averaging
975  Real d_fact_new, d_fact_old;
976  if (m_t_avg && m_t_init[lev]) {
977  d_fact_new = m_fact_new;
978  d_fact_old = m_fact_old;
979  } else {
980  d_fact_new = one;
981  d_fact_old = zero;
982  }
983 
984  int klo = m_geom[lev].Domain().smallEnd(2);
985 
986  // GPU array to accumulate averages into
987  Gpu::DeviceVector<Real> pavg(plane_average.size(), zero);
988  Real* plane_avg = pavg.data();
989 
990  // Vectors for normalization and buffer storage
991  Vector<Real> denom(plane_average.size(),zero);
992  Vector<Real> val_old(plane_average.size(),zero);
993 
994  //
995  //----------------------------------------------------------
996  // Averages over all the fields
997  //----------------------------------------------------------
998  //
999  Box domain = geom.Domain();
1000 
1001  Array<int,AMREX_SPACEDIM> is_per = {0,0,0};
1002  for (int idim(0); idim < AMREX_SPACEDIM-1; ++idim) {
1003  if (geom.isPeriodic(idim)) is_per[idim] = 1;
1004  }
1005 
1006  // Averages for U,V,T,Qv (not Qc or W)
1007  for (int imf(0); imf < 4; ++imf) {
1008 
1009  // Continue if no valid Qv pointer
1010  if (!fields[imf]) continue;
1011 
1012  denom[imf] = one / (Real)ncell_plane[imf];
1013  val_old[imf] = plane_average[imf]*d_fact_old;
1014 
1015 #ifdef _OPENMP
1016 #pragma omp parallel if (Gpu::notInLaunchRegion())
1017 #endif
1018  for (MFIter mfi(*fields[imf], TileNoZ()); mfi.isValid(); ++mfi) {
1019  Box vbx = mfi.validbox(); // This is the grid (not tile)
1020  Box pbx = mfi.tilebox(); // This is the tile (not grid)
1021 
1022  if (pbx.smallEnd(2) != klo) { continue; }
1023 
1024  // Make planar since mfiter is over fields
1025  pbx.makeSlab(2,klo);
1026 
1027  // Avoid double counting nodal data by changing the high end when we are
1028  // at the high side of the grid (not just of the tile)
1029  IndexType ixt = averages[imf]->boxArray().ixType();
1030  for (int idim(0); idim < AMREX_SPACEDIM-1; ++idim) {
1031  if ( ixt.nodeCentered(idim) && (pbx.bigEnd(idim) == vbx.bigEnd(idim)) ) {
1032  int dom_hi = domain.bigEnd(idim)+1;
1033  if (pbx.bigEnd(idim) < dom_hi || is_per[idim]) {
1034  pbx.growHi(idim,-1);
1035  }
1036  }
1037  }
1038 
1039  auto mf_arr = (m_rotate) ? rot_fields[imf]->const_array(mfi) :
1040  fields[imf]->const_array(mfi);
1041 
1042  if (m_interp) {
1043  const auto plo = geom.ProbLoArray();
1044  const auto dxInv = geom.InvCellSizeArray();
1045  const auto z_phys_arr = z_phys->const_array(mfi);
1046  auto x_pos_arr = x_pos->array(mfi);
1047  auto y_pos_arr = y_pos->array(mfi);
1048  auto z_pos_arr = z_pos->array(mfi);
1049  ParallelFor(Gpu::KernelInfo().setReduction(true), pbx, [=]
1050  AMREX_GPU_DEVICE(int i, int j, int , Gpu::Handler const& handler) noexcept
1051  {
1052  Real interp{0};
1053  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1054  &interp, mf_arr, z_phys_arr, plo, dxInv, 1);
1055  Real val = interp;
1056  Gpu::deviceReduceSum(&plane_avg[imf], val, handler);
1057  });
1058  } else {
1059  auto k_arr = k_indx->const_array(mfi);
1060  auto j_arr = j_indx ? j_indx->const_array(mfi) : Array4<const int> {};
1061  auto i_arr = i_indx ? i_indx->const_array(mfi) : Array4<const int> {};
1062  ParallelFor(Gpu::KernelInfo().setReduction(true), pbx, [=]
1063  AMREX_GPU_DEVICE(int i, int j, int , Gpu::Handler const& handler) noexcept
1064  {
1065  int mk = k_arr(i,j,0);
1066  int mj = j_arr ? j_arr(i,j,0) : j;
1067  int mi = i_arr ? i_arr(i,j,0) : i;
1068  Real val = mf_arr(mi,mj,mk);
1069  Gpu::deviceReduceSum(&plane_avg[imf], val, handler);
1070  });
1071  }
1072  }
1073  }
1074 
1075  //
1076  //------------------------------------------------------------------------
1077  // Averages for virtual potential temperature
1078  // (This is cell-centered so we don't need to worry about double-counting)
1079  //------------------------------------------------------------------------
1080  //
1081  if (fields[3]) // We have water vapor
1082  {
1083  int iavg = 4;
1084  denom[iavg] = one / (Real)ncell_plane[iavg];
1085  val_old[iavg] = plane_average[iavg]*d_fact_old;
1086 
1087 #ifdef _OPENMP
1088 #pragma omp parallel if (Gpu::notInLaunchRegion())
1089 #endif
1090  for (MFIter mfi(*fields[3], TileNoZ()); mfi.isValid(); ++mfi)
1091  {
1092  Box pbx = mfi.tilebox();
1093 
1094  if (pbx.smallEnd(2) != klo) { continue; }
1095 
1096  pbx.makeSlab(2,klo);
1097 
1098  const Array4<Real const>& T_mf_arr = fields[2]->const_array(mfi);
1099  const Array4<Real const>& qv_mf_arr = fields[3]->const_array(mfi);
1100  const Array4<Real const>& qr_mf_arr = (fields[4]) ? fields[4]->const_array(mfi) :
1101  Array4<const Real> {};
1102 
1103  if (m_interp) {
1104  const auto plo = m_geom[lev].ProbLoArray();
1105  const auto dxInv = m_geom[lev].InvCellSizeArray();
1106  const auto z_phys_arr = z_phys->const_array(mfi);
1107  auto x_pos_arr = x_pos->array(mfi);
1108  auto y_pos_arr = y_pos->array(mfi);
1109  auto z_pos_arr = z_pos->array(mfi);
1110  ParallelFor(Gpu::KernelInfo().setReduction(true), pbx, [=]
1111  AMREX_GPU_DEVICE(int i, int j, int , Gpu::Handler const& handler) noexcept
1112  {
1113  Real T_interp{0};
1114  Real qv_interp{0};
1115  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1116  &T_interp, T_mf_arr, z_phys_arr, plo, dxInv, 1);
1117  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1118  &qv_interp, qv_mf_arr, z_phys_arr, plo, dxInv, 1);
1119  Real vfac;
1120  if (qr_mf_arr) {
1121  // We also have liquid water
1122  Real qr_interp{0};
1123  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1124  &qr_interp, qr_mf_arr, z_phys_arr, plo, dxInv, 1);
1125  vfac = one + epsv*qv_interp - qr_interp;
1126  } else {
1127  vfac = one + epsv*qv_interp;
1128  }
1129  const Real val = T_interp * vfac;
1130  Gpu::deviceReduceSum(&plane_avg[iavg], val, handler);
1131  });
1132  } else {
1133  auto k_arr = k_indx->const_array(mfi);
1134  auto j_arr = j_indx ? j_indx->const_array(mfi) : Array4<const int> {};
1135  auto i_arr = i_indx ? i_indx->const_array(mfi) : Array4<const int> {};
1136  ParallelFor(Gpu::KernelInfo().setReduction(true), pbx, [=]
1137  AMREX_GPU_DEVICE(int i, int j, int , Gpu::Handler const& handler) noexcept
1138  {
1139  int mk = k_arr(i,j,0);
1140  int mj = j_arr ? j_arr(i,j,0) : j;
1141  int mi = i_arr ? i_arr(i,j,0) : i;
1142  Real vfac;
1143  if (qr_mf_arr) {
1144  // We also have liquid water
1145  vfac = one + epsv*qv_mf_arr(mi,mj,mk) - qr_mf_arr(mi,mj,mk);
1146  } else {
1147  vfac = one + epsv*qv_mf_arr(mi,mj,mk);
1148  }
1149  const Real val = T_mf_arr(mi,mj,mk) * vfac;
1150  Gpu::deviceReduceSum(&plane_avg[iavg], val, handler);
1151  });
1152  }
1153  }
1154  }
1155  else // copy temperature
1156  {
1157  int iavg = m_navg - 2;
1158  denom[iavg] = one / (Real)ncell_plane[iavg];
1159  // plane_avg[iavg] = plane_avg[2]
1160  Gpu::copy(Gpu::deviceToDevice, pavg.begin() + 2, pavg.begin() + 3,
1161  pavg.begin() + iavg);
1162  }
1163 
1164  //
1165  //------------------------------------------------------------------------
1166  // Averages for the tangential velocity magnitude
1167  // (This is cell-centered so we don't need to worry about double-counting)
1168  //------------------------------------------------------------------------
1169  //
1170  {
1171  int imf_cc = 2;
1172  int imf = 0;
1173  int iavg = m_navg - 1;
1174  denom[iavg] = one / (Real)ncell_plane[iavg];
1175  val_old[iavg] = plane_average[iavg]*d_fact_old;
1176 
1177  const Real Vsg = m_Vsg[lev];
1178 
1179 #ifdef _OPENMP
1180 #pragma omp parallel if (Gpu::notInLaunchRegion())
1181 #endif
1182  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi)
1183  {
1184  Box pbx = mfi.tilebox();
1185 
1186  if (pbx.smallEnd(2) != klo) { continue; }
1187 
1188  pbx.makeSlab(2,klo);
1189 
1190  // Last element is Umag and always cell centered
1191  auto u_mf_arr = (m_rotate) ? rot_fields[imf ]->const_array(mfi) :
1192  fields[imf ]->const_array(mfi);
1193  auto v_mf_arr = (m_rotate) ? rot_fields[imf+1]->const_array(mfi) :
1194  fields[imf+1]->const_array(mfi);
1195 
1196  if (m_interp) {
1197  const auto plo = m_geom[lev].ProbLoArray();
1198  const auto dxInv = m_geom[lev].InvCellSizeArray();
1199  const auto z_phys_arr = z_phys->const_array(mfi);
1200  auto x_pos_arr = x_pos->array(mfi);
1201  auto y_pos_arr = y_pos->array(mfi);
1202  auto z_pos_arr = z_pos->array(mfi);
1203  ParallelFor(Gpu::KernelInfo().setReduction(true), pbx, [=]
1204  AMREX_GPU_DEVICE(int i, int j, int , Gpu::Handler const& handler) noexcept
1205  {
1206  Real u_interp{0};
1207  Real v_interp{0};
1208  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1209  &u_interp, u_mf_arr, z_phys_arr, plo, dxInv, 1);
1210  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1211  &v_interp, v_mf_arr, z_phys_arr, plo, dxInv, 1);
1212  const Real val = std::sqrt(u_interp*u_interp + v_interp*v_interp + Vsg*Vsg);
1213  Gpu::deviceReduceSum(&plane_avg[iavg], val, handler);
1214  });
1215  } else {
1216  auto k_arr = k_indx->const_array(mfi);
1217  auto j_arr = j_indx ? j_indx->const_array(mfi) : Array4<const int> {};
1218  auto i_arr = i_indx ? i_indx->const_array(mfi) : Array4<const int> {};
1219  ParallelFor(Gpu::KernelInfo().setReduction(true), pbx, [=]
1220  AMREX_GPU_DEVICE(int i, int j, int , Gpu::Handler const& handler) noexcept
1221  {
1222  int mk = k_arr(i,j,0);
1223  int mj = j_arr ? j_arr(i,j,0) : j;
1224  int mi = i_arr ? i_arr(i,j,0) : i;
1225  const Real u_val = myhalf * (u_mf_arr(mi,mj,mk) + u_mf_arr(mi+1,mj ,mk));
1226  const Real v_val = myhalf * (v_mf_arr(mi,mj,mk) + v_mf_arr(mi ,mj+1,mk));
1227  const Real val = std::sqrt(u_val*u_val + v_val*v_val + Vsg*Vsg);
1228  Gpu::deviceReduceSum(&plane_avg[iavg], val, handler);
1229  });
1230  }
1231  }
1232  }
1233 
1234  // Copy to host and sum across procs
1235  Gpu::copy(Gpu::deviceToHost, pavg.begin(), pavg.end(), plane_average.begin());
1236  ParallelDescriptor::ReduceRealSum(plane_average.data(), static_cast<int>(plane_average.size()));
1237 
1238  // No spatial variation with plane averages
1239  for (int iavg(0); iavg < m_navg; ++iavg){
1240  plane_average[iavg] *= denom[iavg]*d_fact_new;
1241  plane_average[iavg] += val_old[iavg];
1242  averages[iavg]->setVal(plane_average[iavg]);
1243  }
1244 }
constexpr amrex::Real myhalf
Definition: ERF_Constants.H:13
amrex::GpuArray< Real, AMREX_SPACEDIM > dxInv
Definition: ERF_InitCustomPertVels_ParticleTests.H:17
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_y_pos
Definition: ERF_MOSTAverage.H:350
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > m_i_indx
Definition: ERF_MOSTAverage.H:352
amrex::Vector< amrex::MultiFab * > m_z_phys_nd
Definition: ERF_MOSTAverage.H:336
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_x_pos
Definition: ERF_MOSTAverage.H:349
amrex::Vector< amrex::Vector< std::unique_ptr< amrex::MultiFab > > > m_rot_fields
Definition: ERF_MOSTAverage.H:356
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_z_pos
Definition: ERF_MOSTAverage.H:351
amrex::Vector< amrex::Vector< int > > m_ncell_plane
Definition: ERF_MOSTAverage.H:360
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > m_j_indx
Definition: ERF_MOSTAverage.H:353
bool m_interp
Definition: ERF_MOSTAverage.H:371
AMREX_GPU_HOST_DEVICE static AMREX_INLINE void trilinear_interp_T(const amrex::Real &xp, const amrex::Real &yp, const amrex::Real &zp, amrex::Real *interp_vals, amrex::Array4< amrex::Real const > const &interp_array, amrex::Array4< amrex::Real const > const &z_arr, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &plo, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &dxi, const int interp_comp)
Definition: ERF_MOSTAverage.H:265
amrex::Vector< std::unique_ptr< amrex::iMultiFab > > m_k_indx
Definition: ERF_MOSTAverage.H:354

Referenced by compute_averages().

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

◆ compute_region_averages()

void MOSTAverage::compute_region_averages ( const int &  lev)

Fill averages for the point or region policy.

Parameters
[in]levlevel index

Function to compute average over local region.

Parameters
[in]levCurrent level
1254 {
1255  // Peel back the level
1256  auto& fields = m_fields[lev];
1257  auto& rot_fields = m_rot_fields[lev];
1258  auto& averages = m_averages[lev];
1259  const auto & geom = m_geom[lev];
1260 
1261  auto& z_phys = m_z_phys_nd[lev];
1262  auto& x_pos = m_x_pos[lev];
1263  auto& y_pos = m_y_pos[lev];
1264  auto& z_pos = m_z_pos[lev];
1265 
1266  auto& i_indx = m_i_indx[lev];
1267  auto& j_indx = m_j_indx[lev];
1268  auto& k_indx = m_k_indx[lev];
1269 
1270  int klo = m_geom[lev].Domain().smallEnd(2);
1271 
1272  // Set factors for time averaging
1273  Real d_fact_new, d_fact_old;
1274  if (m_t_avg && m_t_init[lev]) {
1275  d_fact_new = m_fact_new;
1276  d_fact_old = m_fact_old;
1277  } else {
1278  d_fact_new = one;
1279  d_fact_old = zero;
1280  }
1281 
1282  // Number of cells contained in the local average
1283  const Real denom = one / (Real) m_ncell_region;
1284 
1285  // Capture radius for device
1286  int d_radius = m_radius;
1287 
1288  //
1289  //----------------------------------------------------------
1290  // Averages for U,V,T,Qv
1291  //----------------------------------------------------------
1292  //
1293  for (int imf(0); imf < 4; ++imf) {
1294 
1295  // Continue if no valid Qv pointer
1296  if (!fields[imf]) continue;
1297 
1298 #ifdef _OPENMP
1299 #pragma omp parallel if (Gpu::notInLaunchRegion())
1300 #endif
1301  for (MFIter mfi(*fields[imf], TileNoZ()); mfi.isValid(); ++mfi) {
1302  Box pbx = mfi.tilebox();
1303 
1304  if (pbx.smallEnd(2) != klo) { continue; }
1305 
1306  // Make planar since mfiter is over fields
1307  pbx.makeSlab(2,klo);
1308 
1309  auto mf_arr = (m_rotate) ? rot_fields[imf]->const_array(mfi) :
1310  fields[imf]->const_array(mfi);
1311  auto ma_arr = averages[imf]->array(mfi);
1312 
1313  if (m_interp) {
1314  const auto plo = geom.ProbLoArray();
1315  const auto dx = geom.CellSizeArray();
1316  const auto dxInv = geom.InvCellSizeArray();
1317  const auto z_phys_arr = z_phys->const_array(mfi);
1318  auto x_pos_arr = x_pos->array(mfi);
1319  auto y_pos_arr = y_pos->array(mfi);
1320  auto z_pos_arr = z_pos->array(mfi);
1321  ParallelFor(pbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1322  {
1323  ma_arr(i,j,0) *= d_fact_old;
1324 
1325  Real met_h_zeta = Compute_h_zeta_AtCellCenter(i,j,k,dxInv,z_phys_arr);
1326  for (int lk(-d_radius); lk <= (d_radius); ++lk) {
1327  for (int lj(-d_radius); lj <= (d_radius); ++lj) {
1328  for (int li(-d_radius); li <= (d_radius); ++li) {
1329  Real interp{0};
1330  Real xp = x_pos_arr(i+li,j+lj,0);
1331  Real yp = y_pos_arr(i+li,j+lj,0);
1332  Real zp = z_pos_arr(i+li,j+lj,0) + met_h_zeta*lk*dx[2];
1333  trilinear_interp_T(xp, yp, zp, &interp, mf_arr, z_phys_arr, plo, dxInv, 1);
1334  Real val = denom * interp * d_fact_new;
1335  ma_arr(i,j,0) += val;
1336  }
1337  }
1338  }
1339  });
1340  } else {
1341  auto k_arr = k_indx->const_array(mfi);
1342  auto j_arr = j_indx ? j_indx->const_array(mfi) : Array4<const int> {};
1343  auto i_arr = i_indx ? i_indx->const_array(mfi) : Array4<const int> {};
1344  ParallelFor(pbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
1345  {
1346  ma_arr(i,j,0) *= d_fact_old;
1347 
1348  int mk = k_arr(i,j,0);
1349  int mj = j_arr ? j_arr(i,j,0) : j;
1350  int mi = i_arr ? i_arr(i,j,0) : i;
1351  for (int lk(mk-d_radius); lk <= (mk+d_radius); ++lk) {
1352  for (int lj(mj-d_radius); lj <= (mj+d_radius); ++lj) {
1353  for (int li(mi-d_radius); li <= (mi+d_radius); ++li) {
1354  Real val = denom * mf_arr(li, lj, lk) * d_fact_new;
1355  ma_arr(i,j,0) += val;
1356  }
1357  }
1358  }
1359  });
1360  }
1361  } // MFiter
1362 
1363  // Fill interior ghost cells and any ghost cells outside a periodic domain
1364  //***********************************************************************************
1365  averages[imf]->FillBoundary(geom.periodicity());
1366 
1367  } // imf
1368 
1369  //
1370  //----------------------------------------------------------
1371  // Averages for virtual potential temperature
1372  //----------------------------------------------------------
1373  //
1374  if (fields[3]) // We have water vapor
1375  {
1376  int iavg = 4;
1377 
1378 #ifdef _OPENMP
1379 #pragma omp parallel if (Gpu::notInLaunchRegion())
1380 #endif
1381  for (MFIter mfi(*fields[3], TileNoZ()); mfi.isValid(); ++mfi) {
1382  Box pbx = mfi.tilebox();
1383 
1384  if (pbx.smallEnd(2) != klo) { continue; }
1385 
1386  pbx.makeSlab(2,klo);
1387 
1388  const Array4<Real const>& T_mf_arr = fields[2]->const_array(mfi);
1389  const Array4<Real const>& qv_mf_arr = fields[3]->const_array(mfi);
1390  const Array4<Real const>& qr_mf_arr = (fields[4]) ? fields[4]->const_array(mfi) :
1391  Array4<const Real> {};
1392  auto ma_arr = averages[iavg]->array(mfi);
1393 
1394  if (m_interp) {
1395  const auto plo = geom.ProbLoArray();
1396  const auto dx = geom.CellSizeArray();
1397  const auto dxInv = geom.InvCellSizeArray();
1398  const auto z_phys_arr = z_phys->const_array(mfi);
1399  auto x_pos_arr = x_pos->array(mfi);
1400  auto y_pos_arr = y_pos->array(mfi);
1401  auto z_pos_arr = z_pos->array(mfi);
1402  ParallelFor(pbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1403  {
1404  ma_arr(i,j,0) *= d_fact_old;
1405 
1406  Real met_h_zeta = Compute_h_zeta_AtCellCenter(i,j,k,dxInv,z_phys_arr);
1407  for (int lk(-d_radius); lk <= (d_radius); ++lk) {
1408  for (int lj(-d_radius); lj <= (d_radius); ++lj) {
1409  for (int li(-d_radius); li <= (d_radius); ++li) {
1410  Real T_interp{0};
1411  Real qv_interp{0};
1412  Real xp = x_pos_arr(i+li,j+lj,0);
1413  Real yp = y_pos_arr(i+li,j+lj,0);
1414  Real zp = z_pos_arr(i+li,j+lj,0) + met_h_zeta*lk*dx[2];
1415  trilinear_interp_T(xp, yp, zp, &T_interp, T_mf_arr, z_phys_arr, plo, dxInv, 1);
1416  trilinear_interp_T(xp, yp, zp, &qv_interp, qv_mf_arr, z_phys_arr, plo, dxInv, 1);
1417  Real vfac;
1418  if (qr_mf_arr) {
1419  // We also have liquid water
1420  Real qr_interp{0};
1421  trilinear_interp_T(x_pos_arr(i,j,0), y_pos_arr(i,j,0), z_pos_arr(i,j,0),
1422  &qr_interp, qr_mf_arr, z_phys_arr, plo, dxInv, 1);
1423  vfac = one + epsv*qv_interp - qr_interp;
1424  } else {
1425  vfac = one + epsv*qv_interp;
1426  }
1427  const Real mag = T_interp * vfac;
1428  const Real val = denom * mag * d_fact_new;
1429  ma_arr(i,j,0) += val;
1430  }
1431  }
1432  }
1433  });
1434  } else {
1435  auto k_arr = k_indx->const_array(mfi);
1436  auto j_arr = j_indx ? j_indx->const_array(mfi) : Array4<const int> {};
1437  auto i_arr = i_indx ? i_indx->const_array(mfi) : Array4<const int> {};
1438  ParallelFor(pbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
1439  {
1440  ma_arr(i,j,0) *= d_fact_old;
1441 
1442  int mk = k_arr(i,j,0);
1443  int mj = j_arr ? j_arr(i,j,0) : j;
1444  int mi = i_arr ? i_arr(i,j,0) : i;
1445  for (int lk(mk-d_radius); lk <= (mk+d_radius); ++lk) {
1446  for (int lj(mj-d_radius); lj <= (mj+d_radius); ++lj) {
1447  for (int li(mi-d_radius); li <= (mi+d_radius); ++li) {
1448  Real vfac;
1449  if (qr_mf_arr) {
1450  // We also have liquid water
1451  vfac = one + epsv*qv_mf_arr(li,lj,lk) - qr_mf_arr(li,lj,lk);
1452  } else {
1453  vfac = one + epsv*qv_mf_arr(li,lj,lk);
1454  }
1455  const Real mag = T_mf_arr(li,lj,lk) * vfac;
1456  const Real val = denom * mag * d_fact_new;
1457  ma_arr(i,j,0) += val;
1458  }
1459  }
1460  }
1461  });
1462  }
1463  } // MFiter
1464 
1465  // Fill interior ghost cells and any ghost cells outside a periodic domain
1466  //***********************************************************************************
1467  averages[iavg]->FillBoundary(geom.periodicity());
1468 
1469  }
1470  else // copy temperature
1471  {
1472  int iavg = m_navg - 2;
1473  IntVect ng = averages[iavg]->nGrowVect();
1474  MultiFab::Copy(*(averages[iavg]),*(averages[2]),0,0,1,ng);
1475  }
1476 
1477  //
1478  //----------------------------------------------------------
1479  // Averages for the tangential velocity magnitude
1480  //----------------------------------------------------------
1481  //
1482  {
1483  int imf_cc = 2;
1484  int imf = 0;
1485  int iavg = m_navg - 1;
1486 
1487  const Real Vsg = m_Vsg[lev];
1488 
1489 #ifdef _OPENMP
1490 #pragma omp parallel if (Gpu::notInLaunchRegion())
1491 #endif
1492  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
1493  Box pbx = mfi.tilebox();
1494 
1495  if (pbx.smallEnd(2) != klo) { continue; }
1496 
1497  pbx.makeSlab(2,klo);
1498 
1499  auto u_mf_arr = (m_rotate) ? rot_fields[imf ]->const_array(mfi) :
1500  fields[imf ]->const_array(mfi);
1501  auto v_mf_arr = (m_rotate) ? rot_fields[imf+1]->const_array(mfi) :
1502  fields[imf+1]->const_array(mfi);
1503  auto ma_arr = averages[iavg]->array(mfi);
1504 
1505  if (m_interp) {
1506  const auto plo = geom.ProbLoArray();
1507  const auto dx = geom.CellSizeArray();
1508  const auto dxInv = geom.InvCellSizeArray();
1509  const auto z_phys_arr = z_phys->const_array(mfi);
1510  auto x_pos_arr = x_pos->array(mfi);
1511  auto y_pos_arr = y_pos->array(mfi);
1512  auto z_pos_arr = z_pos->array(mfi);
1513  ParallelFor(pbx, [=] AMREX_GPU_DEVICE(int i, int j, int k) noexcept
1514  {
1515  ma_arr(i,j,0) *= d_fact_old;
1516 
1517  Real met_h_zeta = Compute_h_zeta_AtCellCenter(i,j,k,dxInv,z_phys_arr);
1518  for (int lk(-d_radius); lk <= (d_radius); ++lk) {
1519  for (int lj(-d_radius); lj <= (d_radius); ++lj) {
1520  for (int li(-d_radius); li <= (d_radius); ++li) {
1521  Real u_interp{0};
1522  Real v_interp{0};
1523  Real xp = x_pos_arr(i+li,j+lj,0);
1524  Real yp = y_pos_arr(i+li,j+lj,0);
1525  Real zp = z_pos_arr(i+li,j+lj,0) + met_h_zeta*lk*dx[2];
1526  trilinear_interp_T(xp, yp, zp, &u_interp, u_mf_arr, z_phys_arr, plo, dxInv, 1);
1527  trilinear_interp_T(xp, yp, zp, &v_interp, v_mf_arr, z_phys_arr, plo, dxInv, 1);
1528  const Real mag = std::sqrt(u_interp*u_interp + v_interp*v_interp + Vsg*Vsg);
1529  Real val = denom * mag * d_fact_new;
1530  ma_arr(i,j,0) += val;
1531  }
1532  }
1533  }
1534  });
1535  } else {
1536  auto k_arr = k_indx->const_array(mfi);
1537  auto j_arr = j_indx ? j_indx->const_array(mfi) : Array4<const int> {};
1538  auto i_arr = i_indx ? i_indx->const_array(mfi) : Array4<const int> {};
1539  ParallelFor(pbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
1540  {
1541  ma_arr(i,j,0) *= d_fact_old;
1542 
1543  int mk = k_arr(i,j,0);
1544  int mj = j_arr ? j_arr(i,j,0) : j;
1545  int mi = i_arr ? i_arr(i,j,0) : i;
1546  for (int lk(mk-d_radius); lk <= (mk+d_radius); ++lk) {
1547  for (int lj(mj-d_radius); lj <= (mj+d_radius); ++lj) {
1548  for (int li(mi-d_radius); li <= (mi+d_radius); ++li) {
1549  const Real u_val = myhalf * (u_mf_arr(li,lj,lk) + u_mf_arr(li+1,lj ,lk));
1550  const Real v_val = myhalf * (v_mf_arr(li,lj,lk) + v_mf_arr(li ,lj+1,lk));
1551  const Real mag = std::sqrt(u_val*u_val + v_val*v_val + Vsg*Vsg);
1552  Real val = denom * mag * d_fact_new;
1553  ma_arr(i,j,0) += val;
1554  }
1555  }
1556  }
1557  });
1558  }
1559  } // MFiter
1560 
1561  // Fill interior ghost cells and any ghost cells outside a periodic domain
1562  //***********************************************************************************
1563  averages[iavg]->FillBoundary(geom.periodicity());
1564 
1565  }
1566 
1567  // NOTE: Checking periodicity with the geom structure is not
1568  // sufficient at higher levels. The BA may be contained
1569  // within the domain and it's exterior ghost cells filled
1570  // from interpolation; yet the domain BCs are periodic.
1571 
1572  // Need to fill ghost cells outside the domain if not periodic
1573  bool not_per_x = !(geom.periodicity().isPeriodic(0));
1574  bool not_per_y = !(geom.periodicity().isPeriodic(1));
1575  Box cc_bnd_bx = (m_fields[lev][2]->boxArray()).minimalBox();
1576  Box domain = geom.Domain();
1577  if (domain.contains(cc_bnd_bx) || (not_per_x || not_per_y)) {
1578  for (int iavg(0); iavg < m_navg; ++iavg) {
1579  IntVect ng = averages[iavg]->nGrowVect(); ng[2]=0;
1580 
1581  // NOTE: Level 0 spans the whole domain, but finer
1582  // levels do not have such a restriction.
1583  // For now, use the bounding box of the boxArray.
1584 
1585  // NOTE2: The fields and averages have different indexing.
1586  // The averages are: U/V/T/Qv/Tv/Umag
1587  // The fields are: U/V/T/Qv/Qr/W
1588  // We clip iavg at 2 since all the remaining data is CC
1589 
1590  // Bounded box of CC data used for normalization
1591  int imf = min(iavg,2);
1592  Box bnd_bx = (fields[imf]->boxArray()).minimalBox();
1593 #ifdef _OPENMP
1594 #pragma omp parallel if (Gpu::notInLaunchRegion())
1595 #endif
1596  for (MFIter mfi(*fields[imf], TileNoZ()); mfi.isValid(); ++mfi) {
1597  Box gpbx = mfi.growntilebox(ng);
1598 
1599  if (gpbx.smallEnd(2) != klo) { continue; }
1600 
1601  gpbx.makeSlab(2,klo);
1602 
1603  if (bnd_bx.contains(gpbx)) continue;
1604 
1605  auto ma_arr = averages[iavg]->array(mfi);
1606 
1607  int i_lo = bnd_bx.smallEnd(0); int i_hi = bnd_bx.bigEnd(0);
1608  int j_lo = bnd_bx.smallEnd(1); int j_hi = bnd_bx.bigEnd(1);
1609  ParallelFor(gpbx, [=] AMREX_GPU_DEVICE(int i, int j, int ) noexcept
1610  {
1611  int li, lj;
1612  li = i < i_lo ? i_lo : i;
1613  li = li > i_hi ? i_hi : li;
1614  lj = j < j_lo ? j_lo : j;
1615  lj = lj > j_hi ? j_hi : lj;
1616 
1617  ma_arr(i,j,0) = ma_arr(li,lj,0);
1618  });
1619  } // MFiter
1620  } // iavg
1621  } // Not periodic
1622 }
AMREX_FORCE_INLINE AMREX_GPU_DEVICE amrex::Real Compute_h_zeta_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:55
int m_radius
Definition: ERF_MOSTAverage.H:365
int m_ncell_region
Definition: ERF_MOSTAverage.H:366
@ ng
Definition: ERF_Morrison.H:49

Referenced by compute_averages().

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

◆ get_average()

const amrex::MultiFab* MOSTAverage::get_average ( const int &  lev,
const int &  comp 
) const
inline

Return one 2D average MultiFab.

Parameters
[in]levlevel index
[in]compaverage component index
235 { return m_averages[lev][comp].get(); }

Referenced by SurfaceLayer::get_mac_avg().

Here is the caller graph for this function:

◆ get_k_indices()

const amrex::iMultiFab* MOSTAverage::get_k_indices ( const int &  lev) const
inline

Return the k-index iMultiFab.

Parameters
[in]levlevel index
249 { return m_k_indx[lev].get(); }

◆ get_zref()

amrex::MultiFab* MOSTAverage::get_zref ( const int &  lev) const
inline

Return z_ref, which may be computed from a specified k-index.

Parameters
[in]levlevel index
242 { return m_zref[lev].get(); }
amrex::Vector< std::unique_ptr< amrex::MultiFab > > m_zref
Definition: ERF_MOSTAverage.H:348

Referenced by SurfaceLayer::get_zref().

Here is the caller graph for this function:

◆ make_MOSTAverage_at_level()

void MOSTAverage::make_MOSTAverage_at_level ( const int &  lev,
const amrex::Vector< amrex::MultiFab * > &  vars_old,
std::unique_ptr< amrex::MultiFab > &  Theta_prim,
std::unique_ptr< amrex::MultiFab > &  Qv_prim,
std::unique_ptr< amrex::MultiFab > &  Qr_prim,
std::unique_ptr< amrex::MultiFab > &  z_phys_nd 
)

Make MOST-average data structures at one level.

Parameters
[in]levlevel index
[in]vars_oldold-time state and velocity fields
[in]Theta_primprimitive potential-temperature field
[in]Qv_primprimitive water-vapor field
[in]Qr_primprimitive rain-water field
[in]z_phys_ndnodal physical-height field

Make MOSTAverage data structures at one level.

Parameters
[in]levCurrent level
[in]vars_oldState data used by the average calculator
[in]Theta_primPrimitive theta component at this level
[in]Qv_primPrimitive water-vapor component at this level
[in]Qr_primPrimitive rain-water component at this level
[in]z_phys_ndNodal physical height at this level
110 {
111  m_fields[lev].resize(m_nvar);
112  m_rot_fields[lev].resize(m_nvar-1);
113  m_averages[lev].resize(m_navg);
114  m_z_phys_nd[lev] = z_phys_nd.get();
115 
116  bool use_terrain_fitted_coords = ( (m_terrain_type == TerrainType::StaticFittedMesh) ||
117  (m_terrain_type == TerrainType::MovingFittedMesh) );
118 
119  bool use_eb = (m_terrain_type == TerrainType::EB);
120 
121  { // Nodal in x
122  auto& mf = *vars_old[Vars::xvel];
123  // Create a 2D ba, dm, & ghost cells
124  const BoxArray& ba = mf.boxArray();
125  BoxList bl2d = ba.boxList();
126  for (auto& b : bl2d) { b.setRange(2,0); }
127  BoxArray ba2d(std::move(bl2d));
128  const DistributionMapping& dm = mf.DistributionMap();
129  const int ncomp = 1;
130  IntVect ng = mf.nGrowVect(); ng[2]=0;
131 
132  m_fields[lev][0] = vars_old[Vars::xvel];
133  m_averages[lev][0] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
134  m_averages[lev][0]->setVal(bogus_large_value);
135  if (m_rotate) {
136  m_rot_fields[lev][0] = std::make_unique<MultiFab>(ba,dm,ncomp,ng);
137  MultiFab::Copy(*m_rot_fields[lev][0],mf,0,0,1,ng);
138  } else {
139  m_rot_fields[lev][0] = nullptr;
140  }
141  }
142  { // Nodal in y
143  auto& mf = *vars_old[Vars::yvel];
144  // Create a 2D ba, dm, & ghost cells
145  const BoxArray& ba = mf.boxArray();
146  BoxList bl2d = ba.boxList();
147  for (auto& b : bl2d) { b.setRange(2,0); }
148  BoxArray ba2d(std::move(bl2d));
149  const DistributionMapping& dm = mf.DistributionMap();
150  const int ncomp = 1;
151  IntVect ng = mf.nGrowVect(); ng[2]=0;
152 
153  m_fields[lev][1] = vars_old[Vars::yvel];
154  m_averages[lev][1] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
155  m_averages[lev][1]->setVal(bogus_large_value);
156  if (m_rotate) {
157  m_rot_fields[lev][1] = std::make_unique<MultiFab>(ba,dm,ncomp,ng);
158  MultiFab::Copy(*m_rot_fields[lev][1],mf,0,0,1,ng);
159  } else {
160  m_rot_fields[lev][1] = nullptr;
161  }
162  }
163  { // CC vars
164  auto& mf = *Theta_prim;
165  // Create a 2D ba, dm, & ghost cells
166  const BoxArray& ba = mf.boxArray();
167  BoxList bl2d = ba.boxList();
168  for (auto& b : bl2d) { b.setRange(2,0); }
169  BoxArray ba2d(std::move(bl2d));
170  const DistributionMapping& dm = mf.DistributionMap();
171  const int ncomp = 1;
172  const int incomp = 1;
173  IntVect ng = mf.nGrowVect(); ng[2]=0;
174 
175  // Get field pointers
176  m_fields[lev][2] = Theta_prim.get();
177  m_fields[lev][3] = Qv_prim.get();
178  m_fields[lev][4] = Qr_prim.get();
179 
180  // Initialize remaining multifabs
181  for (int iavg(2); iavg < m_navg; ++iavg) {
182  m_averages[lev][iavg] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
183  m_averages[lev][iavg]->setVal(bogus_large_value);
184  }
185 
186  // Default to dry
187  m_averages[lev][3]->setVal(0.0);
188 
189  if (m_rotate) {
190  m_rot_fields[lev][2] = std::make_unique<MultiFab>(ba,dm,ncomp,ng);
191  m_rot_fields[lev][3] = std::make_unique<MultiFab>(ba,dm,ncomp,ng);
192  MultiFab::Copy(*m_rot_fields[lev][2],*Theta_prim,0,0,1,ng);
193  if (Qv_prim) MultiFab::Copy(*m_rot_fields[lev][3],*Qv_prim,0,0,1,ng);
194  } else {
195  m_rot_fields[lev][2] = nullptr;
196  m_rot_fields[lev][3] = nullptr;
197  }
198 
199  // Default zref to 10 and fill will true values later
200  m_zref[lev] = std::make_unique<MultiFab>(ba2d,dm,1,ng);
201  m_zref[lev]->setVal(zref_default);
202 
203  if (use_terrain_fitted_coords && m_norm_vec && m_interp) {
204  m_x_pos[lev] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
205  m_y_pos[lev] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
206  m_z_pos[lev] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
207  } else if (use_terrain_fitted_coords && m_interp) {
208  m_x_pos[lev] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
209  m_y_pos[lev] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
210  m_z_pos[lev] = std::make_unique<MultiFab>(ba2d,dm,ncomp,ng);
211  } else if (use_terrain_fitted_coords && m_norm_vec) {
212  m_i_indx[lev] = std::make_unique<iMultiFab>(ba2d,dm,incomp,ng);
213  m_j_indx[lev] = std::make_unique<iMultiFab>(ba2d,dm,incomp,ng);
214  m_k_indx[lev] = std::make_unique<iMultiFab>(ba2d,dm,incomp,ng);
215  } else {
216  if (!use_eb) {
217  m_k_indx[lev] = std::make_unique<iMultiFab>(ba2d,dm,incomp,ng);
218  }
219  }
220  }
221  // Nodal in z (only used with terrain stress rotations)
222  m_fields[lev][5] = vars_old[Vars::zvel];
223 
224  // Setup auxiliary data for spatial configuration & policy
225  //--------------------------------------------------------
226  if (use_terrain_fitted_coords && m_norm_vec && m_interp) { // Terrain w/ norm & w/ interpolation
228  } else if (use_terrain_fitted_coords && m_interp) { // Terrain w/ interpolation
229  set_z_positions_T(lev);
230  } else if (use_terrain_fitted_coords && m_norm_vec) { // Terrain w/ norm & w/o interpolation
231  set_norm_indices_T(lev);
232  } else if (use_terrain_fitted_coords) { // Terrain
233  set_k_indices_T(lev);
234  } else if (use_eb) { // EB
235  set_z_positions_EB(lev);
236  } else { // No Terrain
237  set_k_indices_N(lev);
238  }
239 
240  // Setup normalization data for the chosen policy
241  //--------------------------------------------------------
242  switch(m_policy) {
243  case 0: // Plane average
245  break;
246  case 1: // Local region/point
248  break;
249  case 2: // EB
251  break;
252  default:
253  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(false, "Unknown policy for MOSTAverage!");
254  }
255 
256  // Set up the exponential time filtering
257  //--------------------------------------------------------
258  if (m_t_avg) {
259  // Exponential filter function
260  m_fact_old = static_cast<amrex::Real>(std::exp(-1.0 / m_time_window));
261 
262  // Enforce discrete normalization: (mfn*val_new + mfo*val_old)
264 
265  // None of the averages are initialized
266  m_t_init.resize(m_maxlev,0);
267  }
268 
269  // Correction to the mean surface velocity at this level
270  m_Vsg[lev] = zero;
271  if (include_subgrid_vel) {
272  Print() << "Subgrid velocity scale correction at level : " << lev << ' ';
273  const auto dxArr = m_geom[lev].CellSizeArray();
274  Real dx = std::sqrt(dxArr[0]*dxArr[1]);
275  if (dx > Real(5000.)) {
276  m_Vsg[lev] = Real(0.32) * std::pow(dx/Real(5000.)-1, Real(0.33));
277  }
278  Print() << m_Vsg[lev] << std::endl;
279  }
280 }
constexpr amrex::Real bogus_large_value
Definition: ERF_Constants.H:26
double m_time_window
Definition: ERF_MOSTAverage.H:383
void set_z_positions_EB(const int &lev)
Definition: ERF_MOSTAverage.cpp:577
void set_region_normalization(const int &)
Definition: ERF_MOSTAverage.H:126
void set_z_positions_T(const int &lev)
Definition: ERF_MOSTAverage.cpp:770
void set_norm_positions_T(const int &lev)
Definition: ERF_MOSTAverage.cpp:835
void set_k_indices_T(const int &lev)
Definition: ERF_MOSTAverage.cpp:600
void set_plane_normalization(const int &lev)
Definition: ERF_MOSTAverage.cpp:373
TerrainType m_terrain_type
Definition: ERF_MOSTAverage.H:339
bool m_norm_vec
Definition: ERF_MOSTAverage.H:372
int m_nvar
Definition: ERF_MOSTAverage.H:343
void set_norm_indices_T(const int &lev)
Definition: ERF_MOSTAverage.cpp:681
int m_maxlev
Definition: ERF_MOSTAverage.H:345
void set_eb_normalization(const int &lev)
Definition: ERF_MOSTAverage.cpp:431
bool include_subgrid_vel
Definition: ERF_MOSTAverage.H:388
void set_k_indices_N(const int &lev)
Definition: ERF_MOSTAverage.cpp:520
const amrex::Real zref_default
Definition: ERF_MOSTAverage.H:393
@ xvel
Definition: ERF_IndexDefines.H:177
@ zvel
Definition: ERF_IndexDefines.H:179
@ yvel
Definition: ERF_IndexDefines.H:178

Referenced by SurfaceLayer::make_SurfaceLayer_at_level().

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

◆ operator=() [1/2]

MOSTAverage& MOSTAverage::operator= ( const MOSTAverage other)
delete

Deleted copy-assignment operator.

Parameters
[in]othersource object

◆ operator=() [2/2]

MOSTAverage& MOSTAverage::operator= ( MOSTAverage &&  other)
deletenoexcept

Deleted move-assignment operator.

Parameters
[in]othersource object

◆ set_eb_normalization()

void MOSTAverage::set_eb_normalization ( const int &  lev)

Compute total embedded-boundary surface area.

Parameters
[in]levlevel index

Function to compute normalization for average over EB.

Parameters
[in]levCurrent level
432 {
433  AMREX_ALWAYS_ASSERT(m_eb_vec[lev] != nullptr);
434 
435  // Get EB data - need both cell-centered and face-centered flags
436  const auto& cc_flags = m_eb_vec[lev]->get_const_factory()->getMultiEBCellFlagFab();
437 
438  // Get area fractions for different centerings
439  auto cc_afrac = m_eb_vec[lev]->get_const_factory()->getAreaFrac(); // Cell-centered area fractions (Array of 3 MultiCutFab*)
440 
441  // Initialize storage
443  m_total_bndry_area[lev].resize(m_navg, zero);
444  m_plane_average.resize(m_maxlev);
445  m_plane_average[lev].resize(m_navg, zero);
446 
447  // Compute total area for each field type based on its centering
448  // iavg: 0=U(xface), 1=V(yface), 2=T(cc), 3=Qv(cc), 4=Tv(cc), 5=Umag(cc)
449 
450  // Get geometry for cell sizes
451  auto const& dx_arr = m_geom[lev].CellSizeArray();
452  Real dx = dx_arr[0];
453  Real dy = dx_arr[1];
454  Real dz = dx_arr[2];
455 
456  // GPU array to accumulate areas
457  Gpu::DeviceVector<Real> area_vec(m_navg, zero);
458  Real* area_device = area_vec.data();
459 
460  // All fields are now averaged on cell-centered grid
461  // Compute total area once on cell-centered grid and use for all iavg
462  Real total_area = zero;
463 #ifdef _OPENMP
464 #pragma omp parallel if (Gpu::notInLaunchRegion())
465 #endif
466  for (MFIter mfi(cc_flags, TileNoZ()); mfi.isValid(); ++mfi) {
467  const auto& flag = cc_flags[mfi];
468 
469  // Skip boxes that are not singlevalued (MultiCutFab only has data for singlevalued boxes)
470  if (flag.getType() != FabType::singlevalued) continue;
471 
472  Box bx = mfi.tilebox();
473  auto const flag_arr = flag.const_array();
474  auto const afrac_x = cc_afrac[0]->const_array(mfi);
475  auto const afrac_y = cc_afrac[1]->const_array(mfi);
476  auto const afrac_z = cc_afrac[2]->const_array(mfi);
477 
478  // Sum area only for cut cells using atomic reduction
479  ParallelFor(Gpu::KernelInfo().setReduction(true), bx, [=]
480  AMREX_GPU_DEVICE(int i, int j, int k, Gpu::Handler const& handler) noexcept
481  {
482  if (flag_arr(i,j,k).isSingleValued()) {
483  // Compute area from face-centered area fractions
484  Real axm = afrac_x(i ,j ,k );
485  Real axp = afrac_x(i+1,j ,k );
486  Real aym = afrac_y(i ,j ,k );
487  Real ayp = afrac_y(i ,j+1,k );
488  Real azm = afrac_z(i ,j ,k );
489  Real azp = afrac_z(i ,j ,k+1);
490 
491  Real adx = (axm - axp) * dy * dz;
492  Real ady = (aym - ayp) * dx * dz;
493  Real adz = (azm - azp) * dx * dy;
494 
495  Real area = std::sqrt(adx*adx + ady*ady + adz*adz);
496 
497  Gpu::deviceReduceSum(&area_device[0], area, handler);
498  }
499  });
500  }
501 
502  // Copy to host and sum across MPI ranks
503  Gpu::copy(Gpu::deviceToHost, area_vec.begin(), area_vec.begin() + 1, &total_area);
504  ParallelDescriptor::ReduceRealSum(&total_area, 1);
505 
506  // Set the same total area for all iavg
507  for (int iavg = 0; iavg < m_navg; ++iavg) {
508  m_total_bndry_area[lev][iavg] = total_area;
509  }
510 
511  Print() << "EB surface area on cell-centerd grid at level " << lev << ": " << total_area << std::endl;
512 }

Referenced by make_MOSTAverage_at_level().

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

◆ set_k_indices_N()

void MOSTAverage::set_k_indices_N ( const int &  lev)

Populate the 2D k-index iMultiFab without terrain.

Parameters
[in]levlevel index

Function to set K indices without terrain.

Parameters
[in]levCurrent level
521 {
522  ParmParse pp(m_pp_prefix);
523  Real zref_tmp = zref_default;
524  auto read_z = pp.query("most.zref",zref_tmp);
525  auto read_k = pp.queryarr("most.k_arr_in",m_k_in);
526 
527  // Default behavior is to use the first cell center
528  if (!read_z && !read_k) {
529  Real m_zlo = m_geom[0].ProbLo(2);
530  Real m_dz = m_geom[0].CellSize(2);
531  zref_tmp = m_zlo + myhalf * m_dz;
532  m_zref[lev]->setVal( zref_tmp );
533  Print() << "Reference height for MOST set to " << zref_tmp << std::endl;
534  read_z = true;
535  }
536 
537  // Specify z_ref & compute k_indx (z_ref takes precedence)
538  if (read_z) {
539  Real m_zlo = m_geom[lev].ProbLo(2);
540  Real m_zhi = m_geom[lev].ProbHi(2);
541  Real m_dz = m_geom[lev].CellSize(2);
542 
543  amrex::ignore_unused(m_zhi);
544 
545  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(zref_tmp >= m_zlo + myhalf * m_dz,
546  "Query point must be past first z-cell!");
547 
548  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(zref_tmp <= m_zhi - myhalf * m_dz,
549  "Query point must be below the last z-cell!");
550 
551  int lk = static_cast<int>(floor((zref_tmp - m_zlo) / m_dz - myhalf));
552 
553  m_zref[lev]->setVal( (lk + myhalf) * m_dz + m_zlo );
554 
556 
557  m_k_indx[lev]->setVal(lk);
558  // Specified k_indx & compute z_ref
559  } else if (read_k) {
561  "K index must be larger than averaging radius!");
562  m_k_indx[lev]->setVal(m_k_in[lev]);
563 
564  // TODO: check that z_ref is constant across levels
565  Real m_zlo = m_geom[0].ProbLo(2);
566  Real m_dz = m_geom[0].CellSize(2);
567  m_zref[lev]->setVal( ((Real)m_k_in[0] + myhalf) * m_dz + m_zlo );
568  }
569 }
ParmParse pp("prob")
std::string m_pp_prefix
Definition: ERF_MOSTAverage.H:337
amrex::Vector< int > m_k_in
Definition: ERF_MOSTAverage.H:367

Referenced by make_MOSTAverage_at_level().

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

◆ set_k_indices_T()

void MOSTAverage::set_k_indices_T ( const int &  lev)

Populate the 2D k-index iMultiFab with terrain.

Parameters
[in]levlevel index

Function to set K indices with terrain (w/o terrain normals or interpolation).

Parameters
[in]levCurrent level
601 {
602  // Peel back the level
603  auto& fields = m_fields[lev];
604 
605  // MFIter over CC data
606  int imf_cc = 2;
607 
608  ParmParse pp(m_pp_prefix);
609  Real zref_tmp = zref_default;
610  auto read_z = pp.query("most.zref",zref_tmp);
611  auto read_k = pp.queryarr("most.k_arr_in",m_k_in);
612  int klo = m_geom[lev].Domain().smallEnd(2);
613 
614  // Allow default zref
615  if (!read_z) {
616  Print() << "most.zref not specified, query distance default is " << zref_tmp << std::endl;
617  read_z = true;
618  }
619 
620  // No default behavior with terrain (we can't tell the difference between
621  // vertical grid stretching and true terrain)
622  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(read_z != read_k,
623  "Need to specify zref or k_arr_in for MOST");
624 
625  // Capture for device
626  Real d_zref = zref_tmp;
627  Real d_radius = static_cast<Real>(m_radius);
628  amrex::ignore_unused(d_radius);
629 
630  // Specify z_ref & compute k_indx (z_ref takes precedence)
631  if (read_z) {
632  int kmax = m_geom[lev].Domain().bigEnd(2);
633  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
634  Box npbx = mfi.tilebox(IntVect(1,1,0),IntVect(1,1,0));
635 
636  if (npbx.smallEnd(2) != klo) { continue; }
637 
638  npbx.makeSlab(2,klo);
639 
640  const auto z_phys_arr = m_z_phys_nd[lev]->const_array(mfi);
641  auto k_arr = m_k_indx[lev]->array(mfi);
642  auto zref_arr = m_zref[lev]->array(mfi);
643  ParallelFor(npbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
644  {
645  k_arr(i,j,k) = klo;
646  bool found = false;
647  Real z_bot_face = fourth * ( z_phys_arr(i ,j ,k) + z_phys_arr(i+1,j ,k)
648  + z_phys_arr(i ,j+1,k) + z_phys_arr(i+1,j+1,k) );
649  Real z_target = z_bot_face + d_zref;
650  for (int lk(klo); lk<=kmax; ++lk) {
651  Real z_lo = fourth * ( z_phys_arr(i,j ,lk ) + z_phys_arr(i+1,j ,lk )
652  + z_phys_arr(i,j+1,lk ) + z_phys_arr(i+1,j+1,lk ) );
653  Real z_hi = fourth * ( z_phys_arr(i,j ,lk+1) + z_phys_arr(i+1,j ,lk+1)
654  + z_phys_arr(i,j+1,lk+1) + z_phys_arr(i+1,j+1,lk+1) );
655  if (z_target > z_lo && z_target < z_hi){
656  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(lk >= d_radius,
657  "K index must be larger than averaging radius!");
658  k_arr(i,j,0) = lk;
659  zref_arr(i,j,0) = myhalf * (z_hi + z_lo) - z_bot_face;
660  found = true;
661  break;
662  }
663  }
665  "zref not found with terrain!");
666  });
667  }
668  // Specified k_indx & compute z_ref
669  } else if (read_k) {
670  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(false, "Specified k-indx with terrain not implemented!");
671  }
672 }
constexpr amrex::Real fourth
Definition: ERF_Constants.H:14

Referenced by make_MOSTAverage_at_level().

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

◆ set_norm_indices_T()

void MOSTAverage::set_norm_indices_T ( const int &  lev)

Populate all 2D normal-index iMultiFabs with terrain.

Parameters
[in]levlevel index

Function to set I,J,K indices with terrain normals (w/o interpolation).

Parameters
[in]levCurrent level
682 {
683  // Peel back the level
684  auto& fields = m_fields[lev];
685 
686  // MFIter over CC data
687  int imf_cc = 2;
688 
689  ParmParse pp(m_pp_prefix);
690  Real zref_tmp = zref_default;
691  auto read_zref = pp.query("most.zref",zref_tmp);
692  if (!read_zref) {
693  Print() << "most.zref not specified, query distance default is " << zref_tmp << std::endl;
694  }
695  int klo = m_geom[lev].Domain().smallEnd(2);
696 
697  // Capture for device
698  Real d_zref = zref_tmp;
699  Real d_radius = static_cast<Real>(m_radius);
700 
701  const auto dxInv = m_geom[lev].InvCellSizeArray();
702  IntVect ng = m_k_indx[lev]->nGrowVect(); ng[2]=0;
703  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
704  Box npbx = mfi.tilebox(IntVect(1,1,0),IntVect(1,1,0));
705 
706  if (npbx.smallEnd(2) != klo) { continue; }
707 
708  int kmax = npbx.bigEnd(2);
709 
710  npbx.makeSlab(2,klo);
711 
712  const auto z_phys_arr = m_z_phys_nd[lev]->const_array(mfi);
713  auto i_arr = m_i_indx[lev]->array(mfi);
714  auto j_arr = m_j_indx[lev]->array(mfi);
715  auto k_arr = m_k_indx[lev]->array(mfi);
716  auto zref_arr = m_zref[lev]->array(mfi);
717  ParallelFor(npbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
718  {
719  // Elements of normal vector
720  Real met_h_xi = Compute_h_xi_AtCellCenter (i,j,k,dxInv,z_phys_arr);
721  Real met_h_eta = Compute_h_eta_AtCellCenter(i,j,k,dxInv,z_phys_arr);
722  Real mag = std::sqrt(met_h_xi*met_h_xi + met_h_eta*met_h_eta + one);
723 
724  // Unit-normal vector scaled by z_ref
725  Real delta_x = -met_h_xi/mag * d_zref;
726  Real delta_y = -met_h_eta/mag * d_zref;
727  Real delta_z = one/mag * d_zref;
728 
729  // Compute i & j as displacements (no grid stretching)
730  int delta_i = static_cast<int>(std::round(delta_x*dxInv[0]));
731  int delta_j = static_cast<int>(std::round(delta_y*dxInv[1]));
732  int i_new = i + delta_i;
733  int j_new = j + delta_j;
734  i_arr(i,j,0) = i_new;
735  j_arr(i,j,0) = j_new;
736 
737  // Search for k (grid is stretched in z)
738  Real z_bot_face = fourth * ( z_phys_arr(i ,j ,k) + z_phys_arr(i+1,j ,k)
739  + z_phys_arr(i ,j+1,k) + z_phys_arr(i+1,j+1,k) );
740  Real z_target = z_bot_face + delta_z;
741  k_arr(i,j,0) = klo;
742  zref_arr(i,j,0) = myhalf * z_bot_face +
743  Real(0.125) * ( z_phys_arr(i ,j ,k+1) + z_phys_arr(i+1,j ,k+1)
744  + z_phys_arr(i ,j+1,k+1) + z_phys_arr(i+1,j+1,k+1) );
745  for (int lk(klo); lk<=kmax; ++lk) {
746  Real z_lo = fourth * ( z_phys_arr(i_new,j_new ,lk ) + z_phys_arr(i_new+1,j_new ,lk )
747  + z_phys_arr(i_new,j_new+1,lk ) + z_phys_arr(i_new+1,j_new+1,lk ) );
748  Real z_hi = fourth * ( z_phys_arr(i_new,j_new ,lk+1) + z_phys_arr(i_new+1,j_new ,lk+1)
749  + z_phys_arr(i_new,j_new+1,lk+1) + z_phys_arr(i_new+1,j_new+1,lk+1) );
750  if (z_target > z_lo && z_target < z_hi){
751  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(lk >= d_radius,
752  "K index must be larger than averaging radius!");
753  amrex::ignore_unused(d_radius);
754  k_arr(i,j,0) = lk;
755  zref_arr(i,j,0) = myhalf * (z_hi + z_lo) - z_bot_face;
756  break;
757  }
758  }
759  });
760  }
761 }
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:85
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtCellCenter(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:70

Referenced by make_MOSTAverage_at_level().

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

◆ set_norm_positions_T()

void MOSTAverage::set_norm_positions_T ( const int &  lev)

Populate terrain-aware normal positions.

Parameters
[in]levlevel index

Function to set positions with terrain and normal vector (with interpolation).

Parameters
[in]levCurrent level
836 {
837  // Peel back the level
838  auto& fields = m_fields[lev];
839 
840  // MFIter over CC data
841  int imf_cc = 2;
842 
843  ParmParse pp(m_pp_prefix);
844  Real zref_tmp = zref_default;
845  auto read_zref = pp.query("most.zref",zref_tmp);
846  if (!read_zref) {
847  Print() << "most.zref not specified, query distance default is " << zref_tmp << std::endl;
848  }
849  int klo = m_geom[lev].Domain().smallEnd(2);
850 
851  // Capture for device
852  Real d_zref = zref_tmp;
853  const auto plo = m_geom[lev].ProbLoArray();
854 
855  RealVect base;
856  const auto dx = m_geom[lev].CellSizeArray();
857  const auto dxInv = m_geom[lev].InvCellSizeArray();
858  IntVect ng = m_x_pos[lev]->nGrowVect(); ng[2]=0;
859  const int position_ng = (m_radius > 1) ? m_radius : 1;
860  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
861  Box npbx = mfi.tilebox(IntVect(1,1,0),IntVect(position_ng,position_ng,0));
862  Box gtbx = mfi.growntilebox(ng);
863  RealBox grb{gtbx,dx.data(),base.dataPtr()};
864 
865  if (npbx.smallEnd(2) != klo) { continue; }
866 
867  npbx.makeSlab(2,klo);
868 
869  const auto z_phys_arr = m_z_phys_nd[lev]->const_array(mfi);
870  auto x_pos_arr = m_x_pos[lev]->array(mfi);
871  auto y_pos_arr = m_y_pos[lev]->array(mfi);
872  auto z_pos_arr = m_z_pos[lev]->array(mfi);
873  auto zref_arr = m_zref[lev]->array(mfi);
874  ParallelFor(npbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
875  {
876  // Elements of normal vector
877  Real met_h_xi = Compute_h_xi_AtCellCenter (i,j,k,dxInv,z_phys_arr);
878  Real met_h_eta = Compute_h_eta_AtCellCenter(i,j,k,dxInv,z_phys_arr);
879  Real imag = one / std::sqrt(met_h_xi*met_h_xi + met_h_eta*met_h_eta + one);
880 
881  // Unit-normal vector scaled by z_ref
882  Real delta_x = -met_h_xi * imag * d_zref;
883  Real delta_y = -met_h_eta * imag * d_zref;
884  Real delta_z = imag * d_zref;
885 
886  // Position of the current node (indx:0,0,1)
887  Real x0 = plo[0] + ((Real) i + myhalf) * dx[0];
888  Real y0 = plo[1] + ((Real) j + myhalf) * dx[1];
889 
890  // Final position at end of vector
891  x_pos_arr(i,j,0) = x0 + delta_x;
892  y_pos_arr(i,j,0) = y0 + delta_y;
893  Real z_bot_face = fourth * ( z_phys_arr(i ,j ,k) + z_phys_arr(i+1,j ,k)
894  + z_phys_arr(i ,j+1,k) + z_phys_arr(i+1,j+1,k) );
895  z_pos_arr(i,j,0) = z_bot_face + delta_z;
896 
897  // NOTE: Normal vector end point can be below the surface for concave regions.
898  // Here we protect against that by augmenting the normal if needed.
899  int i_new = (int) ((x_pos_arr(i,j,0) - plo[0]) / dx[0] - myhalf);
900  int j_new = (int) ((y_pos_arr(i,j,0) - plo[1]) / dx[1] - myhalf);
901  Real z_new_bot_face = fourth * ( z_phys_arr(i_new,j_new ,k) + z_phys_arr(i_new+1,j_new ,k)
902  + z_phys_arr(i_new,j_new+1,k) + z_phys_arr(i_new+1,j_new+1,k) );
903  if (z_pos_arr(i,j,0) < z_new_bot_face) {
904  z_pos_arr(i,j,0) = z_new_bot_face + delta_z;
905  }
906 
907  zref_arr(i,j,0) = delta_z;
908 
909  // Destination position must be contained on the current process!
910  Real pos[] = {x_pos_arr(i,j,0)-plo[0],y_pos_arr(i,j,0)-plo[1],myhalf*dx[2]};
911  amrex::ignore_unused(pos);
912  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(grb.contains(&pos[0]),
913  "Query point outside of proc domain!");
914  });
915  }
916 }

Referenced by make_MOSTAverage_at_level().

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

◆ set_plane_normalization()

void MOSTAverage::set_plane_normalization ( const int &  lev)

Compute number of cells per averaging plane.

Parameters
[in]levlevel index

Function to compute normalization for plane average.

Parameters
[in]levCurrent level
374 {
375  // Cells per plane and temp avg storage
376  m_ncell_plane.resize(m_maxlev);
377  m_plane_average.resize(m_maxlev);
378 
379  // True domain not used for normalization
380  Box domain = m_geom[lev].Domain();
381 
382  // NOTE: Level 0 spans the whole domain, but finer
383  // levels do not have such a restriction.
384  // For now, use the bounding box of the boxArray
385  // for normalization, consistent with avg routine.
386 
387  // Bounded box of CC data used for normalization
388  Box bnd_bx = (m_fields[lev][2]->boxArray()).minimalBox();
389 
390  // NOTE: Bounding box must lie on the periodic boundaries
391  // in order to trip the is_per flag
392 
393  // Num components, plane avg, cells per plane
394  Array<int,AMREX_SPACEDIM> is_per = {0,0,0};
395  for (int idim(0); idim < AMREX_SPACEDIM-1; ++idim) {
396  if ( m_geom[lev].isPeriodic(idim) &&
397  bnd_bx.bigEnd(idim)==domain.bigEnd(idim) &&
398  bnd_bx.smallEnd(idim)==domain.smallEnd(idim) ) { is_per[idim] = 1; }
399  }
400 
401  m_ncell_plane[lev].resize(m_navg);
402  m_plane_average[lev].resize(m_navg);
403  for (int iavg(0); iavg < m_navg; ++iavg) {
404  // Convert bnd_bx to current index type
405  IndexType ixt = m_averages[lev][iavg]->boxArray().ixType();
406  bnd_bx.convert(ixt);
407  IntVect bnd_bx_lo(bnd_bx.loVect());
408  IntVect bnd_bx_hi(bnd_bx.hiVect());
409 
410  m_plane_average[lev][iavg] = zero;
411 
412  m_ncell_plane[lev][iavg] = 1;
413  for (int idim(0); idim < AMREX_SPACEDIM; ++idim) {
414  if (idim != 2) {
415  if (ixt.nodeCentered(idim) && is_per[idim]) {
416  m_ncell_plane[lev][iavg] *= (bnd_bx_hi[idim] - bnd_bx_lo[idim]);
417  } else {
418  m_ncell_plane[lev][iavg] *= (bnd_bx_hi[idim] - bnd_bx_lo[idim] + 1);
419  }
420  }
421  } // idim
422  } // iavg
423 }

Referenced by make_MOSTAverage_at_level().

Here is the caller graph for this function:

◆ set_region_normalization()

void MOSTAverage::set_region_normalization ( const int &  )
inline

Compute number of cells in the region average.

Parameters
[in]levlevel index
127  {m_ncell_region = (2 * m_radius + 1) * (2 * m_radius + 1) * (2 * m_radius + 1);}

Referenced by make_MOSTAverage_at_level().

Here is the caller graph for this function:

◆ set_rotated_fields()

void MOSTAverage::set_rotated_fields ( const int &  lev)

Update the rotated fields.

Parameters
[in]levlevel index

Function to set the rotated velocities.

Parameters
[in]levCurrent level
314 {
315  // Peel back the level
316  auto& fields = m_fields[lev];
317  auto& rot_fields = m_rot_fields[lev];
318  auto z_phys_nd = m_z_phys_nd[lev];
319 
320  // Inverse grid size
321  const auto dxInv = m_geom[lev].InvCellSizeArray();
322 
323  // Single MFIter over CC data
324  int imf_cc = 2;
325 
326  // Populate rotated U & V for terrain
327 #ifdef _OPENMP
328 #pragma omp parallel if (Gpu::notInLaunchRegion())
329 #endif
330  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
331  Box ubx = mfi.tilebox(IntVect(1,0,0));
332  Box vbx = mfi.tilebox(IntVect(0,1,0));
333 
334  const Array4<const Real>& z_phys_arr = z_phys_nd->const_array(mfi);
335 
336  const Array4<const Real>& u_arr = fields[0]->const_array(mfi);
337  const Array4<const Real>& v_arr = fields[1]->const_array(mfi);
338  const Array4<const Real>& w_arr = fields[5]->const_array(mfi);
339 
340  const Array4<Real>& u_rot_arr = rot_fields[0]->array(mfi);
341  const Array4<Real>& v_rot_arr = rot_fields[1]->array(mfi);
342 
343  // U rotated magnitude
344  ParallelFor(ubx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
345  {
346  // Elements of first tangent vector
347  Real met_h_xi = Compute_h_xi_AtIface(i,j,k,dxInv,z_phys_arr);
348  u_rot_arr(i,j,k) = (u_arr(i,j,k) + met_h_xi*w_arr(i,j,k))
349  / std::sqrt(met_h_xi*met_h_xi + one);
350  });
351 
352  // V rotated magnitude
353  ParallelFor(vbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
354  {
355  // Elements of second tangent vector
356  Real met_h_eta = Compute_h_eta_AtJface(i,j,k,dxInv,z_phys_arr);
357  v_rot_arr(i,j,k) = (v_arr(i,j,k) + met_h_eta*w_arr(i,j,k))
358  / std::sqrt(met_h_eta*met_h_eta + one);
359  });
360  }
361 
362  // Direct copy of other scalar variables
363  MultiFab::Copy(*rot_fields[2],*fields[2],0,0,1,rot_fields[2]->nGrowVect());
364  if (fields[3]) MultiFab::Copy(*rot_fields[3],*fields[3],0,0,1,rot_fields[3]->nGrowVect());
365 }
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_xi_AtIface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:117
AMREX_GPU_DEVICE AMREX_FORCE_INLINE amrex::Real Compute_h_eta_AtJface(const int &i, const int &j, const int &k, const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &cellSizeInv, const amrex::Array4< const amrex::Real > &z_nd)
Definition: ERF_TerrainMetrics.H:170

Referenced by compute_averages().

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

◆ set_z_positions_EB()

void MOSTAverage::set_z_positions_EB ( const int &  lev)

Populate embedded-boundary z positions.

Parameters
[in]levlevel index

Function to set K indices for EB.

Parameters
[in]levCurrent level
578 {
579  Real zref_tmp = zref_default;
580  ParmParse pp(m_pp_prefix);
581  auto read_z = pp.query("most.zref",zref_tmp);
582 
583  if (read_z) {
584  m_zref[lev]->setVal( zref_tmp );
585  // Default behavior is to use the first cell center
586  } else {
587  Real m_dz = m_geom[0].CellSize(2);
588  zref_tmp = myhalf * m_dz;
589  m_zref[lev]->setVal( zref_tmp );
590  Print() << "Reference height for MOST set to " << zref_tmp << std::endl;
591  }
592 }

Referenced by make_MOSTAverage_at_level().

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

◆ set_z_positions_T()

void MOSTAverage::set_z_positions_T ( const int &  lev)

Populate terrain-aware z positions.

Parameters
[in]levlevel index

Function to set positions with terrain and e_z vector (with interpolation but no terrain normals)

Parameters
[in]levCurrent level
771 {
772  // Peel back the level
773  auto& fields = m_fields[lev];
774 
775  // MFIter over CC data
776  int imf_cc = 2;
777 
778  ParmParse pp(m_pp_prefix);
779  Real zref_tmp = zref_default;
780  auto read_zref = pp.query("most.zref",zref_tmp);
781  if (!read_zref) {
782  Print() << "most.zref not specified, query distance default is " << zref_tmp << std::endl;
783  } else {
784  m_zref[lev]->setVal(zref_tmp);
785  }
786  int klo = m_geom[lev].Domain().smallEnd(2);
787 
788  // Capture for device
789  Real d_zref = zref_tmp;
790  const auto plo = m_geom[lev].ProbLoArray();
791 
792  RealVect base;
793  const auto dx = m_geom[lev].CellSizeArray();
794  IntVect ng = m_x_pos[lev]->nGrowVect(); ng[2]=0;
795  const int position_ng = (m_radius > 1) ? m_radius : 1;
796  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
797  Box npbx = mfi.tilebox(IntVect(1,1,0),IntVect(position_ng,position_ng,0));
798  Box gtbx = mfi.growntilebox(ng);
799 
800  if (npbx.smallEnd(2) != klo) { continue; }
801 
802  npbx.makeSlab(2,klo);
803 
804  RealBox grb{gtbx,dx.data(),base.dataPtr()};
805 
806  const auto z_phys_arr = m_z_phys_nd[lev]->const_array(mfi);
807  auto x_pos_arr = m_x_pos[lev]->array(mfi);
808  auto y_pos_arr = m_y_pos[lev]->array(mfi);
809  auto z_pos_arr = m_z_pos[lev]->array(mfi);
810  ParallelFor(npbx, [=] AMREX_GPU_DEVICE (int i, int j, int k) noexcept
811  {
812  // Final position at end of vector
813  x_pos_arr(i,j,0) = plo[0] + ((Real) i + myhalf) * dx[0];
814  y_pos_arr(i,j,0) = plo[1] + ((Real) j + myhalf) * dx[1];
815  Real z_bot_face = fourth * ( z_phys_arr(i ,j ,k) + z_phys_arr(i+1,j ,k)
816  + z_phys_arr(i ,j+1,k) + z_phys_arr(i+1,j+1,k) );
817  z_pos_arr(i,j,0) = z_bot_face + d_zref;
818 
819  // Destination position must be contained on the current process!
820  Real pos[] = {x_pos_arr(i,j,0)-plo[0],y_pos_arr(i,j,0)-plo[1],myhalf*dx[2]};
821  amrex::ignore_unused(pos);
822  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(grb.contains(&pos[0]),
823  "Query point outside of proc domain!");
824  });
825  }
826 }

Referenced by make_MOSTAverage_at_level().

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

◆ trilinear_interp_T()

AMREX_GPU_HOST_DEVICE static AMREX_INLINE void MOSTAverage::trilinear_interp_T ( const amrex::Real xp,
const amrex::Real yp,
const amrex::Real zp,
amrex::Real interp_vals,
amrex::Array4< amrex::Real const > const &  interp_array,
amrex::Array4< amrex::Real const > const &  z_arr,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &  plo,
const amrex::GpuArray< amrex::Real, AMREX_SPACEDIM > &  dxi,
const int  interp_comp 
)
inlinestatic

Function to compute trilinear interpolation with terrain.

Parameters
[in]xpX-position
[in]ypY-position
[in]zpZ-position
[out]interp_valsValues interpolated
[in]interp_arrayArray to interpolate on
[in]z_arrPhysical heights
[in]ploProblem lower bounds
[in]dxiInverse cell size array
[in]interp_compNumber of components to interpolate
274  {
275  // Search to get z/k
276  bool found = false;
277  int kmax = ubound(z_arr).z;
278  amrex::Real zval = zero;
279  amrex::Real z_target = zp;
280 
281  // Map position to i,j (must be same mapping in cpp file)
282  amrex::Real ireal = (xp - plo[0]) * dxi[0];
283  amrex::Real jreal = (yp - plo[1]) * dxi[1];
284  int i_new = (int) (ireal - myhalf);
285  int j_new = (int) (jreal - myhalf);
286 
287  for (int lk(0); lk<kmax; ++lk) {
288  amrex::Real z_lo = fourth * ( z_arr(i_new,j_new ,lk ) + z_arr(i_new+1,j_new ,lk )
289  + z_arr(i_new,j_new+1,lk ) + z_arr(i_new+1,j_new+1,lk ) );
290  amrex::Real z_hi = fourth * ( z_arr(i_new,j_new ,lk+1) + z_arr(i_new+1,j_new ,lk+1)
291  + z_arr(i_new,j_new+1,lk+1) + z_arr(i_new+1,j_new+1,lk+1) );
292  if (z_target > z_lo && z_target < z_hi){
293  found = true;
294  zval = (amrex::Real) lk + ((z_target - z_lo) / (z_hi - z_lo)) + myhalf;
295  break;
296  }
297  }
298 
299  amrex::ignore_unused(found);
300  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(found, "MOSTAverage: Height above terrain not found, try increasing z_ref!");
301 
302  // NOTE: This is the point ahead of the current i,j (e.g. i/j_new + 1)
303  const amrex::RealVect lx(ireal + myhalf, jreal + myhalf, zval);
304 
305  const amrex::IntVect ijk = lx.floor();
306 
307  int i = ijk[0]; int j = ijk[1]; int k = ijk[2];
308 
309  // Convert ijk (IntVect) to a RealVect explicitly
310  amrex::RealVect ijk_r(static_cast<amrex::Real>(ijk[0]),
311  static_cast<amrex::Real>(ijk[1]),
312  static_cast<amrex::Real>(ijk[2]));
313 
314  // Weights
315  const amrex::RealVect sx_hi = lx - ijk_r;
316  const amrex::RealVect sx_lo = one - sx_hi;
317 
318  for (int n = 0; n < interp_comp; n++) {
319  interp_vals[n] = sx_lo[0]*sx_lo[1]*sx_lo[2]*interp_array(i-1, j-1, k-1,n) +
320  sx_lo[0]*sx_lo[1]*sx_hi[2]*interp_array(i-1, j-1, k ,n) +
321  sx_lo[0]*sx_hi[1]*sx_lo[2]*interp_array(i-1, j , k-1,n) +
322  sx_lo[0]*sx_hi[1]*sx_hi[2]*interp_array(i-1, j , k ,n) +
323  sx_hi[0]*sx_lo[1]*sx_lo[2]*interp_array(i , j-1, k-1,n) +
324  sx_hi[0]*sx_lo[1]*sx_hi[2]*interp_array(i , j-1, k ,n) +
325  sx_hi[0]*sx_hi[1]*sx_lo[2]*interp_array(i , j , k-1,n) +
326  sx_hi[0]*sx_hi[1]*sx_hi[2]*interp_array(i , j , k ,n);
327  }
328  }

Referenced by compute_plane_averages(), and compute_region_averages().

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

◆ update_field_ptrs()

void MOSTAverage::update_field_ptrs ( const int &  lev,
amrex::Vector< amrex::Vector< amrex::MultiFab >> &  vars_old,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Theta_prim,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Qv_prim,
amrex::Vector< std::unique_ptr< amrex::MultiFab >> &  Qr_prim 
)

Reset pointers to field MultiFabs.

Parameters
[in]levlevel index
[in]vars_oldold-time state and velocity fields
[in]Theta_primprimitive potential-temperature fields by level
[in]Qv_primprimitive water-vapor fields by level
[in]Qr_primprimitive rain-water fields by level

Function to reset the pointers to field variables.

Parameters
[in]levCurrent level
[in]vars_oldConserved variables at each level
[in]Theta_primPrimitive theta component at each level
[in]Qv_primPrimitive water-vapor component at each level
[in]Qr_primPrimitive rain-water component at each level
298 {
299  m_fields[lev][0] = &vars_old[lev][Vars::xvel];
300  m_fields[lev][1] = &vars_old[lev][Vars::yvel];
301  m_fields[lev][2] = Theta_prim[lev].get();
302  m_fields[lev][3] = Qv_prim[lev].get();
303  m_fields[lev][4] = Qr_prim[lev].get();
304  m_fields[lev][5] = &vars_old[lev][Vars::zvel];
305 }

Referenced by SurfaceLayer::update_mac_ptrs().

Here is the caller graph for this function:

◆ write_averages()

void MOSTAverage::write_averages ( const int &  lev)

Write averages on the 2D MultiFab.

Parameters
[in]levlevel index

Function to write averages to text file.

Parameters
[in]levCurrent level
2065 {
2066  // Peel back the level
2067  auto& fields = m_fields[lev];
2068  auto& averages = m_averages[lev];
2069 
2070  // MFIter on CC
2071  int imf_cc = 2;
2072 
2073  int klo = m_geom[lev].Domain().smallEnd(2);
2074 
2075  int navg = m_navg - 1;
2076 
2077  std::ofstream ofile;
2078  ofile.open ("MOST_averages.txt");
2079  ofile << "Averages computed via MOSTAverages class:\n";
2080 
2081  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
2082  Box pbx = mfi.tilebox();
2083 
2084  if(pbx.smallEnd(2) != klo) { continue; }
2085 
2086  pbx.makeSlab(2,klo);
2087 
2088  int il = pbx.smallEnd(0); int iu = pbx.bigEnd(0);
2089  int jl = pbx.smallEnd(1); int ju = pbx.bigEnd(1);
2090 
2091  for (int j(jl); j <= ju; ++j) {
2092  for (int i(il); i <= iu; ++i) {
2093  ofile << "(I,J): " << "(" << i << "," << j << ")" << "\n";
2094  int k = 0;
2095  for (int iavg(0); iavg <= navg; ++iavg) {
2096  auto mf_arr = averages[iavg]->array(mfi);
2097  ofile << "iavg val: "
2098  << iavg << ' '
2099  << mf_arr(i,j,k) << "\n";
2100  }
2101  ofile << "\n";
2102  }
2103  }
2104  }
2105  ofile.close();
2106 }
Here is the call graph for this function:

◆ write_k_indices()

void MOSTAverage::write_k_indices ( const int &  lev)

Write k-index data.

Parameters
[in]levlevel index

Function to write the K indices to text file.

Parameters
[in]levCurrent level
1919 {
1920  // Peel back the level
1921  auto& fields = m_fields[lev];
1922  auto& k_indx = m_k_indx[lev];
1923 
1924  // MFIter on CC
1925  int imf_cc = 2;
1926 
1927  int klo = m_geom[lev].Domain().smallEnd(2);
1928 
1929  std::ofstream ofile;
1930  ofile.open ("MOST_k_indices.txt");
1931  ofile << "K indices used to compute averages via MOSTAverages class:\n";
1932 
1933  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
1934  Box pbx = mfi.tilebox();
1935 
1936  if(pbx.smallEnd(2) != klo) { continue; }
1937 
1938  pbx.makeSlab(2,klo);
1939 
1940  int il = pbx.smallEnd(0); int iu = pbx.bigEnd(0);
1941  int jl = pbx.smallEnd(1); int ju = pbx.bigEnd(1);
1942 
1943  auto k_arr = k_indx->array(mfi);
1944 
1945  for (int j(jl); j <= ju; ++j) {
1946  for (int i(il); i <= iu; ++i) {
1947  ofile << "(I,J): " << "(" << i << "," << j << ")" << "\n";
1948  int k = 0;
1949  ofile << "K_ind: "
1950  << k_arr(i,j,k) << "\n";
1951  ofile << "\n";
1952  }
1953  }
1954  }
1955  ofile.close();
1956 }
Here is the call graph for this function:

◆ write_norm_indices()

void MOSTAverage::write_norm_indices ( const int &  lev)

Write normal-index data.

Parameters
[in]levlevel index

Function to write I,J,K indices to text file.

Parameters
[in]levCurrent level
1966 {
1967  // Peel back the level
1968  auto& fields = m_fields[lev];
1969  auto& k_indx = m_k_indx[lev];
1970  auto& j_indx = m_j_indx[lev];
1971  auto& i_indx = m_i_indx[lev];
1972 
1973  // MFIter on CC
1974  int imf_cc = 2;
1975 
1976  int klo = m_geom[lev].Domain().smallEnd(2);
1977 
1978  std::ofstream ofile;
1979  ofile.open ("MOST_ijk_indices.txt");
1980  ofile << "IJK indices used to compute averages via MOSTAverages class:\n";
1981 
1982  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
1983  Box pbx = mfi.tilebox();
1984 
1985  if(pbx.smallEnd(2) != klo) { continue; }
1986 
1987  pbx.makeSlab(2,klo);
1988 
1989  int il = pbx.smallEnd(0); int iu = pbx.bigEnd(0);
1990  int jl = pbx.smallEnd(1); int ju = pbx.bigEnd(1);
1991 
1992  auto k_arr = k_indx->array(mfi);
1993  auto j_arr = j_indx ? j_indx->array(mfi) : Array4<int> {};
1994  auto i_arr = i_indx ? i_indx->array(mfi) : Array4<int> {};
1995 
1996  for (int j(jl); j <= ju; ++j) {
1997  for (int i(il); i <= iu; ++i) {
1998  ofile << "(I1,J1,K1): " << "(" << i << "," << j << "," << 0 << ")" << "\n";
1999 
2000  int k = 0;
2001  int km = k_arr(i,j,k);
2002  int jm = j_arr ? j_arr(i,j,k) : j;
2003  int im = i_arr ? i_arr(i,j,k) : i;
2004 
2005  ofile << "(I2,J2,K2): "
2006  << "(" << im << "," << jm << "," << km << ")" << "\n";
2007  ofile << "\n";
2008  }
2009  }
2010  }
2011  ofile.close();
2012 }
Here is the call graph for this function:

◆ write_xz_positions()

void MOSTAverage::write_xz_positions ( const int &  lev,
const int &  j 
)

Write XZ planar positions.

Parameters
[in]levlevel index
[in]jy-index of the plane to write

Function to write X & Z positions to text file.

Parameters
[in]levCurrent level
[in]jIndex in y-dir
2024 {
2025  // Peel back the level
2026  auto& fields = m_fields[lev];
2027  auto& x_pos_mf = m_x_pos[lev];
2028  auto& z_pos_mf = m_z_pos[lev];
2029 
2030  // MFIter on CC
2031  int imf_cc = 2;
2032 
2033  int klo = m_geom[lev].Domain().smallEnd(2);
2034 
2035  std::ofstream ofile;
2036  ofile.open ("MOST_xz_positions.txt");
2037 
2038  for (MFIter mfi(*fields[imf_cc], TileNoZ()); mfi.isValid(); ++mfi) {
2039  Box pbx = mfi.tilebox();
2040 
2041  if(pbx.smallEnd(2) != klo) { continue; }
2042 
2043  pbx.makeSlab(2,klo);
2044 
2045  int il = pbx.smallEnd(0); int iu = pbx.bigEnd(0);
2046 
2047  auto x_pos_arr = x_pos_mf->array(mfi);
2048  auto z_pos_arr = z_pos_mf->array(mfi);
2049 
2050  int k = 0;
2051  for (int i(il); i <= iu; ++i)
2052  ofile << x_pos_arr(i,j,k) << ' ' << z_pos_arr(i,j,k) << "\n";
2053  }
2054  ofile.close();
2055 }
Here is the call graph for this function:

Member Data Documentation

◆ include_subgrid_vel

bool MOSTAverage::include_subgrid_vel = false
protected

◆ m_averages

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > MOSTAverage::m_averages
protected

◆ m_eb_vec

amrex::Vector<const eb_*> MOSTAverage::m_eb_vec
protected

◆ m_fact_new

◆ m_fact_old

◆ m_fields

◆ m_geom

◆ m_i_indx

amrex::Vector<std::unique_ptr<amrex::iMultiFab> > MOSTAverage::m_i_indx
protected

◆ m_interp

bool MOSTAverage::m_interp {false}
protected

◆ m_j_indx

amrex::Vector<std::unique_ptr<amrex::iMultiFab> > MOSTAverage::m_j_indx
protected

◆ m_k_in

amrex::Vector<int> MOSTAverage::m_k_in
protected

Referenced by set_k_indices_N(), and set_k_indices_T().

◆ m_k_indx

◆ m_maxlev

int MOSTAverage::m_maxlev {0}
protected

◆ m_mesh_type

MeshType MOSTAverage::m_mesh_type
protected

◆ m_navg

◆ m_ncell_plane

amrex::Vector<amrex::Vector<int> > MOSTAverage::m_ncell_plane
protected

◆ m_ncell_region

int MOSTAverage::m_ncell_region {1}
protected

◆ m_norm_vec

bool MOSTAverage::m_norm_vec {false}
protected

◆ m_nvar

int MOSTAverage::m_nvar {6}
protected

◆ m_plane_average

amrex::Vector<amrex::Vector<amrex::Real> > MOSTAverage::m_plane_average
protected

◆ m_policy

int MOSTAverage::m_policy {0}
protected

◆ m_pp_prefix

std::string MOSTAverage::m_pp_prefix
protected

◆ m_radius

◆ m_rot_fields

amrex::Vector<amrex::Vector<std::unique_ptr<amrex::MultiFab> > > MOSTAverage::m_rot_fields
protected

◆ m_rotate

bool MOSTAverage::m_rotate {false}
protected

◆ m_t_avg

◆ m_t_init

amrex::Vector<int> MOSTAverage::m_t_init
protected

◆ m_terrain_type

TerrainType MOSTAverage::m_terrain_type
protected

◆ m_time_window

double MOSTAverage::m_time_window {1.0e-16}
protected

◆ m_total_bndry_area

amrex::Vector<amrex::Vector<amrex::Real> > MOSTAverage::m_total_bndry_area
protected

◆ m_Vsg

amrex::Vector<amrex::Real> MOSTAverage::m_Vsg
protected

◆ m_x_pos

amrex::Vector<std::unique_ptr<amrex::MultiFab> > MOSTAverage::m_x_pos
protected

◆ m_y_pos

amrex::Vector<std::unique_ptr<amrex::MultiFab> > MOSTAverage::m_y_pos
protected

◆ m_z_phys_nd

◆ m_z_pos

amrex::Vector<std::unique_ptr<amrex::MultiFab> > MOSTAverage::m_z_pos
protected

◆ m_zref

amrex::Vector<std::unique_ptr<amrex::MultiFab> > MOSTAverage::m_zref
protected

◆ zref_default


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