// ***********************************************************************************
// Idefix MHD astrophysical code
//
// Source file src/dataBlock/fargo.cpp
//
// Last modified : 12/2025
//
// Copyright(C) by :
// - Geoffroy Lesur (IPAG/UGA/CNRS - 2021 - 2025)
// - Gaylor Wafflard (IPAG/UGA/CNRS - 2024)
// and other code contributors
//
// Licensed under CeCILL 2.1 License, see COPYING for more information
// ***********************************************************************************
#include
#include
#include
#include "idefix.hpp"
#include "fluid.hpp"
#include "dataBlock.hpp"
#include "fargo.hpp"
Fargo::Fargo(Input &input, int nmax, DataBlock *data) {
idfx::pushRegion("Fargo::Init");
this->data = data;
// A bit of arithmetic to get the sizes of the working array
this->nghost = data->nghost;
this->beg = data->beg;
this->end = data->end;
if(input.CheckBlock("Fargo")) {
std::string opType = input.Get("Fargo","velocity",0);
if(opType.compare("userdef")==0) {
#if GEOMETRY != SPHERICAL && GEOMETRY != POLAR
IDEFIX_ERROR("Fargo+userdef is only compatible with SPHERICAL and POLAR geometries");
#endif
this->type=userdef;
} else if(opType.compare("shearingbox")==0) {
this->type=shearingbox;
#if GEOMETRY != CARTESIAN
// Actually, this has never really been tested, so assumes it doesn't work...
IDEFIX_ERROR("Fargo+shearingbox is only compatible with cartesian geometry");
#endif
} else {
IDEFIX_ERROR("Unknown fargo velocity in the input file. "
"Only userdef and shearingbox are allowed");
}
this->maxShift = input.GetOrSet("Fargo", "maxShift",0, 10);
} else {
// DEPRECATED: initialisation from the [Hydro] block
if(input.CheckEntry("Hydro","fargo")>=0) {
std::string opType = input.Get("Hydro","fargo",0);
if(opType.compare("userdef")==0) {
this->type=userdef;
} else if(opType.compare("shearingbox")==0) {
this->type=shearingbox;
#if GEOMETRY != CARTESIAN
IDEFIX_ERROR("Fargo+shearingbox only compatible with cartesian geometry");
#endif
} else {
IDEFIX_ERROR("Unknown fargo option in the input file. "
"Only userdef and shearingbox are allowed");
}
} else {
IDEFIX_ERROR("Fargo should be enabled in the input file with either userdef "
"or shearingbox options");
}
}
#if GEOMETRY == CARTESIAN || GEOMETRY == POLAR
#if DIMENSIONS < 2
IDEFIX_ERROR("Fargo should be used with DIMENSIONS >= 2 in Cartesian or Polar geometries");
#endif
// Check if there is a domain decomposition in the intended fargo direction
if(data->mygrid->nproc[JDIR]>1) {
haveDomainDecomposition = true;
this->nghost[JDIR] += this->maxShift;
this->beg[JDIR] += this->maxShift;
this->end[JDIR] += this->maxShift;
if(data->np_int[JDIR] < this->maxShift + data->nghost[JDIR]) {
IDEFIX_ERROR("Subdomain size < Fargo:maxShift. "
"Try reducting the number of processes along X2");
}
}
if(this->type==userdef)
this->meanVelocity = IdefixArray2D("FargoVelocity",data->np_tot[KDIR],
data->np_tot[IDIR]);
#elif GEOMETRY == SPHERICAL
#if DIMENSIONS < 3
IDEFIX_ERROR("Fargo should be used with DIMENSIONS == 3 in Spherical geometry");
#endif
// Check if there is a domain decomposition in the intended fargo direction
if(data->mygrid->nproc[KDIR]>1) {
haveDomainDecomposition = true;
this->nghost[KDIR] += this->maxShift;
this->beg[KDIR] += this->maxShift;
this->end[KDIR] += this->maxShift;
if(data->np_int[KDIR] < this->maxShift + data->nghost[KDIR]) {
IDEFIX_ERROR("Subdomain size < Fargo:maxShift. "
"Try reducting the number of processes along X3");
}
}
this->meanVelocity = IdefixArray2D("FargoVelocity",data->np_tot[JDIR],
data->np_tot[IDIR]);
#else
IDEFIX_ERROR("Fargo is not compatible with the GEOMETRY you intend to use");
#endif
// Initialise our scratch space
// Maximum number of variables
int nvar = data->hydro->Vc.extent(0);
if(data->haveDust) {
for(int n = 0 ; n < data->dust.size() ; n++) {
nvar = std::max(nvar,static_cast(data->dust[n]->Vc.extent(0)));
}
}
this->scrhUc = IdefixArray4D("FargoVcScratchSpace",nvar
,end[KDIR]-beg[KDIR] + 2*nghost[KDIR]
,end[JDIR]-beg[JDIR] + 2*nghost[JDIR]
,end[IDIR]-beg[IDIR] + 2*nghost[IDIR]);
#if MHD == YES
if(haveDomainDecomposition) {
this->scrhVs = IdefixArray4D("FargoVsScratchSpace",DIMENSIONS
,end[KDIR]-beg[KDIR] + 2*nghost[KDIR]+KOFFSET
,end[JDIR]-beg[JDIR] + 2*nghost[JDIR]+JOFFSET
,end[IDIR]-beg[IDIR] + 2*nghost[IDIR]+IOFFSET);
} else {
// A separate allocation for scrhVs is only needed with domain decomposition, otherwise,
// we just make a reference to scrhVs
this->scrhVs = data->hydro->Vs;
}
#endif
#ifdef WITH_MPI
if(haveDomainDecomposition) {
std::vector vars;
for(int i=0 ; i < nvar ; i++) {
vars.push_back(i);
}
#if GEOMETRY == CARTESIAN || GEOMETRY == POLAR
const int dirShift = JDIR;
#elif GEOMETRY == SPHERICAL
const int dirShift = KDIR;
#endif
#if MHD == YES
this->mpiExchanger.Init(data->mygrid, dirShift, vars, this->nghost, data->np_int, true);
#else
this->mpiExchanger.Init(data->mygrid, dirShift, vars, this->nghost, data->np_int, false);
#endif
}
#endif
idfx::popRegion();
}
void Fargo::ShowConfig() {
idfx::pushRegion("Fargo::ShowConfig");
if(type==userdef) {
idfx::cout x[IDIR];
xj = data->x[KDIR];
dxk = data->dx[JDIR];
#elif GEOMETRY == SPHERICAL
ibeg = data->beg[IDIR];
iend = data->end[IDIR];
jbeg = data->beg[JDIR];
jend = data->end[JDIR];
xi = data->x[IDIR];
xj = data->x[JDIR];
dxk = data->dx[KDIR];
#endif
idefix_reduce("CheckMaxDt", jbeg, jend, ibeg, iend,
KOKKOS_LAMBDA(int j, int i, real &invDtLoc) {
real w,dphi;
#if GEOMETRY == CARTESIAN
if(fargoType==userdef) {
w = meanV(j,i);
} else if(fargoType==shearingbox) {
w = sbS*xi(i);
}
#elif GEOMETRY == POLAR
w = meanV(j,i)/xi(i);
#elif GEOMETRY == SPHERICAL
w = meanV(j,i)/(xi(i)*sin(xj(j)));
#endif
dphi = dxk(0); // dphi is supposedly constant when using fargo.
invDtLoc = FMAX(invDtLoc, FABS(w/dphi));
},
Kokkos::Max(invDt));
#ifdef WITH_MPI
if(idfx::psize>1) {
MPI_SAFE_CALL(MPI_Allreduce(MPI_IN_PLACE, &invDt, 1, realMPI, MPI_MAX, MPI_COMM_WORLD));
}
#endif
this->dtMax = this->maxShift / invDt;
}
void Fargo::AddVelocity(const real t) {
idfx::pushRegion("Fargo::AddVelocity");
this->AddVelocityFluid(t, data->hydro.get());
if(data->haveDust) {
for(int i = 0 ; i < data->dust.size() ; i++) {
this->AddVelocityFluid(t, data->dust[i].get());
}
}
idfx::popRegion();
}
void Fargo::SubstractVelocity(const real t) {
idfx::pushRegion("Fargo::SubstractVelocity");
this->SubstractVelocityFluid(t, data->hydro.get());
if(data->haveDust) {
for(int i = 0 ; i < data->dust.size() ; i++) {
this->SubstractVelocityFluid(t, data->dust[i].get());
}
}
idfx::popRegion();
}
void Fargo::ShiftSolution(const real t, const real dt) {
idfx::pushRegion("Fargo::ShiftFluid");
this->ShiftFluid(t,dt,data->hydro.get());
if(data->haveDust) {
for(int i = 0 ; i < data->dust.size() ; i++) {
this->ShiftFluid(t,dt,data->dust[i].get());
}
}
idfx::popRegion();
}