Program Listing for File grid.cpp
↰ Return to documentation for file (grid.cpp
)
// ***********************************************************************************
// Idefix MHD astrophysical code
// Copyright(C) 2020-2022 Geoffroy R. J. Lesur <geoffroy.lesur@univ-grenoble-alpes.fr>
// and other code contributors
// Licensed under CeCILL 2.1 License, see COPYING for more information
// ***********************************************************************************
#include <string>
#include "idefix.hpp"
#include "gridHost.hpp"
#include "grid.hpp"
Grid::Grid(Input &input) {
idfx::pushRegion("Grid::Grid(Input)");
xbeg = std::vector<real>(3);
xend = std::vector<real>(3);
nghost = std::vector<int>(3);
// Get grid size from input file, block [Grid]
int npoints[3];
for(int dir = 0 ; dir < 3 ; dir++) {
npoints[dir] = 1;
nghost[dir] = 0;
std::string label = std::string("X")+std::to_string(dir+1)+std::string("-grid");
int numPatch = input.Get<int>("Grid",label,0);
if(dir<DIMENSIONS) {
#if ORDER < 4
nghost[dir] = 2;
#else
nghost[dir] = 3;
#endif
npoints[dir] = 0;
for(int patch = 0; patch < numPatch ; patch++) {
npoints[dir] += input.Get<int>("Grid",label,2+3*patch );
}
}
}
np_tot = std::vector<int>(3);
np_int = std::vector<int>(3);
lbound = std::vector<BoundaryType>(3);
rbound = std::vector<BoundaryType>(3);
for(int dir = 0 ; dir < 3 ; dir++) {
np_tot[dir] = npoints[dir] + 2*nghost[dir];
np_int[dir] = npoints[dir];
std::string label = std::string("X")+std::to_string(dir+1)+std::string("-beg");
std::string boundary = input.Get<std::string>("Boundary",label,0);
if(boundary.compare("outflow") == 0) {
lbound[dir] = outflow;
} else if(boundary.compare("periodic") == 0) {
lbound[dir] = periodic;
} else if(boundary.compare("reflective") == 0) {
lbound[dir] = reflective;
} else if(boundary.compare("internal") == 0) {
lbound[dir] = internal;
} else if(boundary.compare("shearingbox") == 0) {
lbound[dir] = shearingbox;
} else if(boundary.compare("axis") == 0) {
if(dir!= JDIR) {
IDEFIX_ERROR("Axis Boundaries are only applicable to X2");
}
lbound[dir] = axis;
haveAxis = true;
} else if(boundary.compare("userdef") == 0) {
lbound[dir] = userdef;
} else {
std::stringstream msg;
msg << "Unknown boundary type " << boundary;
IDEFIX_ERROR(msg);
}
label = std::string("X")+std::to_string(dir+1)+std::string("-end");
boundary = input.Get<std::string>("Boundary",label,0);
if(boundary.compare("outflow") == 0) {
rbound[dir] = outflow;
} else if(boundary.compare("periodic") == 0) {
rbound[dir] = periodic;
} else if(boundary.compare("reflective") == 0) {
rbound[dir] = reflective;
} else if(boundary.compare("internal") == 0) {
rbound[dir] = internal;
} else if(boundary.compare("shearingbox") == 0) {
rbound[dir] = shearingbox;
} else if(boundary.compare("axis") == 0) {
if(dir!= JDIR) {
IDEFIX_ERROR("Axis Boundaries are only applicable to X2");
}
rbound[dir] = axis;
haveAxis = true;
} else if(boundary.compare("userdef") == 0) {
rbound[dir] = userdef;
} else {
std::stringstream msg;
msg << "Unknown boundary type " << boundary;
IDEFIX_ERROR(msg);
}
}
// Allocate the grid structure on device. Initialisation will come from GridHost
x = std::vector<IdefixArray1D<real>>(3);
xr = std::vector<IdefixArray1D<real>>(3);
xl = std::vector<IdefixArray1D<real>>(3);
dx = std::vector<IdefixArray1D<real>>(3);
for(int dir = 0 ; dir < 3 ; dir++) {
x[dir] = IdefixArray1D<real>("Grid_x",np_tot[dir]);
xr[dir] = IdefixArray1D<real>("Grid_xr",np_tot[dir]);
xl[dir] = IdefixArray1D<real>("Grid_xl",np_tot[dir]);
dx[dir] = IdefixArray1D<real>("Grid_dx",np_tot[dir]);
}
// Allocate proc structure (by default: one proc in each direction, size one)
nproc = std::vector<int> (3);
xproc = std::vector<int> (3);
for(int i=0 ; i < 3; i++) {
nproc[i] = 1;
xproc[i] = 0;
}
#ifdef WITH_MPI
// Domain decomposition required for the grid
// Init periodicity array
int period[3];
for(int i=0 ; i < 3 ; i++)
period[i] = 0;
// Check if the dec option has been passed when number of procs > 1
if(idfx::psize>1) {
if(input.CheckEntry("CommandLine","dec") != DIMENSIONS) {
// No command line decomposition, make auto-decomposition if possible
// (only when nproc and dimensions are powers of 2, and in 1D)
if(DIMENSIONS == 1) {
nproc[0] = idfx::psize;
} else {
if(!isPow2(idfx::psize))
IDEFIX_ERROR(
"Automatic domain decomposition requires the number of processes to be a power of 2. "
"Alternatively, set a manual decomposition with -dec"
);
for(int dir = 0; dir < 3; dir++) {
if(!isPow2(np_int[dir]))
IDEFIX_ERROR(
"Automatic domain decomposition requires nx1, nx2 and nx3 to be powers of 2. "
"Alternatively, set a manual decomposition with -dec"
);
}
makeDomainDecomposition();
}
} else {
// Manual domain decomposition (with -dec option)
int ntot=1;
for(int dir=0 ; dir < DIMENSIONS; dir++) {
nproc[dir] = input.Get<int>("CommandLine","dec",dir);
// Check that the dimension is effectively divisible by number of procs
if(np_int[dir] % nproc[dir])
IDEFIX_ERROR("Grid size must be a multiple of the domain decomposition");
// Count the total number of procs we'll need for the specified domain decomposition
ntot = ntot * nproc[dir];
}
if(ntot != idfx::psize) {
std::stringstream msg;
msg << "The number of MPI process (" << idfx::psize
<< ") does not match your domain decomposition (";
for(int dir=0 ; dir < DIMENSIONS; dir++) {
msg << nproc[dir];
if(dir<DIMENSIONS-1) msg << ", ";
}
msg << ").";
IDEFIX_ERROR(msg);
}
}
}
// Add periodicity indications
for(int dir=0 ; dir < DIMENSIONS; dir++) {
if(rbound[dir] == periodic || rbound[dir] == shearingbox) period[dir] = 1;
}
// Create cartesian communicator along with cartesian coordinates.
MPI_Cart_create(MPI_COMM_WORLD, 3, nproc.data(), period, 0, &CartComm);
MPI_Cart_coords(CartComm, idfx::prank, 3, xproc.data());
MPI_Barrier(MPI_COMM_WORLD);
if(haveAxis) {
// create axis communicator to be able to exchange data over the axis
// (only retain the phi dimension)
int remainDims[3] = {false, false, true};
MPI_SAFE_CALL(MPI_Cart_sub(CartComm, remainDims, &AxisComm));
}
#endif
// init coarsening
if(input.CheckEntry("Grid","coarsening")>=0) {
std::string coarsenType = input.Get<std::string>("Grid","coarsening",0);
if(coarsenType.compare("static")==0) {
this->haveGridCoarsening = GridCoarsening::enabled;
} else if(coarsenType.compare("dynamic")==0) {
this->haveGridCoarsening = GridCoarsening::dynamic;
} else {
std::stringstream msg;
msg << "Grid coarsening can only be static or dynamic. I got: " << coarsenType;
IDEFIX_ERROR(msg);
}
this->coarseningDirection = std::vector<bool>(3, false);
int directions = input.CheckEntry("Grid","coarsening");
for(int i = 1 ; i < directions ; i++) {
std::string dirname = input.Get<std::string>("Grid","coarsening",i);
if(dirname.compare("X1")==0) {
coarseningDirection[IDIR] = true;
} else if(dirname.compare("X2")==0) {
coarseningDirection[JDIR] = true;
} else if(dirname.compare("X3")==0) {
coarseningDirection[KDIR] = true;
} else {
std::stringstream msg;
msg << "Grid coarsening direction can only be X1, X2 and/or X3. I got: " << dirname;
IDEFIX_ERROR(msg);
}
}
this->haveGridCoarsening = GridCoarsening::enabled;
}
idfx::popRegion();
}
bool Grid::isPow2(int n) {
return( (n & (n-1)) == 0);
}
// Produce a domain decomposition in nProc assuming psize and np_int[] are powers of 2
void Grid::makeDomainDecomposition() {
// initialize the routine
int nleft=idfx::psize;
int nlocal[3];
for(int dir = 0; dir < 3; dir++) {
nproc[dir] = 1;
nlocal[dir] = np_int[dir];
}
// Loop
while(nleft>1) {
// Find the direction where there is a maximum of point
int dirmax;
int nmax=1;
int ndir=2;
for(int dir = ndir; dir >= 0; dir--) {
// We do this loop backward so that we divide the domain first in the last dimension
// (better for cache optimisation)
if(nlocal[dir]>nmax ) {
nmax=nlocal[dir];
dirmax=dir;
}
}
// At this point, we have nmax points in direction dirmax,
// which is the direction we're going to divide by 2
if(nmax==1)
IDEFIX_ERROR("Your domain size is too small to be decomposed "
"on this number of MPI processes");
nlocal[dirmax]=nlocal[dirmax]/2;
nproc[dirmax]=nproc[dirmax]*2;
nleft=nleft/2;
}
}
/*
Grid& Grid::operator=(const Grid& grid) {
for(int dir = 0 ; dir < 3 ; dir++) {
x[dir] = grid.x[dir];
xr[dir] = grid.xr[dir];
xl[dir] = grid.xl[dir];
dx[dir] = grid.dx[dir];
xbeg[dir] = grid.xbeg[dir];
xend[dir] = grid.xend[dir];
np_tot[dir] = grid.np_tot[dir];
np_int[dir] = grid.np_int[dir];
nghost[dir] = grid.nghost[dir];
lbound[dir] = grid.lbound[dir];
rbound[dir] = grid.rbound[dir];
}
return *this;
}
*/
void Grid::ShowConfig() {
idfx::cout << "Grid: full grid size is " << std::endl;
for(int dir = 0 ; dir < DIMENSIONS ; dir++) {
std::string lboundString, rboundString;
switch(lbound[dir]) {
case outflow:
lboundString="outflow";
break;
case reflective:
lboundString="reflective";
break;
case periodic:
lboundString="periodic";
break;
case internal:
lboundString="internal";
break;
case shearingbox:
lboundString="shearingbox";
break;
case axis:
lboundString="axis";
break;
case userdef:
lboundString="userdef";
break;
default:
lboundString="unknown";
}
switch(rbound[dir]) {
case outflow:
rboundString="outflow";
break;
case reflective:
rboundString="reflective";
break;
case periodic:
rboundString="periodic";
break;
case internal:
rboundString="internal";
break;
case shearingbox:
rboundString="shearingbox";
break;
case axis:
rboundString="axis";
break;
case userdef:
rboundString="userdef";
break;
default:
rboundString="unknown";
}
idfx::cout << "\t Direction X" << (dir+1) << ": " << lboundString << "\t" << xbeg[dir]
<< "...." << np_int[dir] << "...." << xend[dir] << "\t" << rboundString
<< std::endl;
}
#ifdef WITH_MPI
idfx::cout << "Grid: MPI domain decomposition is (";
for(int dir = 0 ; dir < DIMENSIONS ; dir++) {
idfx::cout << " " << nproc[dir] << " ";
}
idfx::cout << ")" << std::endl;
idfx::cout << "Grid: Current MPI proc coordinates (";
for(int dir = 0; dir < 3; dir++) {
idfx::cout << xproc[dir];
if(dir < 2) idfx::cout << ", ";
}
idfx::cout << ")" << std::endl;
#endif
if(haveGridCoarsening) {
if(haveGridCoarsening == GridCoarsening::enabled ) {
idfx::cout << "Grid: static grid coarsening enabled in direction(s) ";
} else if (haveGridCoarsening == GridCoarsening::dynamic ) {
idfx::cout << "Grid: dynamic grid coarsening enabled in direction(s) ";
} else {
IDEFIX_ERROR("Unknown grid coarsening");
}
for(int i = 0 ; i < 3 ; i++) {
if(coarseningDirection[i]) {
idfx::cout << "X" << i+1 << " ";
}
}
idfx::cout << std::endl;
}
}