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

#include <ERF_PlanarBoundary.H>

Collaboration diagram for PlanarBoundary:

Classes

struct  Buffer
 

Public Member Functions

void define (const amrex::BoxArray &ba3d, const amrex::BoxArray &ba2d, const amrex::DistributionMapping &dm, int surface_index, bool is_low=true, int normal_dir=2)
 
void fill (amrex::MultiFab &mf, const amrex::Periodicity &period)
 
void gather_surface (const amrex::MultiFab &mf, amrex::MultiFab &dst, int scomp, int dcomp, int ncomp) const
 
const amrex::BoxArray & surface_boxes () const
 Cell-centered surface copies of the planar boxes, without duplicates. More...
 
const amrex::DistributionMapping & surface_dm () const
 Ranks owning the surface copies: each is on the rank of the planar box it is a copy of. More...
 
const amrex::Vector< int > & surface_index () const
 Index of each surface copy in the planar BoxArray. More...
 
AMREX_GPU_HOST bool is_surface_copy (const int planar_index) const
 Whether a planar-box index is the computed copy that touches the surface. More...
 

Private Member Functions

amrex::MultiFab & buffer (const amrex::MultiFab &mf)
 

Private Attributes

int m_nplanar {0}
 
amrex::BoxArray m_ba_sfc
 
amrex::DistributionMapping m_dm_sfc
 
amrex::Vector< int > m_src_index
 
amrex::Vector< Bufferm_buffers
 

Detailed Description

Ghost-cell fill for planar (2D) MultiFabs built on the collapse of a 3D BoxArray along a surface-normal direction.

The surface-layer fields and the MOST averages are defined on one 2D box per 3D box. When the 3D BoxArray is split in the normal direction, several 3D boxes collapse onto the same 2D box, so the planar BoxArray holds duplicate boxes with overlapping valid regions, and only the copy that belongs to the 3D box touching the surface is ever computed. A FillBoundary on such a MultiFab may fill a ghost cell from an uncomputed copy.

define() records which planar boxes are those surface copies. fill() copies them into a buffer without duplicates and ParallelCopies the buffer onto every planar box, valid region and ghost cells alike. Unlike FillBoundary it therefore also overwrites the valid region of the uncomputed copies, so that any copy can be read afterwards. Without duplicates fill() is a plain FillBoundary.

The buffer is kept for each target layout, index type, and number of components, so only the first fill of each kind allocates. For a face-centered MultiFab (the MOST velocity averages) neighbouring surface boxes share a face in the buffer. Both should hold the same value there, but nothing enforces it, so fill() gives the shared faces a single value before it copies: the lowest global box index wins, which does not depend on the decomposition or the rank count, and every copy of the field then agrees.

A level none of whose boxes reaches the surface (a fine level away from the surface) has no computed copy to fill from, and fill() leaves the MultiFab as it is.

Member Function Documentation

◆ buffer()

MultiFab & PlanarBoundary::buffer ( const amrex::MultiFab &  mf)
private

Gather buffer for one target layout, index type, and number of components, allocated on first use. The source MultiFab list avoids rebuilding the layout key on every fill.

Parameters
[in]mfplanar MultiFab to buffer
136 {
137  const IndexType ixtype = mf.ixType();
138  const int ncomp = mf.nComp();
139 
140  // define() clears m_buffers whenever the underlying layout changes, so a
141  // source pointer is a stable per-field cache key for the lifetime of this
142  // PlanarBoundary definition.
143  for (auto& b : m_buffers) {
144  if (b.ixtype == ixtype && b.ncomp == ncomp &&
145  std::find(b.sources.begin(), b.sources.end(), &mf) != b.sources.end()) {
146  return *b.mf;
147  }
148  }
149 
150  // Only an unfamiliar source field needs the more expensive derived-layout
151  // construction below. Fields with the same layout continue to share one
152  // gather buffer.
153  BoxList bl_sfc(ixtype);
154  Vector<int> pmap;
155  for (int src : m_src_index) {
156  bl_sfc.push_back(mf.boxArray()[src]);
157  pmap.push_back(mf.DistributionMap()[src]);
158  }
159  BoxArray ba(std::move(bl_sfc));
160  DistributionMapping dm(std::move(pmap));
161 
162  for (auto& b : m_buffers) {
163  if (b.ixtype == ixtype && b.ncomp == ncomp && b.ba == ba && b.dm == dm) {
164  b.sources.push_back(&mf);
165  return *b.mf;
166  }
167  }
168  m_buffers.push_back(Buffer{ixtype, ncomp, ba, dm, {&mf},
169  std::make_unique<MultiFab>(ba, dm, ncomp, 0)});
170  return *m_buffers.back().mf;
171 }
amrex::Vector< int > m_src_index
Definition: ERF_PlanarBoundary.H:122
amrex::Vector< Buffer > m_buffers
Definition: ERF_PlanarBoundary.H:123

