Chombo-3 basics

To fully understand this documentation the user should be familiar with Chombo. This documentation uses class names from Chombo and the most relevant Chombo data structures are summarized here. What follows is a very brief introduction to these data structures, for in-depth explanations please see the Chombo manual.

Real

Real is a typedef’ed structure for holding a single floating point number. Compiling with double precision will typedef Real as double, otherwise it is typedef’ed as float.

RealVect

RealVect is a spatial vector. It holds two Real components in 2D and three Real components in 3D. The RealVect class has floating point arithmetic, e.g. addition, subtraction, multiplication etc.

Most of chombo-discharge is written in dimension-independent code, and for cases where RealVect is initialized with components the constructor uses Chombo macros for expanding the correct number of arguments. For example

RealVect v(D_DECL(vx, vy, vz));

will expand to RealVect v(vx,vy) in 2D and RealVect v(vx, vy, vz) in 3D.

IntVect

IntVect is an integer spatial vector, and is used for indexing data structures. It works in much the same way as RealVect, except that the components are integers.

Box

The Box object describes a box in Cartesian space. The boxes are indexed by the low and high corners, both of which are an IntVect. The Box may be cell-centered or face-centered. To turn a cell-centered Box into a face-centered box one would do

Box bx(IntVect::Zero, IntVect::Unit); // Default constructor give cell centered boxes
bx.surroundingNodes():                // Now a cell-centered box

This will increase the box dimensions by one in each coordinate direction.

EBCellFAB and FArrayBox

The EBCellFAB object is an array for holding cell-centered data in an embedded boundary context. The EBCellFAB has two data structures: An FArrayBox that holds the data on the cell centers, and a additional data structure that holds data in cells that are multiply cut. Doing arithmetic with EBCellFAB usually requires one to iterate over all the cell in the FArrayBox, and then to iterate over the irregular cells (i.e. cut-cells) later. A VoFIterator is such as object; it can iterate over cut-cells. Usually, code for doing anything with the EBCellFAB looks like this:

// Call Fortran code
FORT_DO_SOMETHING(....)

// Iterate over cut-cells
for (VoFIterator vofit(...); vofit.ok(); ++vofit){
   (...)
}

Important

The FArrayBox stores the data in column major order.

Vector

Vector<T> is a one-dimensional array with constant-time random access and range checking. It uses std::vector under the hood and can access the most commonly used std::vector functionality through the public member functions. E.g. to obtain an element in the vector

Vector<T> my_vector(10, T());

T& element = my_vector[5];

Likewise, push_back, resize etc works in much the same way as for std::vector.

RefCountedPtr

RefCountedPtr<T> is a pointer class in Chombo with reference counting. That is, when objects that hold a reference to some RefCountedPtr<T> object goes out of scope the reference counter is decremented. If the reference counter reaches zero, the object that RefCountedPtr<T> points to it deallocated. Using RefCountedPtr<T> is much preferred over using a raw pointer T* to 1) avoid memory leaks and 2) compress code since no explicit deallocations need to be called.

In modern C++-speak, RefCountedPtr<T> can be thought of as a very simple version of std::shared_ptr<T>.

DisjointBoxLayout

The DisjointBoxLayout class describes a grid on an AMR level where all the boxes are disjoint, i.e. they don’t overlap. DisjointBoxLayout is built upon a union of non-overlapping boxes having the same grid resolution and with unique rank-to-box ownership. The constructor is

Vector<Box> boxes(...);  // Vector of disjoint boxes
Vector<int> ranks(...);  // Ownership of each box

DisjointBoxLayout dbl(boxes, ranks);

In simple terms, DisjointBoxLayout is the decomposed grid on each level in which MPI ranks have unique ownership of specific parts of the grid.

The DisjointBoxLayout view is global, i.e. each MPI rank knows about all the boxes and the box ownership on the entire AMR level. However, ranks will only allocate data on the part of the grid that they own. Data iterators also exist, and the most common is to use iterators that only iterate over the part of the DisjointBoxLayout that the specific MPI ranks own:

DisjointBoxLayout dbl;
for (DataIterator dit(dbl); dit.ok(); ++dit){
   // Do something
}

Each MPI rank will then iterate only over the part of the grid where it has ownership.

Other data iterators exist that iterate over all boxes in the grid:

for (LayoutIterator lit = dbl.layoutIterator(); dit.ok(); ++dit){
   // Do something
}

This is typically used if one wants to do some global operation, e.g. count the number of cells in the grid. However, trying to use LayoutIterator to retrieve data that was allocated locally on a different MPI rank is an error.

LevelData

The LevelData<T> template structure holds data on all the grid patches of one AMR level. The data is distributed with the domain decomposition specified by DisjointBoxLayout, and each patch contains exactly one instance of T. LevelData<T> uses a factory pattern for creating the T objects, so if you have new data structures that should fit the in LevelData<T> structure you must also implement a factory method for T.

The LevelData<T> object provides the domain decomposition method in Chombo and chombo-discharge. Often, T is an EBCellFAB, i.e. a Cartesian grid patch that also supports EB formulations.

To iterate over LevelData<T> one will use the data iterator above:

LevelData<T> myData;
for (DataIterator dit(dbl); dit.ok(); ++dit){
   T& = myData[dit()];
}

LevelData<T> also includes the concept of ghost cells and exchange operations.

EBISLayout and EBISBox

The EBISLayout holds the geometric information over one DisjointBoxLayout level. Typically, the EBISLayout is used for fetching the geometric moments that are required for performing computations near cut-cells. EBISLayout can be thought of as an object which provides all EB-related information on a specific grid level. The EB information consists of e.g. cell flags (i.e., is the cell a cut-cell?), volume fractions, etc. This information is stored in a class EBISBox, which holds all the EB information for one specific grid patch. To obtain the EB-information for a specific grid patch, one will call:

EBISLayout ebisl;
for (DataIterator dit(dbl); dit.ok(); ++dit){
   EBISBox& ebisbox = ebisl[dit()];
}

where EBISBox contains the geometric information over only one grid patch. One can thus think of the EBISLayout as a LevelData<EBISBox> structure.

As an example, to iterate over all the cut-cells defined for a cell-centered data holder an AMR-level one would do:

constexpr int comp = 0;

// Assume that these exist.
LevelData<EBCellFAB> myData;
EBISLayout ebisl;

// Iterate over all the patches on a grid level.
for (DataIterator dit(dbl); ++dit){
   const Box  cellBox   = dbl[dit()];
   EBCellFAB& patchData = myData[dit()];
   EBISBox&   ebisbox   = ebisl [dit()];

   // Get all the cut-cells in the grid patch
   const IntVectSet& ivs = ebisbox.getIrregIVS(cellBox);
   const EBGraph&        = ebisbox.getEBGraph();

   // Define a VoFIterator for the cut-cells and iterate over all the cut-cells.
   for (VoFIterator vofit(ivs, ebgraph); vofit.ok(); ++vofit){
      const VolIndex& vof = vofit();

      patchData(vof, comp) = ...
   }
}

Here, EBGraph is the graph that describes the connectivity of the cut cells.

BaseIF

The BaseIF is a Chombo class which encapsulates an implicit function (recall that all SDFs are also implicit functions, see Geometry representation). BaseIF is therefore used for fundamentally constructing a geometric object. Many examples of BaseIF are found in Chombo itself, and chombo-discharge includes additional ones.

To implement a new implicit function, the user must inherit from BaseIF and implement the pure function

virtual Real BaseIF::value(const RealVect& a_point) const = 0;

The implemention should return a positive value if the point a_point is inside the object and a negative value otherwise.