Program Listing for File slopeLimiter.hpp
↰ Return to documentation for file (hydro/slopeLimiter.hpp
)
// ***********************************************************************************
// 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
// ***********************************************************************************
#ifndef HYDRO_SLOPELIMITER_HPP_
#define HYDRO_SLOPELIMITER_HPP_
#include "hydro.hpp"
#include "dataBlock.hpp"
#include "shockFlattening.hpp"
// Build a left and right extrapolation of the primitive variables along direction dir
// These functions extrapolate the cell prim vars to the faces. Definitions are as followed
//
// | cell i-1 interface i cell i
// |-----------------------------------|------------------------------------||
// Vc(i-1) PrimL(i) PrimR(i) Vc(i)
template<const int dir,
const int nvmax,
const Limiter limiter = Limiter::VanLeer,
const int order = ORDER>
class SlopeLimiter {
public:
SlopeLimiter(IdefixArray4D<real> &Vc, IdefixArray1D<real> &dx, ShockFlattening &sf):
Vc(Vc), dx(dx), flags(sf.flagArray), shockFlattening(sf.isActive) {}
KOKKOS_FORCEINLINE_FUNCTION real MinModLim(const real dvp, const real dvm) const {
real dq= 0.0;
// MinMod
if(dvp*dvm >0.0) {
real dq = ( fabs(dvp) < fabs(dvm) ? dvp : dvm);
}
return(dq);
}
KOKKOS_FORCEINLINE_FUNCTION real LimO3Lim(const real dvp, const real dvm, const real dx) const {
real r = 0.1;
real a,b,c,q, th, lim;
real eta, psi, eps = 1.e-12;
th = dvm/(dvp + 1.e-16);
q = (2.0 + th)/3.0;
a = FMIN(1.5,2.0*th);
a = FMIN(q,a);
b = FMAX(-0.5*th,a);
c = FMIN(q,b);
psi = FMAX(0.0,c);
eta = r*dx;
eta = (dvm*dvm + dvp*dvp)/(eta*eta);
if ( eta <= 1.0 - eps) {
lim = q;
} else if (eta >= 1.0 + eps) {
lim = psi;
} else {
psi = (1.0 - (eta - 1.0)/eps)*q
+ (1.0 + (eta - 1.0)/eps)*psi;
lim = 0.5*psi;
}
return (lim);
}
KOKKOS_FORCEINLINE_FUNCTION real VanLeerLim(const real dvp, const real dvm) const {
real dq= 0.0;
dq = (dvp*dvm > ZERO_F ? TWO_F*dvp*dvm/(dvp + dvm) : ZERO_F);
return(dq);
}
KOKKOS_FORCEINLINE_FUNCTION real McLim(const real dvp, const real dvm) const {
real dq = 0;
if(dvp*dvm >0.0) {
real dqc = 0.5*(dvp+dvm);
real d2q = 2.0*( fabs(dvp) < fabs(dvm) ? dvp : dvm);
dq= fabs(d2q) < fabs(dqc) ? d2q : dqc;
}
return(dq);
}
KOKKOS_FORCEINLINE_FUNCTION real PLMLim(const real dvp, const real dvm) const {
if constexpr(limiter == Limiter::VanLeer) return(VanLeerLim(dvp,dvm));
if constexpr(limiter == Limiter::McLim) return(McLim(dvp,dvm));
if constexpr(limiter == Limiter::MinMod) return(MinModLim(dvp,dvm));
}
template <typename T>
KOKKOS_FORCEINLINE_FUNCTION int sign(T val) const {
return (T(0) < val) - (val < T(0));
}
KOKKOS_FORCEINLINE_FUNCTION void limitPPMFaceValues(const real vm1, const real v0, const real vp1,
const real vp2, real &vph) const {
// if local extremum, then use limited curvature estimate
if( (vp1-vph)*(vph-v0) < 0.0) {
// Collela, eqns. 85
const real deltaL = (vm1-2*v0+vp1);
const real deltaC = 3*(v0-2*vph+vp1);
const real deltaR = (v0-2*vp1+vp2);
// Compute limited curvature estimate
real delta = 0.0;
if(sign(deltaL) == sign(deltaC) && sign(deltaR) == sign(deltaC)) {
const real C = 1.25;
delta = C * FMIN(FABS(deltaL), FABS(deltaR));
delta = sign(deltaC) * FMIN(delta, FABS(deltaC));
}
vph = 0.5*(v0+vp1) - delta / 6.0;
}
}
// This implementation follows the PPM4 scheme of Peterson & Hammet (PH13)
// SIAM J. Sci Comput (2013)
KOKKOS_FORCEINLINE_FUNCTION void getPPMStates(const real vm2, const real vm1, const real v0,
const real vp1, const real vp2, real &vl, real &vr)
const {
const int n = 2;
vr = 7.0/12.0*(v0+vp1) - 1.0/12.0*(vm1+vp2);
vl = 7.0/12.0*(vm1+v0) - 1.0/12.0*(vm2+vp1);
limitPPMFaceValues(vm2,vm1,v0,vp1,vl);
limitPPMFaceValues(vm1,v0,vp1,vp2,vr);
real d2qf = 6.0*(vl + vr - 2.0*v0);
real d2qc0 = vm1 + vp1 - 2.0*v0;
real d2qcp1 = v0 + vp2 - 2.0*vp1;
real d2qcm1 = vm2 + v0 - 2.0*vm1;
real d2q = 0.0;
if(sign(d2qf) == sign(d2qc0) && sign(d2qf) == sign(d2qcp1) && sign(d2qf) == sign(d2qcm1)) {
// smooth extrememum
const real C = 1.25;
d2q = FMIN(FABS(d2qc0),FABS(d2qcp1));
d2q = C * FMIN(FABS(d2qcm1), d2q);
d2q = sign(d2qf) * FMIN(FABS(d2qf), d2q);
}
real qmax = FMAX(FMAX(FABS(vm1),FABS(v0)),FABS(vp1));
real rho = 0.0;
// todo(GL): replace 1e-12 by mixed precision value
if(FABS(d2qf) > 1e-12*qmax) {
rho = d2q / d2qf;
}
// PH13 3.31
if( ((vr - v0)*(v0 - vl) <= 0) || (vm1 - v0)*(v0 - vp1) <= 0 ) {
if(rho <= (1.0 - 1e-12)) {
vl = v0 - rho * (v0 - vl);
vr = v0 + rho * (vr - v0);
}
} else {
// PH13 3.32
if(FABS(vr-v0) >= n*FABS(v0-vl)) {
vr = v0 + n*(v0-vl);
}
if(FABS(vl-v0) >= n*FABS(v0-vr)) {
vl = v0 + n*(v0-vr);
}
}
}
KOKKOS_FORCEINLINE_FUNCTION void ExtrapolatePrimVar(const int i,
const int j,
const int k,
real vL[], real vR[]) const {
// 1-- Store the primitive variables on the left, right, and averaged states
constexpr int ioffset = (dir==IDIR ? 1 : 0);
constexpr int joffset = (dir==JDIR ? 1 : 0);
constexpr int koffset = (dir==KDIR ? 1 : 0);
for(int nv = 0 ; nv < nvmax ; nv++) {
if constexpr(order == 1) {
vL[nv] = Vc(nv,k-koffset,j-joffset,i-ioffset);
vR[nv] = Vc(nv,k,j,i);
} else if constexpr(order == 2) {
real dvm = Vc(nv,k-koffset,j-joffset,i-ioffset)
-Vc(nv,k-2*koffset,j-2*joffset,i-2*ioffset);
real dvp = Vc(nv,k,j,i)-Vc(nv,k-koffset,j-joffset,i-ioffset);
real dv;
if(shockFlattening) {
if(flags(k-koffset,j-joffset,i-ioffset) == FlagShock::Shock) {
// Force slope limiter to minmod
dv = MinModLim(dvp,dvm);
} else {
dv = PLMLim(dvp,dvm);
}
} else { // No shock flattening
dv = PLMLim(dvp,dvm);
}
vL[nv] = Vc(nv,k-koffset,j-joffset,i-ioffset) + HALF_F*dv;
dvm = dvp;
dvp = Vc(nv,k+koffset,j+joffset,i+ioffset) - Vc(nv,k,j,i);
if(shockFlattening) {
if(flags(k,j,i) == FlagShock::Shock) {
dv = MinModLim(dvp,dvm);
} else {
dv = PLMLim(dvp,dvm);
}
} else { // No shock flattening
dv = PLMLim(dvp,dvm);
}
vR[nv] = Vc(nv,k,j,i) - HALF_F*dv;
} else if constexpr(order == 3) {
// 1D index along the chosen direction
const int index = ioffset*i + joffset*j + koffset*k;
real dvm = Vc(nv,k-koffset,j-joffset,i-ioffset)
-Vc(nv,k-2*koffset,j-2*joffset,i-2*ioffset);
real dvp = Vc(nv,k,j,i)-Vc(nv,k-koffset,j-joffset,i-ioffset);
// Limo3 limiter
real dv = dvp * LimO3Lim(dvp, dvm, dx(index-1));
vL[nv] = Vc(nv,k-koffset,j-joffset,i-ioffset) + HALF_F*dv;
// Check positivity
if(nv==RHO) {
// If face element is negative, revert to vanleer
if(vL[nv] <= 0.0) {
dv = (dvp*dvm > ZERO_F ? TWO_F*dvp*dvm/(dvp + dvm) : ZERO_F);
vL[nv] = Vc(nv,k-koffset,j-joffset,i-ioffset) + HALF_F*dv;
}
}
#if HAVE_ENERGY
if(nv==PRS) {
// If face element is negative, revert to vanleer
if(vL[nv] <= 0.0) {
dv = (dvp*dvm > ZERO_F ? TWO_F*dvp*dvm/(dvp + dvm) : ZERO_F);
vL[nv] = Vc(nv,k-koffset,j-joffset,i-ioffset) + HALF_F*dv;
}
}
#endif
dvm = dvp;
dvp = Vc(nv,k+koffset,j+joffset,i+ioffset) - Vc(nv,k,j,i);
// Limo3 limiter
dv = dvm * LimO3Lim(dvm, dvp, dx(index));
vR[nv] = Vc(nv,k,j,i) - HALF_F*dv;
// Check positivity
if(nv==RHO) {
// If face element is negative, revert to vanleer
if(vR[nv] <= 0.0) {
dv = (dvp*dvm > ZERO_F ? TWO_F*dvp*dvm/(dvp + dvm) : ZERO_F);
vR[nv] = Vc(nv,k,j,i) - HALF_F*dv;
}
}
#if HAVE_ENERGY
if(nv==PRS) {
// If face element is negative, revert to vanleer
if(vR[nv] <= 0.0) {
dv = (dvp*dvm > ZERO_F ? TWO_F*dvp*dvm/(dvp + dvm) : ZERO_F);
vR[nv] = Vc(nv,k,j,i) - HALF_F*dv;
}
}
#endif
} else if constexpr(order == 4) {
// Reconstruction in cell i-1
real vm2 = Vc(nv,k-3*koffset,j-3*joffset,i-3*ioffset);;
real vm1 = Vc(nv,k-2*koffset,j-2*joffset,i-2*ioffset);
real v0 = Vc(nv,k-koffset,j-joffset,i-ioffset);
real vp1 = Vc(nv,k,j,i);
real vp2 = Vc(nv,k+koffset,j+joffset,i+ioffset);
real vr,vl;
getPPMStates(vm2, vm1, v0, vp1, vp2, vl, vr);
// vL= left side of current interface (i-1/2)= right side of cell i-1
// Check positivity
if(nv==RHO) {
// If face element is negative, revert to vanleer
if(vr <= 0.0) {
real dv = PLMLim(vp1-v0,v0-vm1);
vr = v0+HALF_F*dv;
}
}
#if HAVE_ENERGY
if(nv==PRS) {
// If face element is negative, revert to vanleer
if(vr <= 0.0) {
real dv = PLMLim(vp1-v0,v0-vm1);
vr = v0+HALF_F*dv;
}
}
#endif
vL[nv] = vr;
// Reconstruction in cell i
vm2 = vm1;
vm1 = v0;
v0 = vp1;
vp1 = vp2;
vp2 = Vc(nv,k+2*koffset,j+2*joffset,i+2*ioffset);
getPPMStates(vm2, vm1, v0, vp1, vp2, vl, vr);
// Check positivity
if(nv==RHO) {
// If face element is negative, revert to vanleer
if(vl <= 0.0) {
real dv = PLMLim(vp1-v0,v0-vm1);
vl = v0-HALF_F*dv;
}
}
#if HAVE_ENERGY
if(nv==PRS) {
// If face element is negative, revert to vanleer
if(vl <= 0.0) {
real dv = PLMLim(vp1-v0,v0-vm1);
vl = v0-HALF_F*dv;
}
}
#endif
vR[nv] = vl;
}
}
}
IdefixArray4D<real> Vc;
IdefixArray1D<real> dx;
IdefixArray3D<FlagShock> flags;
bool shockFlattening{false};
};
#endif // HYDRO_SLOPELIMITER_HPP_