Program Listing for File gridHost.cpp

Return to documentation for file (gridHost.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 <vector>

#include "idefix.hpp"
#include "grid.hpp"
#include "gridHost.hpp"

GridHost::GridHost(Grid &grid) {
  idfx::pushRegion("GridHost::GridHost(Grid)");

  this->grid=&grid;
  nghost = grid.nghost;
  np_tot = grid.np_tot;
  np_int = grid.np_int;

  lbound = grid.lbound;
  rbound = grid.rbound;

  xbeg = grid.xbeg;
  xend = grid.xend;

  haveAxis = grid.haveAxis;

  // Create mirrors on host
  x = std::vector<IdefixArray1D<real>::HostMirror> (3);
  xr = std::vector<IdefixArray1D<real>::HostMirror> (3);
  xl = std::vector<IdefixArray1D<real>::HostMirror> (3);
  dx = std::vector<IdefixArray1D<real>::HostMirror> (3);
  for(int dir = 0 ; dir < 3 ; dir++) {
    x[dir] = Kokkos::create_mirror_view(grid.x[dir]);
    xr[dir] = Kokkos::create_mirror_view(grid.xr[dir]);
    xl[dir] = Kokkos::create_mirror_view(grid.xl[dir]);
    dx[dir] = Kokkos::create_mirror_view(grid.dx[dir]);
  }

  idfx::popRegion();
}

void GridHost::MakeGrid(Input &input) {
  idfx::pushRegion("GridHost::MakeGrid");

  real xstart[3];
  real xend[3];
  // Create the grid

  // Get grid parameters from input file, block [Grid]
  for(int dir = 0 ; dir < 3 ; dir++) {
    std::string label = std::string("X")+std::to_string(dir+1)+std::string("-grid");
    int numPatch = input.Get<int>("Grid",label,0);

    xstart[dir] = input.Get<real>("Grid",label,1);
    xend[dir] = input.Get<real>("Grid",label,4+(numPatch-1)*3);

    this->xbeg[dir] = xstart[dir];
    this->xend[dir] = xend[dir];


    if(dir<DIMENSIONS) {
      // Loop on all the patches
      int idxstart = nghost[dir];
      for(int patch = 0 ; patch < numPatch ; patch++) {
        std::string patchType = input.Get<std::string>("Grid",label,3+patch*3);
        real patchStart = input.Get<real>("Grid",label,1+patch*3);
        real patchEnd = input.Get<real>("Grid",label,4+patch*3);
        int patchSize = input.Get<int>("Grid",label,2+patch*3);

        // If this is the first or last patch, also define ghost cells
        int ghostStart = 0;
        int ghostEnd = 0;
        if(patch == 0) ghostStart = nghost[dir];
        if(patch == numPatch-1) ghostEnd = nghost[dir];
        // Define the grid depending on patch type
        if(patchType.compare("u")==0) {
          // Uniform patch
          for(int i = 0 - ghostStart ; i < patchSize + ghostEnd ; i++) {
            dx[dir](i+idxstart) = (patchEnd-patchStart)/(patchSize);
            x[dir](i+idxstart)=patchStart + (i+HALF_F)*dx[dir](i+idxstart);
            xl[dir](i+idxstart)=patchStart  + i*dx[dir](i+idxstart);
            xr[dir](i+idxstart)=patchStart  + (i+1)*dx[dir](i+idxstart);
          }
        } else if(patchType.compare("l")==0) {
          // log-increasing patch

          double alpha = (patchEnd + fabs(patchStart) - patchStart)/fabs(patchStart);

          for(int i = 0 - ghostStart ; i < patchSize + ghostEnd ; i++) {
            xl[dir](i+idxstart) = patchStart * pow(alpha,
                                    static_cast<double>(i) / (static_cast<double>(patchSize)));
            xr[dir](i+idxstart) = patchStart * pow(alpha,
                                    static_cast<double>(i+1) / (static_cast<double>(patchSize)));
            dx[dir](i+idxstart) = xr[dir](i+idxstart) - xl[dir](i+idxstart);
            x[dir](i+idxstart)= 0.5*(xr[dir](i+idxstart) + xl[dir](i+idxstart));
          }
        } else if((patchType.compare("s+")==0)||(patchType.compare("s-")==0)) {
          // Stretched grid
          // - means we take the initial dx on the left side, + on the right side
          // refPatch corresponds to the patch from which we compute the initial dx
          // of the stretched grid

          int refPatch=patch;
          if(patchType.compare("s+")==0) {
            refPatch=patch+1;
          } else {
            refPatch=patch-1;
          }
          // Sanity check
          // Check that the reference patch actually exist
          if(refPatch<0 || refPatch >= numPatch) {
            IDEFIX_ERROR("You're attempting to construct a stretched patch "
                         "from a non-existent patch");
          }
          // Check that the reference patch is a uniform one
          if(input.Get<std::string>("Grid",label,3+3*refPatch).compare("u")) {
            IDEFIX_ERROR("You're attempting to construct a stretched patch "
                         "from a non-uniform grid");
          }
          // Ok, we have a well-behaved reference patch, compute dx from the reference patch
          real refPatchStart = input.Get<real>("Grid",label,1+refPatch*3);
          real refPatchEnd = input.Get<real>("Grid",label,4+refPatch*3);
          int refPatchSize = input.Get<int>("Grid",label,2+refPatch*3);
          double delta = (refPatchEnd-refPatchStart)/refPatchSize;
          double logdelta = log((patchEnd-patchStart)/delta);

          // Check that it is possible to make a stretch grid (bug report #28)
          if(std::fabs((patchEnd-patchStart)/patchSize - delta) < 1e-10) {
            IDEFIX_ERROR("A Stretch grid can be defined only if the stretched domain has a mean\n"
                         "spacing different from the reference uniform grid.\n"
                         "Try changing the number of points in your stretched grid.");
          }
          // Next we have to compute the stretch factor q. Let's start with a guess
          double q=1.05;
          // Use Newton method
          for(int iter=0; iter <= 50; iter++) {
            double f = log((pow(q,patchSize+1)-q)/(q-1))-logdelta;
            double fp = ((patchSize+1)*pow(q,patchSize)-1)/(pow(q,patchSize+1)-q)-1/(q-1);
            double dq = f/fp;
            // advance the guess
            q = q - dq;
            // Check whether we have converged
            if(fabs(dq)<1e-14*q) break;
            if(iter==50) IDEFIX_ERROR("Failed to create the stretched grid");
          }

          // once we know q, we can make the grid
          if(patchType.compare("s-")==0) {
            for(int i = 0 - ghostStart ; i < patchSize + ghostEnd ; i++) {
              xl[dir](i+idxstart) = patchStart + q*(pow(q,i)-1)/(q-1)*delta;
              xr[dir](i+idxstart) = patchStart + q*(pow(q,i+1)-1)/(q-1)*delta;
              dx[dir](i+idxstart) = pow(q,i+1)*delta;
              x[dir](i+idxstart)= 0.5*(xr[dir](i+idxstart) + xl[dir](i+idxstart));
            }
          } else {
            for(int i = 0 - ghostStart ; i < patchSize + ghostEnd ; i++) {
              xl[dir](i+idxstart) = patchEnd - q*(pow(q,patchSize-i)-1)/(q-1)*delta;
              xr[dir](i+idxstart) = patchEnd - q*(pow(q,patchSize-i-1)-1)/(q-1)*delta;
              dx[dir](i+idxstart) = pow(q,patchSize-i)*delta;
              x[dir](i+idxstart)= 0.5*(xr[dir](i+idxstart) + xl[dir](i+idxstart));
            }
          }
        } else {
          std::stringstream msg;
          msg << "GridHost::MakeGrid: Unknown grid type :" << patchType << std::endl;
          IDEFIX_ERROR(msg);
        }

        // Increment offset
        idxstart += patchSize;
      }
    } else {
      // dir >= DIMENSIONS/ Init simple uniform grid
      for(int i = 0 ; i < np_tot[dir] ; i++) {
        dx[dir](i) = (xend[dir]-xstart[dir])/(np_int[dir]);
        x[dir](i)=xstart[dir] + (i-nghost[dir]+HALF_F)*dx[dir](i);
        xl[dir](i)=xstart[dir] + (i-nghost[dir])*dx[dir](i);
        xr[dir](i)=xstart[dir] + (i-nghost[dir]+1)*dx[dir](i);
      }
    }
  }

  // Check that axis treatment is compatible with the domain
if(haveAxis) {
  #if GEOMETRY != SPHERICAL
    IDEFIX_ERROR("Axis boundaries only compatible with Spherical boundary conditions");
  #endif
  #if DIMENSIONS < 2
    IDEFIX_ERROR("Axis Boundaries requires at least two dimenions");
  #endif
  if((fabs(xbeg[JDIR])>1e-10) && (lbound[JDIR] == axis)) {
    IDEFIX_ERROR("Axis Boundaries requires your X2 domain to start at exactly x2=0.0");
  }
  if((fabs(xend[JDIR]-M_PI)>1e-10) && (rbound[JDIR] == axis )) {
    IDEFIX_ERROR("Axis Boundaries requires your X2 domain to end at exactly x2=Pi");
  }
  // Enforce symmetry of theta grid spacing when we cross the axis
  if(lbound[JDIR] == axis) {
    int jref = nghost[JDIR];
    for(int j = jref - 1 ; j>=0 ; j -- ) {
      dx[JDIR](j) = dx[JDIR](2*jref - j - 1);
      xl[JDIR](j) = xl[JDIR](j+1)- dx[JDIR](j);
      xr[JDIR](j) = xl[JDIR](j+1);
    }
  }
  if(rbound[JDIR] == axis) {
    int jref = nghost[JDIR]+np_int[JDIR] - 1;
    for(int j = jref + 1 ; j<np_tot[JDIR] ; j++ ) {
      dx[JDIR](j) = dx[JDIR](2*jref - j + 1);
      xr[JDIR](j) = xr[JDIR](j-1) + dx[JDIR](j);
      xl[JDIR](j) = xr[JDIR](j-1);
    }
  }
}

  idfx::popRegion();
}

void GridHost::SyncFromDevice() {
  idfx::pushRegion("GridHost::SyncFromDevice");

  for(int dir = 0 ; dir < 3 ; dir++) {
    Kokkos::deep_copy(x[dir],grid->x[dir]);
    Kokkos::deep_copy(xr[dir],grid->xr[dir]);
    Kokkos::deep_copy(xl[dir],grid->xl[dir]);
    Kokkos::deep_copy(dx[dir],grid->dx[dir]);
  }

  xbeg = grid->xbeg;
  xend = grid->xend;

  idfx::popRegion();
}

void GridHost::SyncToDevice() {
  idfx::pushRegion("GridHost::SyncToDevice");

  // Sync with the device
  for(int dir = 0 ; dir < 3 ; dir++) {
    Kokkos::deep_copy(grid->x[dir],x[dir]);
    Kokkos::deep_copy(grid->xr[dir],xr[dir]);
    Kokkos::deep_copy(grid->xl[dir],xl[dir]);
    Kokkos::deep_copy(grid->dx[dir],dx[dir]);
  }

  grid->xbeg = xbeg;
  grid->xend = xend;

  idfx::popRegion();
}