Program Listing for File hydroboundary.cpp

Return to documentation for file (hydro/boundary/hydroboundary.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 "hydroboundary.hpp"
#include "hydro.hpp"
#include "dataBlock.hpp"
#include "boundaryloop.hpp"


void HydroBoundary::Init(Input & input, Grid &grid, Hydro* hydro) {
  idfx::pushRegion("HydroBoundary::Init");
  this->hydro = hydro;
  this->data = hydro->data;

  if(data->lbound[IDIR] == shearingbox || data->rbound[IDIR] == shearingbox) {
    sBArray = IdefixArray4D<real>("ShearingBoxArray",
                                  NVAR,
                                  data->np_tot[KDIR]+1,
                                  data->np_tot[JDIR]+1,
                                  data->nghost[IDIR]);
  }

  // Init MPI stack when needed
#ifdef WITH_MPI
  // Init variable mappers
  // The variable mapper list all of the variable which are exchanged in MPI boundary calls
  // This is required since we skip some of the variables in Vc to limit the amount of data
  // being exchanged
  #if MHD == YES
  int mapNVars = NVAR - DIMENSIONS; // We will not send magnetic field components which are in Vs
  #else
  int mapNVars = NVAR;
  #endif

  std::vector<int> mapVars;

  // Init the list of variables we will exchange in MPI routines
  int ntarget = 0;
  for(int n = 0 ; n < mapNVars ; n++) {
    mapVars.push_back(ntarget);
    ntarget++;
    #if MHD == YES
      // Skip centered field components if they are also defined in Vs
      #if DIMENSIONS >= 1
        if(ntarget==BX1) ntarget++;
      #endif
      #if DIMENSIONS >= 2
        if(ntarget==BX2) ntarget++;
      #endif
      #if DIMENSIONS == 3
        if(ntarget==BX3) ntarget++;
      #endif
    #endif
  }
  #if MHD == YES
    mpi.Init(data->mygrid, mapVars, data->nghost.data(), data->np_int.data(), true);
  #else
    mpi.Init(data->mygrid, mapVars, data->nghost.data(), data->np_int.data());
  #endif
#endif // MPI
  idfx::popRegion();
}

void HydroBoundary::EnrollFluxBoundary(UserDefBoundaryFunc myFunc) {
  this->haveFluxBoundary = true;
  this->fluxBoundaryFunc = myFunc;
}

void HydroBoundary::EnforceFluxBoundaries(int dir) {
  idfx::pushRegion("HydroBoundary::EnforceFluxBoundaries");
  if(haveFluxBoundary) {
    if(data->lbound[dir] != internal) {
      fluxBoundaryFunc(*data, dir, left, data->t);
    }
    if(data->rbound[dir] != internal) {
      fluxBoundaryFunc(*data, dir, right, data->t);
    }
  } else {
    IDEFIX_ERROR("Cannot enforce flux boundary conditions without enrolling a specific function");
  }
  idfx::popRegion();
}

void HydroBoundary::SetBoundaries(real t) {
  idfx::pushRegion("HydroBoundary::SetBoundaries");
  // set internal boundary conditions
  if(haveInternalBoundary) internalBoundaryFunc(*data, t);
  for(int dir=0 ; dir < DIMENSIONS ; dir++ ) {
      // MPI Exchange data when needed
    #ifdef WITH_MPI
    if(data->mygrid->nproc[dir]>1) {
      switch(dir) {
        case 0:
          mpi.ExchangeX1(hydro->Vc, hydro->Vs);
          break;
        case 1:
          mpi.ExchangeX2(hydro->Vc, hydro->Vs);
          break;
        case 2:
          mpi.ExchangeX3(hydro->Vc, hydro->Vs);
          break;
      }
    }
    #endif
    EnforceBoundaryDir(t, dir);
    #if MHD == YES
      // Reconstruct the normal field component when using CT
      ReconstructNormalField(dir);
    #endif
  } // Loop on dimension ends

#if MHD == YES
  // Remake the cell-centered field.
  ReconstructVcField(hydro->Vc);
#endif
  idfx::popRegion();
}


// Enforce boundary conditions by writing into ghost zones
void HydroBoundary::EnforceBoundaryDir(real t, int dir) {
  idfx::pushRegion("Hydro::EnforceBoundaryDir");

  // left boundary

  switch(data->lbound[dir]) {
    case internal:
      // internal is used for MPI-enforced boundary conditions. Nothing to be done here.
      break;

    case periodic:
      if(data->mygrid->nproc[dir] > 1) break; // Periodicity already enforced by MPI calls
      EnforcePeriodic(dir,left);
      break;

    case reflective:
      EnforceReflective(dir,left);
      break;

    case outflow:
      EnforceOutflow(dir,left);
      break;

    case shearingbox:
      EnforceShearingBox(t,dir,left);
      break;
    case axis:
      hydro->myAxis.EnforceAxisBoundary(left);
      break;
    case userdef:
      if(this->haveUserDefBoundary)
        this->userDefBoundaryFunc(*data, dir, left, t);
      else
        IDEFIX_ERROR("No function has been enrolled to define your own boundary conditions");
      break;

    default:
      std::stringstream msg ("Boundary condition type is not yet implemented");
      IDEFIX_ERROR(msg);
  }

  // right boundary

  switch(data->rbound[dir]) {
    case internal:
      // internal is used for MPI-enforced boundary conditions. Nothing to be done here.
      break;

    case periodic:
      if(data->mygrid->nproc[dir] > 1) break; // Periodicity already enforced by MPI calls
      EnforcePeriodic(dir,right);
      break;
    case reflective:
      EnforceReflective(dir,right);
      break;
    case outflow:
      EnforceOutflow(dir,right);
      break;
    case shearingbox:
      EnforceShearingBox(t,dir,right);
      break;
    case axis:
      hydro->myAxis.EnforceAxisBoundary(right);
      break;
    case userdef:
      if(this->haveUserDefBoundary)
        this->userDefBoundaryFunc(*data, dir, right, t);
      else
        IDEFIX_ERROR("No function has been enrolled to define your own boundary conditions");
      break;
    default:
      std::stringstream msg("Boundary condition type is not yet implemented");
      IDEFIX_ERROR(msg);
  }

  idfx::popRegion();
}


void HydroBoundary::ReconstructVcField(IdefixArray4D<real> &Vc) {
  idfx::pushRegion("Hydro::ReconstructVcField");

  IdefixArray4D<real> Vs=hydro->Vs;

  // Reconstruct cell average field when using CT
  idefix_for("ReconstructVcMagField",0,data->np_tot[KDIR],0,data->np_tot[JDIR],0,data->np_tot[IDIR],
    KOKKOS_LAMBDA (int k, int j, int i) {
      D_EXPAND( Vc(BX1,k,j,i) = HALF_F * (Vs(BX1s,k,j,i) + Vs(BX1s,k,j,i+1)) ;  ,
                Vc(BX2,k,j,i) = HALF_F * (Vs(BX2s,k,j,i) + Vs(BX2s,k,j+1,i)) ;  ,
                Vc(BX3,k,j,i) = HALF_F * (Vs(BX3s,k,j,i) + Vs(BX3s,k+1,j,i)) ;  )
    }
  );

  idfx::popRegion();
}


void HydroBoundary::ReconstructNormalField(int dir) {
  idfx::pushRegion("Hydro::ReconstructNormalField");

  // Reconstruct the field
  IdefixArray4D<real> Vs = hydro->Vs;
  // Coordinates
  IdefixArray1D<real> x1=data->x[IDIR];
  IdefixArray1D<real> x2=data->x[JDIR];
  IdefixArray1D<real> x3=data->x[KDIR];
  IdefixArray1D<real> dx1=data->dx[IDIR];
  IdefixArray1D<real> dx2=data->dx[JDIR];
  IdefixArray1D<real> dx3=data->dx[KDIR];

  IdefixArray3D<real> Ax1=data->A[IDIR];
  IdefixArray3D<real> Ax2=data->A[JDIR];
  IdefixArray3D<real> Ax3=data->A[KDIR];

  int nstart, nend;
  int nx1,nx2,nx3;

  // reconstruct BX1s
  nstart = data->beg[IDIR]-1;
  nend = data->end[IDIR];

  nx1=data->np_tot[IDIR];
  nx2=data->np_tot[JDIR];
  nx3=data->np_tot[KDIR];

  if(dir==IDIR) {
    idefix_for("ReconstructBX1s",0,nx3,0,nx2,
      KOKKOS_LAMBDA (int k, int j) {
        for(int i = nstart ; i>=0 ; i-- ) {
          Vs(BX1s,k,j,i) = 1.0 / Ax1(k,j,i) * ( Ax1(k,j,i+1)*Vs(BX1s,k,j,i+1)
                          +(D_EXPAND( ZERO_F                                                 ,
                            + Ax2(k,j+1,i) * Vs(BX2s,k,j+1,i) - Ax2(k,j,i) * Vs(BX2s,k,j,i) ,
                            + Ax3(k+1,j,i) * Vs(BX3s,k+1,j,i) - Ax3(k,j,i) * Vs(BX3s,k,j,i)  )));
        }

        for(int i = nend ; i<nx1 ; i++ ) {
          Vs(BX1s,k,j,i+1) = 1.0 / Ax1(k,j,i+1) * ( Ax1(k,j,i)*Vs(BX1s,k,j,i)
                            -(D_EXPAND(      ZERO_F                                           ,
                             + Ax2(k,j+1,i) * Vs(BX2s,k,j+1,i) - Ax2(k,j,i) * Vs(BX2s,k,j,i)  ,
                             + Ax3(k+1,j,i) * Vs(BX3s,k+1,j,i) - Ax3(k,j,i) * Vs(BX3s,k,j,i)  )));
        }
      }
    );
  }

#if DIMENSIONS >=2
  if(dir==JDIR) {
    nstart = data->beg[JDIR]-1;
    nend = data->end[JDIR];
    if(!hydro->haveAxis) {
      idefix_for("ReconstructBX2s",0,data->np_tot[KDIR],0,data->np_tot[IDIR],
        KOKKOS_LAMBDA (int k, int i) {
          for(int j = nstart ; j>=0 ; j-- ) {
            Vs(BX2s,k,j,i) = 1.0 / Ax2(k,j,i) * ( Ax2(k,j+1,i)*Vs(BX2s,k,j+1,i)
                        +(D_EXPAND( Ax1(k,j,i+1) * Vs(BX1s,k,j,i+1) - Ax1(k,j,i) * Vs(BX1s,k,j,i)  ,
                                                                                                  ,
                              + Ax3(k+1,j,i) * Vs(BX3s,k+1,j,i) - Ax3(k,j,i) * Vs(BX3s,k,j,i) )));
          }
          for(int j = nend ; j<nx2 ; j++ ) {
            Vs(BX2s,k,j+1,i) = 1.0 / Ax2(k,j+1,i) * ( Ax2(k,j,i)*Vs(BX2s,k,j,i)
                        -(D_EXPAND( Ax1(k,j,i+1) * Vs(BX1s,k,j,i+1) - Ax1(k,j,i) * Vs(BX1s,k,j,i)  ,
                                                                                                  ,
                              + Ax3(k+1,j,i) * Vs(BX3s,k+1,j,i) - Ax3(k,j,i) * Vs(BX3s,k,j,i) )));
          }
        }
      );
    } else {
      // We have an axis, ask myAxis to do that job for us
      hydro->myAxis.ReconstructBx2s();
    }
  }
#endif

#if DIMENSIONS == 3
  if(dir==KDIR) {
    nstart = data->beg[KDIR]-1;
    nend = data->end[KDIR];

    idefix_for("ReconstructBX3s",0,data->np_tot[JDIR],0,data->np_tot[IDIR],
      KOKKOS_LAMBDA (int j, int i) {
        for(int k = nstart ; k>=0 ; k-- ) {
          Vs(BX3s,k,j,i) = 1.0 / Ax3(k,j,i) * ( Ax3(k+1,j,i)*Vs(BX3s,k+1,j,i)
                          + ( Ax1(k,j,i+1) * Vs(BX1s,k,j,i+1) - Ax1(k,j,i) * Vs(BX1s,k,j,i)
                          + Ax2(k,j+1,i) * Vs(BX2s,k,j+1,i) - Ax2(k,j,i) * Vs(BX2s,k,j,i) ));
        }
        for(int k = nend ; k<nx3 ; k++ ) {
          Vs(BX3s,k+1,j,i) = 1.0 / Ax3(k+1,j,i) * ( Ax3(k,j,i)*Vs(BX3s,k,j,i)
                            - ( Ax1(k,j,i+1) * Vs(BX1s,k,j,i+1) - Ax1(k,j,i) * Vs(BX1s,k,j,i)
                            +  Ax2(k,j+1,i) * Vs(BX2s,k,j+1,i) - Ax2(k,j,i) * Vs(BX2s,k,j,i) ));
        }
      }
    );
  }
#endif

  idfx::popRegion();
}


void HydroBoundary::EnrollUserDefBoundary(UserDefBoundaryFunc myFunc) {
  this->userDefBoundaryFunc = myFunc;
  this->haveUserDefBoundary = true;
}

void HydroBoundary::EnrollInternalBoundary(InternalBoundaryFunc myFunc) {
  this->internalBoundaryFunc = myFunc;
  this->haveInternalBoundary = true;
}

void HydroBoundary::EnforcePeriodic(int dir, BoundarySide side ) {
  IdefixArray4D<real> Vc = hydro->Vc;
  int nxi = data->np_int[IDIR];
  int nxj = data->np_int[JDIR];
  int nxk = data->np_int[KDIR];

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

  BoundaryForAll("BoundaryPeriodic", dir, side,
        KOKKOS_LAMBDA (int n, int k, int j, int i) {
          int iref, jref, kref;
          // This hack takes care of cases where we have more ghost zones than active zones
          if(dir==IDIR)
            iref = ighost + (i+ighost*(nxi-1))%nxi;
          else
            iref = i;
          if(dir==JDIR)
            jref = jghost + (j+jghost*(nxj-1))%nxj;
          else
            jref = j;
          if(dir==KDIR)
            kref = kghost + (k+kghost*(nxk-1))%nxk;
          else
            kref = k;

          Vc(n,k,j,i) = Vc(n,kref,jref,iref);
        });

  #if MHD==YES
    IdefixArray4D<real> Vs = hydro->Vs;
    if(dir==JDIR || dir==KDIR) {
      nxi = data->np_int[IDIR]+1;
      nxj = data->np_int[JDIR];
      nxk = data->np_int[KDIR];
      BoundaryForX1s("BoundaryPeriodicX1s",dir,side,
      KOKKOS_LAMBDA (int k, int j, int i) {
        int iref, jref, kref;
          // This hack takes care of cases where we have more ghost zones than active zones
          if(dir==IDIR)
            iref = ighost + (i+ighost*(nxi-1))%nxi;
          else
            iref = i;
          if(dir==JDIR)
            jref = jghost + (j+jghost*(nxj-1))%nxj;
          else
            jref = j;
          if(dir==KDIR)
            kref = kghost + (k+kghost*(nxk-1))%nxk;
          else
            kref = k;

          Vs(BX1s,k,j,i) = Vs(BX1s,kref,jref,iref);
      });
    }
    #if DIMENSIONS >=2
      if(dir==IDIR || dir==KDIR) {
        nxi = data->np_int[IDIR];
        nxj = data->np_int[JDIR]+1;
        nxk = data->np_int[KDIR];
        BoundaryForX2s("BoundaryPeriodicX2s",dir,side,
        KOKKOS_LAMBDA (int k, int j, int i) {
          int iref, jref, kref;
            // This hack takes care of cases where we have more ghost zones than active zones
            if(dir==IDIR)
              iref = ighost + (i+ighost*(nxi-1))%nxi;
            else
              iref = i;
            if(dir==JDIR)
              jref = jghost + (j+jghost*(nxj-1))%nxj;
            else
              jref = j;
            if(dir==KDIR)
              kref = kghost + (k+kghost*(nxk-1))%nxk;
            else
              kref = k;

            Vs(BX2s,k,j,i) = Vs(BX2s,kref,jref,iref);
        });
      }
    #endif
    #if DIMENSIONS == 3
      nxi = data->np_int[IDIR];
      nxj = data->np_int[JDIR];
      nxk = data->np_int[KDIR]+1;
      if(dir==IDIR || dir==JDIR) {
        BoundaryForX3s("BoundaryPeriodicX3s",dir,side,
        KOKKOS_LAMBDA (int k, int j, int i) {
          int iref, jref, kref;
            // This hack takes care of cases where we have more ghost zones than active zones
            if(dir==IDIR)
              iref = ighost + (i+ighost*(nxi-1))%nxi;
            else
              iref = i;
            if(dir==JDIR)
              jref = jghost + (j+jghost*(nxj-1))%nxj;
            else
              jref = j;
            if(dir==KDIR)
              kref = kghost + (k+kghost*(nxk-1))%nxk;
            else
              kref = k;

            Vs(BX3s,k,j,i) = Vs(BX3s,kref,jref,iref);
        });
      }
    #endif
  #endif// MHD
}


void HydroBoundary::EnforceReflective(int dir, BoundarySide side ) {
  IdefixArray4D<real> Vc = hydro->Vc;
  const int nxi = data->np_int[IDIR];
  const int nxj = data->np_int[JDIR];
  const int nxk = data->np_int[KDIR];

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

  BoundaryForAll("BoundaryReflective", dir, side,
        KOKKOS_LAMBDA (int n, int k, int j, int i) {
          // ref= 2*ibound -i -1
          // with ibound = nghost on the left and ibount = nghost + nx -1 on the right
          const int iref = (dir==IDIR) ? 2*(ighost + side*(nxi-1)) - i - 1 : i;
          const int jref = (dir==JDIR) ? 2*(jghost + side*(nxj-1)) - j - 1 : j;
          const int kref = (dir==KDIR) ? 2*(kghost + side*(nxk-1)) - k - 1 : k;

          const int sign = (n == VX1+dir) ? -1.0 : 1.0;

          Vc(n,k,j,i) = sign * Vc(n,kref,jref,iref);
        });

  #if MHD==YES
    IdefixArray4D<real> Vs = hydro->Vs;
    if(dir==JDIR || dir==KDIR) {
      BoundaryForX1s("BoundaryReflectiveX1s",dir,side,
        KOKKOS_LAMBDA (int k, int j, int i) {
          // ref= 2*ibound -i -1
          // with ibound = nghost on the left and ibount = nghost + nx -1 on the right
          //const int iref = (dir==IDIR) ? 2*(ighost + side*(nxi-1)) - i - 1 : i;
          const int jref = (dir==JDIR) ? 2*(jghost + side*(nxj-1)) - j - 1 : j;
          const int kref = (dir==KDIR) ? 2*(kghost + side*(nxk-1)) - k - 1 : k;

          Vs(BX1s,k,j,i) = -Vs(BX1s,kref,jref,i);
        });
    }
    #if DIMENSIONS >=2
      if(dir==IDIR || dir==KDIR) {
        BoundaryForX2s("BoundaryReflectiveX2s",dir,side,
          KOKKOS_LAMBDA (int k, int j, int i) {
            const int iref = (dir==IDIR) ? 2*(ighost + side*(nxi-1)) - i - 1 : i;
            //const int jref = (dir==JDIR) ? 2*(jghost + side*(nxj-1)) - j - 1 : j;
            const int kref = (dir==KDIR) ? 2*(kghost + side*(nxk-1)) - k - 1 : k;

              Vs(BX2s,k,j,i) = -Vs(BX2s,kref,j,iref);
          });
      }
    #endif
    #if DIMENSIONS == 3
      if(dir==IDIR || dir==JDIR) {
        BoundaryForX3s("BoundaryReflectiveX3s",dir,side,
          KOKKOS_LAMBDA (int k, int j, int i) {
            const int iref = (dir==IDIR) ? 2*(ighost + side*(nxi-1)) - i - 1 : i;
            const int jref = (dir==JDIR) ? 2*(jghost + side*(nxj-1)) - j - 1 : j;
            //const int kref = (dir==KDIR) ? 2*(kghost + side*(nxk-1)) - k - 1 : k;

            Vs(BX3s,k,j,i) = -Vs(BX3s,k,jref,iref);
          });
      }
    #endif
  #endif// MHD
}

void HydroBoundary::EnforceOutflow(int dir, BoundarySide side ) {
  IdefixArray4D<real> Vc = hydro->Vc;
  const int nxi = data->np_int[IDIR];
  const int nxj = data->np_int[JDIR];
  const int nxk = data->np_int[KDIR];

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

  BoundaryForAll("BoundaryOutflow", dir, side,
        KOKKOS_LAMBDA (int n, int k, int j, int i) {
          // ref= ibound
          // with ibound = nghost on the left and ibound = nghost + nx -1 on the right
          const int iref = (dir==IDIR) ? ighost + side*(nxi-1) : i;
          const int jref = (dir==JDIR) ? jghost + side*(nxj-1) : j;
          const int kref = (dir==KDIR) ? kghost + side*(nxk-1) : k;

          // should it go inwards or outwards?
          // side = 1 on the left and =-1 on the right
          const int sign = 1-2*side;

          if( (n== VX1+dir) && (sign*Vc(n,kref,jref,iref) >= ZERO_F) ) {
            Vc(n,k,j,i) = ZERO_F;
          } else {
            Vc(n,k,j,i) = Vc(n,kref,jref,iref);
          }
        });

  #if MHD==YES
    IdefixArray4D<real> Vs = hydro->Vs;
    if(dir==JDIR || dir==KDIR) {
      BoundaryForX1s("BoundaryOutflowX1s",dir,side,
        KOKKOS_LAMBDA (int k, int j, int i) {
          // with ibound = nghost on the left and ibount = nghost + nx -1 on the right
          //const int iref = (dir==IDIR) ? ighost + side*(nxi-1) : i;
          const int jref = (dir==JDIR) ? jghost + side*(nxj-1) : j;
          const int kref = (dir==KDIR) ? kghost + side*(nxk-1) : k;

          Vs(BX1s,k,j,i) = Vs(BX1s,kref,jref,i);
        });
    }
    #if DIMENSIONS >=2
      if(dir==IDIR || dir==KDIR) {
        BoundaryForX2s("BoundaryOutflowX2s",dir,side,
          KOKKOS_LAMBDA (int k, int j, int i) {
            const int iref = (dir==IDIR) ? ighost + side*(nxi-1) : i;
            //const int jref = (dir==JDIR) ? jghost + side*(nxj-1) : j;
            const int kref = (dir==KDIR) ? kghost + side*(nxk-1) : k;

            Vs(BX2s,k,j,i) = Vs(BX2s,kref,j,iref);
          });
      }
    #endif
    #if DIMENSIONS == 3
      if(dir==IDIR || dir==JDIR) {
        BoundaryForX3s("BoundaryOutflowX3s",dir,side,
          KOKKOS_LAMBDA (int k, int j, int i) {
            const int iref = (dir==IDIR) ? ighost + side*(nxi-1) : i;
            const int jref = (dir==JDIR) ? jghost + side*(nxj-1) : j;
            //const int kref = (dir==KDIR) ? kghost + side*(nxk-1) : k;

            Vs(BX3s,k,j,i) = Vs(BX3s,k,jref,iref);
          });
      }
    #endif
  #endif// MHD
}

void HydroBoundary::EnforceShearingBox(real t, int dir, BoundarySide side) {
  if(dir != IDIR)
    IDEFIX_ERROR("Shearing box boundaries can only be applied along the X1 direction");
  if(data->mygrid->nproc[JDIR]>1)
    IDEFIX_ERROR("Shearing box is not yet compatible with domain decomposition in X2");

  // First thing is to enforce periodicity (already performed by MPI)
  if(data->mygrid->nproc[dir] == 1) EnforcePeriodic(dir, side);

  IdefixArray4D<real> scrh = sBArray;
  IdefixArray4D<real> Vc = hydro->Vc;

  const int nxi = data->np_int[IDIR];
  const int nxj = data->np_int[JDIR];
  const int nxk = data->np_int[KDIR];

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

  // Where does the boundary starts along x1?
  const int istart = side*(ighost+nxi);

  // 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*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
  BoundaryForAll("BoundaryShearingBox", dir, side,
        KOKKOS_LAMBDA ( int n, int k, int j, int i) {
          // 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 = Vc(n,k,jom1,i) - Vc(n,k,jom2,i);
            dqp = Vc(n,k,jo,i) - Vc(n,k,jom1,i);
            dq = (dqp*dqm > ZERO_F ? TWO_F*dqp*dqm/(dqp + dqm) : ZERO_F);

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

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

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

            Fr = Vc(n,k,jop1,i) - 0.5*dq*(1.0+eps);
          }
          scrh(n,k,j,i-istart) = Vc(n,k,jo,i) - eps*(Fr - Fl);
        });
  // Copy scrach back to our boundary
  BoundaryForAll("BoundaryShearingBoxCopy", dir, side,
        KOKKOS_LAMBDA ( int n, int k, int j, int i) {
          Vc(n,k,j,i) = scrh(n,k,j,i-istart);
          if(n==VX2) Vc(n,k,j,i) += sbVelocity;
        });

  // Magnetised version of the same thing
  #if MHD==YES
    IdefixArray4D<real> Vs = hydro->Vs;
    #if DIMENSIONS >= 2
      for(int component = BX2s ; component < DIMENSIONS ; component++) {
        BoundaryFor("BoundaryShearingBoxBXs", dir, side,
        KOKKOS_LAMBDA (int k, int j, int i) {
          // 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 = Vs(component,k,jom1,i) - Vs(component,k,jom2,i);
            dqp = Vs(component,k,jo,i) - Vs(component,k,jom1,i);
            dq = (dqp*dqm > ZERO_F ? TWO_F*dqp*dqm/(dqp + dqm) : ZERO_F);

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

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

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

            Fr = Vs(component,k,jop1,i) - 0.5*dq*(1.0+eps);
          }
          scrh(0,k,j,i-istart) = Vs(component,k,jo,i) - eps*(Fr - Fl);
        });
        // Copy scratch back to our boundary
        BoundaryFor("BoundaryShearingBoxCopyBXs", dir, side,
              KOKKOS_LAMBDA ( int k, int j, int i) {
                Vs(component,k,j,i) = scrh(0,k,j,i-istart);
              });
      }// loop on components
    #endif// DIMENSIONS
  #endif // MHD
}