Program Listing for File enforceEMFBoundary.cpp

Return to documentation for file (hydro/electromotiveforce/enforceEMFBoundary.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 "electroMotiveForce.hpp"
#include "hydro.hpp"
#include "dataBlock.hpp"


void ElectroMotiveForce::EnforceEMFBoundary() {
  idfx::pushRegion("Emf::EnforceEMFBoundary");
#if MHD == YES
  if(data->hydro.haveEmfBoundary)
    this->data->hydro.emfBoundaryFunc(*data, data->t);

  if(this->data->hydro.haveAxis) {
    this->data->hydro.myAxis.RegularizeEMFs();
  }

  #ifdef WITH_MPI
    // This average the EMFs at the domain surface with immediate neighbours
    // to ensure the EMFs exactly match
    this->ExchangeAll();
  #endif

  IdefixArray3D<real> ex = this->ex;
  IdefixArray3D<real> ey = this->ey;
  IdefixArray3D<real> ez = this->ez;

  // Ensure EMF are strictly periodic to avoid accumulation of roundoff errors
  for(int dir=0 ; dir < DIMENSIONS ; dir++ ) {
    if(data->lbound[dir] == periodic && data->rbound[dir] == periodic) {
      // If domain decomposed, periodicity is already enforced by ExchangeAll
      if(data->mygrid->nproc[dir] == 1) {
        int ioffset = (dir == IDIR) ? data->np_int[IDIR] : 0;
        int joffset = (dir == JDIR) ? data->np_int[JDIR] : 0;
        int koffset = (dir == KDIR) ? data->np_int[KDIR] : 0;

        int ibeg = (dir == IDIR) ? data->beg[IDIR] : 0;
        int iend = (dir == IDIR) ? data->beg[IDIR]+1 : data->np_tot[IDIR];
        int jbeg = (dir == JDIR) ? data->beg[JDIR] : 0;
        int jend = (dir == JDIR) ? data->beg[JDIR]+1 : data->np_tot[JDIR];
        int kbeg = (dir == KDIR) ? data->beg[KDIR] : 0;
        int kend = (dir == KDIR) ? data->beg[KDIR]+1 : data->np_tot[KDIR];
        idefix_for("BoundaryEMFPeriodic",kbeg,kend,jbeg,jend,ibeg,iend,
          KOKKOS_LAMBDA (int k, int j, int i) {
            real em;

            if(dir==IDIR) {
              em = HALF_F*(ez(k,j,i)+ez(k,j,i+ioffset));
              ez(k,j,i) = em;
              ez(k,j,i+ioffset) = em;

              #if DIMENSIONS == 3
              em = HALF_F*(ey(k,j,i)+ey(k,j,i+ioffset));
              ey(k,j,i) = em;
              ey(k,j,i+ioffset) = em;
              #endif
            }

            if(dir==JDIR) {
              em = HALF_F*(ez(k,j,i)+ez(k,j+joffset,i));
              ez(k,j,i) = em;
              ez(k,j+joffset,i) = em;

              #if DIMENSIONS == 3
              em = HALF_F*(ex(k,j,i)+ex(k,j+joffset,i));
              ex(k,j,i) = em;
              ex(k,j+joffset,i) = em;
              #endif
            }

            if(dir==KDIR) {
              em = HALF_F*(ex(k,j,i)+ex(k+koffset,j,i));
              ex(k,j,i) = em;
              ex(k+koffset,j,i) = em;

              em = HALF_F*(ey(k,j,i)+ey(k+koffset,j,i));
              ey(k,j,i) = em;
              ey(k+koffset,j,i) = em;
            }
          });
      }
    }
    if(data->lbound[dir] == shearingbox || data->rbound[dir] == shearingbox) {
      SymmetrizeEMFShearingBox();
    }
  }
#endif // MHD==YES
  idfx::popRegion();
}

void ElectroMotiveForce::SymmetrizeEMFShearingBox() {
  idfx::pushRegion("Emf::EnforceEMFBoundary");
  #if MHD == YES

    IdefixArray2D<real> sbEyL = this->sbEyL;
    IdefixArray2D<real> sbEyR = this->sbEyR;
    IdefixArray2D<real> sbEyRL = this->sbEyRL;

    IdefixArray3D<real> ey = this->ey;

    // Store emf components on the left and right
    if(data->lbound[IDIR]==shearingbox) {
      int i = data->beg[IDIR];
      idefix_for("StoreEyL", 0, data->np_tot[KDIR], 0, data->np_tot[JDIR],
                            KOKKOS_LAMBDA(int k,int j) {
                              sbEyL(k,j) = ey(k,j,i);
                            });
    }
    if(data->rbound[IDIR]==shearingbox) {
      int i = data->end[IDIR];
      idefix_for("StoreEyL", 0, data->np_tot[KDIR], 0, data->np_tot[JDIR],
                            KOKKOS_LAMBDA(int k, int j) {
                              sbEyR(k,j) = ey(k,j,i);
                            });
    }

    // todo: exchange sbEyR and sbEyL when domain-decomposed along y
    #ifdef WITH_MPI
      if(data->mygrid->nproc[IDIR]>1) {
        int procLeft, procRight;
        const int size = data->np_tot[KDIR]*data->np_tot[JDIR];
        MPI_Status status;

        MPI_SAFE_CALL(MPI_Cart_shift(data->mygrid->CartComm,0,1,&procLeft,&procRight));
        if(data->lbound[IDIR]==shearingbox) {
          // We send to our left (which, by periodicity, is the right end of the domain)
          // our value of sbEyL and get
          MPI_Sendrecv(sbEyL.data(), size, realMPI, procLeft, 2001,
                       sbEyR.data(), size, realMPI, procLeft, 2002,
                       data->mygrid->CartComm, &status );
        }
        if(data->rbound[IDIR]==shearingbox) {
          // We send to our right (which, by periodicity, is the left end (=beginning)
          // of the domain) our value of sbEyR and get sbEyL
          MPI_Sendrecv(sbEyR.data(), size, realMPI, procRight, 2002,
                       sbEyL.data(), size, realMPI, procRight, 2001,
                       data->mygrid->CartComm, &status );
        }
      }
    #endif
    // Extrapolate on the left
    if(data->lbound[IDIR]==shearingbox) {
      int i = data->beg[IDIR];
      ExtrapolateEMFShearingBox(left, sbEyR, sbEyRL);

      idefix_for("ReplaceEyLeft", 0, data->np_tot[KDIR], 0, data->np_tot[JDIR],
                              KOKKOS_LAMBDA(int k,int j) {
                                ey(k,j,i) = 0.5*(sbEyL(k,j)+sbEyRL(k,j));
                              });
    }

    // Extrapolate on the right
    if(data->rbound[IDIR]==shearingbox) {
      int i = data->end[IDIR];
      ExtrapolateEMFShearingBox(right, sbEyL, sbEyRL);

      idefix_for("ReplaceEyRight", 0, data->np_tot[KDIR], 0, data->np_tot[JDIR],
                              KOKKOS_LAMBDA(int k,int j) {
                                ey(k,j,i) = 0.5*(sbEyR(k,j)+sbEyRL(k,j));
                              });
    }

  #endif
  idfx::popRegion();
}


void ElectroMotiveForce::ExtrapolateEMFShearingBox(BoundarySide side,
                                                   IdefixArray2D<real> Ein,
                                                   IdefixArray2D<real> Eout) {
  const int nxi = data->np_int[IDIR];
  const int nxj = data->np_int[JDIR];

  const int ighost = data->nghost[IDIR];
  const int jghost = data->nghost[JDIR];

  // Shear rate
  const real S  = hydro->sbS;

  // Box size
  const real Lx = data->mygrid->xend[IDIR] - data->mygrid->xbeg[IDIR];
  const real Ly = data->mygrid->xend[JDIR] - data->mygrid->xbeg[JDIR];

  // total number of cells in y (active domain)
  const int ny = data->mygrid->np_int[JDIR];
  const real dy = Ly/ny;

  // Compute offset in y modulo the box size
  const int sign=2*side-1;
  const real sbVelocity = sign*S*Lx;
  real dL = std::fmod(sbVelocity*data->t,Ly);

  // translate this into # of cells
  const int m = static_cast<int> (std::floor(dL/dy+HALF_F));

  // remainding shift
  const real eps = dL / dy - m;

  // New we need to perform the shift
  idefix_for("BoundaryShearingBoxEMF", 0, data->np_tot[KDIR],
                                       0, data->np_tot[JDIR],
        KOKKOS_LAMBDA (int k, int j) {
          // jorigin
          const int jo = jghost + ((j-m-jghost)%nxj+nxj)%nxj;
          const int jop2 = jghost + ((jo+2-jghost)%nxj+nxj)%nxj;
          const int jop1 = jghost + ((jo+1-jghost)%nxj+nxj)%nxj;
          const int jom1 = jghost + ((jo-1-jghost)%nxj+nxj)%nxj;
          const int jom2 = jghost + ((jo-2-jghost)%nxj+nxj)%nxj;

          // Define Left and right fluxes
          // Fluxes are defined from slop-limited interpolation
          // Using Van-leer slope limiter (consistently with the main advection scheme)
          real Fl,Fr;
          real dqm, dqp, dq;

          if(eps>=ZERO_F) {
            // Compute Fl
            dqm = Ein(k,jom1) - Ein(k,jom2);
            dqp = Ein(k,jo) - Ein(k,jom1);
            dq = (dqp*dqm > ZERO_F ? TWO_F*dqp*dqm/(dqp + dqm) : ZERO_F);

            Fl = Ein(k,jom1) + 0.5*dq*(1.0-eps);
            //Compute Fr
            dqm=dqp;
            dqp = Ein(k,jop1) - Ein(k,jo);
            dq = (dqp*dqm > ZERO_F ? TWO_F*dqp*dqm/(dqp + dqm) : ZERO_F);

            Fr = Ein(k,jo) + 0.5*dq*(1.0-eps);
          } else {
            //Compute Fl
            dqm = Ein(k,jo) - Ein(k,jom1);
            dqp = Ein(k,jop1) - Ein(k,jo);
            dq = (dqp*dqm > ZERO_F ? TWO_F*dqp*dqm/(dqp + dqm) : ZERO_F);

            Fl = Ein(k,jo) - 0.5*dq*(1.0+eps);
            // Compute Fr
            dqm=dqp;
            dqp = Ein(k,jop2) - Ein(k,jop1);
            dq = (dqp*dqm > ZERO_F ? TWO_F*dqp*dqm/(dqp + dqm) : ZERO_F);

            Fr = Ein(k,jop1) - 0.5*dq*(1.0+eps);
          }
          Eout(k,j) = Ein(k,jo) - eps*(Fr - Fl);
        });
}