◆ define()

void PlanarBoundary::define ( const amrex::BoxArray &  ba3d,
const amrex::BoxArray &  ba2d,
const amrex::DistributionMapping &  dm,
int  surface_index,
bool  is_low = true,
int  normal_dir = 2 
)

Record the surface copies of a planar BoxArray.

Parameters
[in]ba3d3D BoxArray the planar BoxArray was collapsed from
[in]ba2dplanar BoxArray, one box per box of ba3d and in the same order
[in]dmDistributionMapping shared by ba3d and ba2d
[in]surface_indexindex of the surface cell in the normal direction
[in]is_lowwhether the surface is the low side of the domain
[in]normal_dirnormal direction of the surface

Record the surface copies of a planar BoxArray (see ERF_PlanarBoundary.H).

Parameters
[in]ba3d3D BoxArray the planar BoxArray was collapsed from
[in]ba2dplanar BoxArray, one box per box of ba3d and in the same order
[in]dmDistributionMapping shared by ba3d and ba2d
[in]surface_indexindex of the surface cell in the normal direction
[in]is_lowwhether the surface is the low side of the domain
[in]normal_dirnormal direction of the surface
27 {
28  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(ba2d.size() == ba3d.size(),
29  "PlanarBoundary::define: the planar BoxArray must hold one box per 3D box");
30 
31  m_nplanar = static_cast<int>(ba2d.size());
32  m_src_index.clear();
33  m_buffers.clear();
34 
35  // The planar boxes are taken as they are, whatever index they were collapsed to
36  BoxList bl_sfc(IndexType::TheCellType());
37  Vector<int> pmap;
38  for (int ib = 0; ib < m_nplanar; ++ib) {
39  const bool touches_surface = is_low
40  ? (ba3d[ib].smallEnd(normal_dir) == surface_index)
41  : (ba3d[ib].bigEnd(normal_dir) == surface_index);
42  if (touches_surface) {
43  bl_sfc.push_back(enclosedCells(ba2d[ib]));
44  pmap.push_back(dm[ib]);
45  m_src_index.push_back(ib);
46  }
47  }
48  m_ba_sfc = BoxArray(std::move(bl_sfc));
49  m_dm_sfc = DistributionMapping(std::move(pmap));
50 
51  // Exactly one computed copy per column: the surface boxes must not overlap
53  "PlanarBoundary::define: the boxes touching the surface overlap in the plane, so a "
54  "planar field would have more than one computed copy per column");
55 }
AMREX_ALWAYS_ASSERT_WITH_MESSAGE(m_cloud_chamber_config.active, "Cloud Chamber: initializer reached without a parsed configuration")
amrex::DistributionMapping m_dm_sfc
Definition: ERF_PlanarBoundary.H:121
amrex::BoxArray m_ba_sfc
Definition: ERF_PlanarBoundary.H:120
int m_nplanar
Definition: ERF_PlanarBoundary.H:119
const amrex::Vector< int > & surface_index() const
Index of each surface copy in the planar BoxArray.
Definition: ERF_PlanarBoundary.H:97
Here is the call graph for this function:

◆ fill()

void PlanarBoundary::fill ( amrex::MultiFab &  mf,
const amrex::Periodicity &  period 
)

Fill every copy of a planar MultiFab, valid region and ghost cells, from the surface copies.

Parameters
[in,out]mfplanar MultiFab on ba2d, of any index type and number of components
[in]periodperiodicity of the level

Fill every copy of a planar MultiFab, valid region and ghost cells, from the surface copies (see ERF_PlanarBoundary.H).

Parameters
[in,out]mfplanar MultiFab on the planar BoxArray given to define
[in]periodperiodicity of the level
66 {
67  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(mf.size()) == m_nplanar,
68  "PlanarBoundary::fill: the MultiFab is not on the planar BoxArray given to define");
69 
70  const int nsfc = static_cast<int>(m_src_index.size());
71 
72  // Every planar box is a surface box: no duplicates, FillBoundary is well defined
73  if (nsfc == m_nplanar) {
74  mf.FillBoundary(period);
75  return;
76  }
77 
78  // No box on this level reaches the surface, so no copy holds computed data to fill
79  // from; leave the MultiFab as it is
80  if (nsfc == 0) { return; }
81 
82  const int ncomp = mf.nComp();
83  MultiFab& buf = buffer(mf);
84  gather_surface(mf, buf, 0, 0, ncomp);
85 
86  // A face-centered buffer's boxes share a face with their neighbours, and the gather above
87  // takes each box's face from its own surface copy, so two boxes can hold different values
88  // there. Give every shared face one value before the ParallelCopy -- OverrideSync's
89  // precedence is the lowest global box index, so the result does not depend on the
90  // decomposition or the rank count -- and every copy of mf then ends up with the same
91  // value. Returns immediately for cell-centered data, which has no shared faces.
92  buf.OverrideSync(period);
93 
94  // Fill every copy, valid region and ghost cells, from the computed surface copies
95  mf.ParallelCopy(buf, 0, 0, ncomp, IntVect(0), mf.nGrowVect(), period);
96 }
amrex::MultiFab & buffer(const amrex::MultiFab &mf)
Definition: ERF_PlanarBoundary.cpp:135
void gather_surface(const amrex::MultiFab &mf, amrex::MultiFab &dst, int scomp, int dcomp, int ncomp) const
Definition: ERF_PlanarBoundary.cpp:109
Here is the call graph for this function:

