Skip to content

Commit

Permalink
New function for domain decomposition (AMReX-Codes#4183)
Browse files Browse the repository at this point in the history
Add a new function that decomposition a Box into BoxArray. The number of
Boxes in the returned BoxArray matches the given nboxes argument, unless
the domain is too small. We aim to decompose the domain into subdomains
that are as cubic as possible, even if this results in Boxes with odd
numbers of cells. Thus, this function is generally not suited for
applications with multiple AMR levels or for multigrid solvers. However,
it could be useful for single-level applications that prefer exactly one
Box per process.
  • Loading branch information
WeiqunZhang authored Oct 5, 2024
1 parent 49245d6 commit 6239d25
Show file tree
Hide file tree
Showing 2 changed files with 188 additions and 0 deletions.
18 changes: 18 additions & 0 deletions Src/Base/AMReX_BoxArray.H
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,24 @@ namespace amrex
//! Note that two BoxArrays that match are not necessarily equal.
[[nodiscard]] bool match (const BoxArray& x, const BoxArray& y);

/**
* \brief Decompose domain box into BoxArray
*
* The returned BoxArray has nboxes Boxes, unless the the domain is too
* small. We aim to decompose the domain into subdomains that are as
* cubic as possible, even if this results in Boxes with odd numbers of
* cells. Thus, this function is generally not suited for applications
* with multiple AMR levels or for multigrid solvers.
*
* \param domain Domain Box
* \param nboxes the target number of Boxes
* \param decomp controls whether domain decomposition should be done in
* that direction.
*/
[[nodiscard]] BoxArray decompose (Box const& domain, int nboxes,
Array<bool,AMREX_SPACEDIM> const& decomp
= {AMREX_D_DECL(true,true,true)});

struct BARef
{
BARef ();
Expand Down
170 changes: 170 additions & 0 deletions Src/Base/AMReX_BoxArray.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@

#include <AMReX_OpenMP.H>

#include <algorithm>
#include <cstdlib>
#include <functional>
#include <iostream>

namespace amrex {
Expand Down Expand Up @@ -1887,6 +1890,173 @@ bool match (const BoxArray& x, const BoxArray& y)
}
}

BoxArray decompose (Box const& domain, int nboxes,
Array<bool,AMREX_SPACEDIM> const& decomp)
{
auto ndecomp = std::count(decomp.begin(), decomp.end(), true);

if (nboxes <= 1 || ndecomp == 0) {
return BoxArray(domain);
}

Box const& ccdomain = amrex::enclosedCells(domain);
IntVect const& ncells = ccdomain.length();
IntVect nprocs(1);

if (ndecomp == 1) {
for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
if (decomp[idim]) {
nprocs[idim] = nboxes;
}
}
} else {
// Factorization of nboxes
Vector<int> factors;
{
int x = 2;
int n = nboxes;
while (x*x <= n) {
std::div_t dv = std::div(n, x);
if (dv.rem == 0) {
factors.push_back(x);
n = dv.quot;
} else {
++x;
}
}
if (n != 1) {
factors.push_back(n);
}
AMREX_ALWAYS_ASSERT(nboxes == std::accumulate(factors.begin(), factors.end(),
1, std::multiplies<>()));
}

struct ProcDim
{
int nproc;
int idim;
Vector<int> procs;
ProcDim (int np, int dim) : nproc(np), idim(dim) {}
};

Vector<ProcDim> procdim;
procdim.reserve(AMREX_SPACEDIM);

Array<Long,AMREX_SPACEDIM> nblocks;

for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
if (decomp[idim]) {
nblocks[idim] = ncells[idim];
procdim.emplace_back(1,idim);
} else {
nblocks[idim] = 0; // This dimension will not be decomposed.
}
}

auto comp = [&] (ProcDim const& a, ProcDim const& b) {
if (nblocks[a.idim]*b.nproc <
nblocks[b.idim]*a.nproc) {
return true;
} else if (nblocks[a.idim]*b.nproc >
nblocks[b.idim]*a.nproc) {
return false;
} else {
return a.procs.size() > b.procs.size();
}
};

int nprocs_tot = 1;
while (!factors.empty()) {
std::sort(procdim.begin(), procdim.end(), comp);
auto f = factors.back();
factors.pop_back();
procdim.back().nproc *= f;
procdim.back().procs.push_back(f);
nprocs_tot *= f;
if (nprocs_tot == nboxes) {
break;
}
}

// swap to see if the decomposition can be improved.
while (true)
{
std::sort(procdim.begin(), procdim.end(), comp);
auto fit = std::find_if(procdim.begin(),procdim.end(),
[] (ProcDim const& x) { return x.nproc > 1; });
if (fit == procdim.end()) { break; } // This should not actually happen.
auto& light = *fit;
auto& heavy = procdim.back();
Long w0 = nblocks[light.idim] * heavy.nproc;
Long w1 = nblocks[heavy.idim] * light.nproc;
if (w0 >= w1) { break; }
bool swapped = false;
for (auto& f0 : light.procs) {
for (auto& f1 : heavy.procs) {
if ((f0 > f1) && (w0*f0 < w1*f1)) {
light.nproc /= f0;
light.nproc *= f1;
heavy.nproc /= f1;
heavy.nproc *= f0;
std::swap(f0,f1);
swapped = true;
break;
}
}
if (swapped) { break;}
}
if (!swapped) { break; }
}

for (int idim = 0; idim < AMREX_SPACEDIM; ++idim) {
if (!decomp[idim]) {
procdim.emplace_back(1,idim);
}
}
for (auto const& pd : procdim) {
nprocs[pd.idim] = pd.nproc;
}
}