◆ gather_surface()

void PlanarBoundary::gather_surface ( const amrex::MultiFab &  mf,
amrex::MultiFab &  dst,
int  scomp,
int  dcomp,
int  ncomp 
) const

Copy the computed surface copies of a planar MultiFab into a MultiFab without duplicates.

define() gives each surface copy the rank of the planar box it is a copy of, so every box of dst is filled from a FAB of mf that lives on the same rank and the gather needs no communication. Reading the surface copies directly, rather than any copy of the field, means the caller need not have filled the duplicates with fill().

Parameters
[in]mfplanar MultiFab on the planar BoxArray given to define
[out]dstMultiFab on surface_boxes() converted to the index type of mf, and on surface_dm()
[in]scompfirst component to read from mf
[in]dcompfirst component to write in dst
[in]ncompnumber of components

Copy the computed surface copies of a planar MultiFab into a MultiFab without duplicates (see ERF_PlanarBoundary.H).

Parameters
[in]mfplanar MultiFab on the planar BoxArray given to define
[out]dstMultiFab on the surface boxes, in the index type of mf
[in]scompfirst component to read from mf
[in]dcompfirst component to write in dst
[in]ncompnumber of components
111 {
112  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(static_cast<int>(mf.size()) == m_nplanar,
113  "PlanarBoundary::gather_surface: the MultiFab is not on the planar BoxArray given to define");
114  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(dst.size() == static_cast<Long>(m_src_index.size()),
115  "PlanarBoundary::gather_surface: the destination is not on the surface boxes");
116 
117  for (MFIter mfi(dst); mfi.isValid(); ++mfi) {
118  const Box& bx = mfi.validbox();
119  const int src = m_src_index[mfi.index()];
120  // The surface copy must be the planar box with the same footprint, on this rank
121  AMREX_ALWAYS_ASSERT_WITH_MESSAGE(bx == mf.boxArray()[src] &&
122  mf.DistributionMap()[src] == ParallelDescriptor::MyProc(),
123  "PlanarBoundary::gather_surface: surface box does not match its planar box");
124  dst[mfi].copy<RunOn::Device>(mf[src], bx, scomp, bx, dcomp, ncomp);
125  }
126 }

Referenced by SurfaceLayer::surface_sum().

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

◆ is_surface_copy()

AMREX_GPU_HOST bool PlanarBoundary::is_surface_copy ( const int  planar_index) const
inline

Whether a planar-box index is the computed copy that touches the surface.

102  {
103  return std::binary_search(m_src_index.begin(), m_src_index.end(), planar_index);
104  }

◆ surface_boxes()

const amrex::BoxArray& PlanarBoundary::surface_boxes ( ) const
inline

Cell-centered surface copies of the planar boxes, without duplicates.

91 { return m_ba_sfc; }

Referenced by SurfaceLayer::surface_sum().

Here is the caller graph for this function:

◆ surface_dm()

const amrex::DistributionMapping& PlanarBoundary::surface_dm ( ) const
inline

Ranks owning the surface copies: each is on the rank of the planar box it is a copy of.

94 { return m_dm_sfc; }

Referenced by SurfaceLayer::surface_sum().

Here is the caller graph for this function:

◆ surface_index()

const amrex::Vector<int>& PlanarBoundary::surface_index ( ) const
inline

Index of each surface copy in the planar BoxArray.

97 { return m_src_index; }

Member Data Documentation

◆ m_ba_sfc

amrex::BoxArray PlanarBoundary::m_ba_sfc
private

Referenced by surface_boxes().

◆ m_buffers

amrex::Vector<Buffer> PlanarBoundary::m_buffers
private

◆ m_dm_sfc

amrex::DistributionMapping PlanarBoundary::m_dm_sfc
private

Referenced by surface_dm().

◆ m_nplanar

int PlanarBoundary::m_nplanar {0}
private

◆ m_src_index

amrex::Vector<int> PlanarBoundary::m_src_index
private

Referenced by is_surface_copy(), and surface_index().


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