AMREX_ALWAYS_ASSERT(AMREX_D_TERM(nprocs[0],*nprocs[1],*nprocs[2]) == nboxes);

IntVect const domlo = ccdomain.smallEnd();
IntVect const sz = ncells / nprocs;
IntVect const extra = ncells - sz*nprocs;
auto ixtyp = domain.ixType();
BoxList bl(ixtyp);
#if (AMREX_SPACEDIM == 3)
for (int k = 0; k < nprocs[2]; ++k) {
// The first extra[2] blocks get one extra cell with a total of
// sz[2]+1. The rest get sz[2] cells. The decomposition in y
// and x directions are similar.
int klo = (k < extra[2]) ? k*(sz[2]+1) : (k*sz[2]+extra[2]);
int khi = (k < extra[2]) ? klo+(sz[2]+1)-1 : klo+sz[2]-1;
klo += domlo[2];
khi += domlo[2];
#endif
#if (AMREX_SPACEDIM >= 2)
for (int j = 0; j < nprocs[1]; ++j) {
int jlo = (j < extra[1]) ? j*(sz[1]+1) : (j*sz[1]+extra[1]);
int jhi = (j < extra[1]) ? jlo+(sz[1]+1)-1 : jlo+sz[1]-1;
jlo += domlo[1];
jhi += domlo[1];
#endif
for (int i = 0; i < nprocs[0]; ++i) {
int ilo = (i < extra[0]) ? i*(sz[0]+1) : (i*sz[0]+extra[0]);
int ihi = (i < extra[0]) ? ilo+(sz[0]+1)-1 : ilo+sz[0]-1;
ilo += domlo[0];
ihi += domlo[0];
Box b{IntVect(AMREX_D_DECL(ilo,jlo,klo)),
IntVect(AMREX_D_DECL(ihi,jhi,khi))};
if (b.ok()) {
bl.push_back(b.convert(ixtyp));
}
AMREX_D_TERM(},},})

return BoxArray(std::move(bl));
}

std::ostream&
operator<< (std::ostream& os, const BoxArray::RefID& id)
{
Expand Down

0 comments on commit 6239d25

Please sign in to comment.