FEMASUBNU32NSG4DNXZX54CGCA57PVRGYO46L3A6F2EJ4BCSJ3SAC 5IAXY3XZJTRMMVT2OVIJ6OXQJI6OJPTPCHHA4IVLVMHANCCC5NKAC 2DKSL6DKZAIYQUJGDULORCKU5K4Z5Z3W4RIKQYDSLKMCNQNDZFBAC MSBBCXVGD3GRLE5KAI6BKAFRV7SQUWI2SNN43AJAUD3ISRCEXY6QC 722HZ7UFINNE3YKSYKP2NHZ5XEG5QQLQHSKC7PREJZR3EX6RDYUAC 33IC3UHCEPZLGS5ACS2JXHGT6CRU5LXU6PM6RDHCERPOIELVRVXQC 7Y37CMWGUQY3IOMFZW46UINY2W5DIEE3TXGN7RDKYZO6FHCLHDBQC UZAKARMGORRQG733ZUPJOEGL5FG243I32NCC2SRSFDCZKUQ5A52QC RCLGQ2LZMFVPBPTU2G55DJ6HZPOGGTPZRZCY54VGP6YLHANJ2LAQC BVR7DVINVPQG7PA6Z7QYVYNQ43YZL7XCC6AOMSMWMGAAB2Q43STAC IEFVL4X5BPCLUX5D24ESP7FTFSX2HMCD2GOIFCMSWNUYEHQEJOIAC TVBD244E7Q7WV44CRBTFST535NUP3JAZH6OLL4IKDR3OWEXSU7HAC U77PE56ICORZNQW33NXGSEMW7GDHCSSZ4EXB6OHBJSHEG6WHYSSQC VAF66DTVLDWNG7N2PEQYEH4OH5SPSMFBXKPR2PU67IIM6CVPCJ7AC KG47IF4CPCUT3BHS34WDRHTH5HYMBTY4OSTB3X7APR2E5ZJ32IYQC PQB3EKQ6MBCXPTW4HB7SGMSTOTYMB3EFZX2573OFCQI6PSOEKCSQC RTNZAS3UPI6GG3KY4Z5WVXJ4R2YF5427BB6WAV3GHRS5W7XPOSUQC 2DD222JSYRPHTXKSRXLSOMSCQPZUORNFLLO2P3GMIDELAAMD5MEQC AEVGZIZEUIC52MCK3J4V547YEV2R4YQL3JUJW7FSP4R34PSZ43DAC WASO7G5FJXRXWNH2U2FLUNEKU6VE63OI3HUYP64BVD4LMD6KE7OQC QN2UTSQP4IMCMVXZNR24J22N24ASGXF6EWXQ2P3VWVQERUPFAFDQC UUGQGVC4WEKN64WAP7F5QPS2UHGQB5ZLMFRIYNWKMIEBDO3QRX4AC WE6MDRN5SPK3THM4COLQFE3IUWBCQ5ZYUIAUCBJAZVEMMOVTNBOAC 2XYZZL42IEZHGDJA6NDKGSQKGJP24LOTLFJ6RNHOKWHHSUYIHGKQC ORAAQXS3UNKXYDJBCTHNEIDJIZDHWMM5EVCZKVMFRKQK2NIQNJGAC TOBGHRPKEPSXDN56WGSZNWOMCBVJ4KUSLWYWI56MC2RR3MM3KLZAC ActiveThorns = "AMReXIOUtilWaveToyAMReX"$nlevels = 7$ncells = 32Cactus::cctk_show_schedule = no# Cactus::terminate = "iteration"# Cactus::cctk_itlast = 64 #TODO 0Cactus::terminate = "time"Cactus::cctk_final_time = 50.0# AMReX::verbose = yesAMReX::xmin = -100.0AMReX::ymin = -100.0AMReX::zmin = -100.0AMReX::xmax = +100.0AMReX::ymax = +100.0AMReX::zmax = +100.0AMReX::ncells_x = $ncellsAMReX::ncells_y = $ncellsAMReX::ncells_z = $ncells# AMReX::periodic_x = no# AMReX::periodic_y = no# AMReX::periodic_z = noAMReX::max_num_levels = $nlevelsAMReX::regrid_every = 16AMReX::regrid_error_threshold = 0.1AMReX::dtfac = 0.5WaveToyAMReX::initial_condition = "central potential"WaveToyAMReX::boundary_condition = "initial"WaveToyAMReX::central_point_charge = 1.0WaveToyAMReX::central_point_radius = 1.0IO::out_dir = $parfileIO::out_every = $ncells * 2 ** ($nlevels - 1) / 32
const Box &fbx = mfi.fabbox();const Box &bx = mfi.tilebox();const Dim3 imin = lbound(bx);int ash[3], lsh[3];for (int d = 0; d < dim; ++d)ash[d] = fbx[orient(d, 1)] - fbx[orient(d, 0)] + 1;// Note: This excludes ghosts, it's not the proper Cactus lshfor (int d = 0; d < dim; ++d)lsh[d] = bx[orient(d, 1)] - bx[orient(d, 0)] + 1;
GridPtrDesc grid(leveldata, mfi);
const CCTK_REAL *restrict err = vars.ptr(imin.x, imin.y, imin.z, vi);const Array4<char> &tagarr = tags.array(mfi);constexpr int di = 1;const int dj = di * ash[0];const int dk = dj * ash[1];
CCTK_REAL *restrict const err = grid.ptr(vars, vi);const Array4<char> &tag = tags.array(mfi);
for (int k = 0; k < lsh[2]; ++k) {for (int j = 0; j < lsh[1]; ++j) {#pragma omp simdfor (int i = 0; i < lsh[0]; ++i) {int idx = di * i + dj * j + dk * k;tagarr(imin.x + i, imin.y + j, imin.z + k) =err[idx] >= regrid_error_threshold ? TagBox::SET : TagBox::CLEAR;}}}
grid.loop_int([&](int i, int j, int k, int idx) {tag(grid.cactus_offset.x + i, grid.cactus_offset.y + j,grid.cactus_offset.z + k) =err[idx] >= regrid_error_threshold ? TagBox::SET : TagBox::CLEAR;});
// Set grid functions to nanMultiFab &mfab = *groupdata.mfab.at(tl);auto mfitinfo = MFItInfo().SetDynamic(true).EnableTiling({max_tile_size_x, max_tile_size_y, max_tile_size_z});#pragma omp parallelfor (MFIter mfi(mfab, mfitinfo); mfi.isValid(); ++mfi) {GridPtrDesc grid(leveldata, mfi);const Array4<CCTK_REAL> &vars = mfab.array(mfi);for (int vi = 0; vi < groupdata.numvars; ++vi) {CCTK_REAL *restrict const ptr = grid.ptr(vars, vi);grid.loop_all([&](int i, int j, int k, int idx) { ptr[idx] = 0.0 / 0.0; });}}}
// Set grid functions to nanauto mfitinfo = MFItInfo().SetDynamic(true).EnableTiling({max_tile_size_x, max_tile_size_y, max_tile_size_z});#pragma omp parallelfor (MFIter mfi(*mfab, mfitinfo); mfi.isValid(); ++mfi) {GridPtrDesc grid(leveldata, mfi);const Array4<CCTK_REAL> &vars = mfab->array(mfi);for (int vi = 0; vi < groupdata.numvars; ++vi) {CCTK_REAL *restrict const ptr = grid.ptr(vars, vi);grid.loop_all([&](int i, int j, int k, int idx) { ptr[idx] = 0.0 / 0.0; });}}
// Set grid functions to nanauto mfitinfo = MFItInfo().SetDynamic(true).EnableTiling({max_tile_size_x, max_tile_size_y, max_tile_size_z});#pragma omp parallelfor (MFIter mfi(*mfab, mfitinfo); mfi.isValid(); ++mfi) {GridPtrDesc grid(leveldata, mfi);const Array4<CCTK_REAL> &vars = mfab->array(mfi);for (int vi = 0; vi < groupdata.numvars; ++vi) {CCTK_REAL *restrict const ptr = grid.ptr(vars, vi);grid.loop_all([&](int i, int j, int k, int idx) { ptr[idx] = 0.0 / 0.0; });}}
pp.add("amrex.verbose", true);pp.add("amrex.signal_handling", false);
// Don't catch Unix signals. If signals are caught, we don't get// core files.pp.add("amrex.signal_handling", 0);// Throw exceptions for failing AMReX assertions. With exceptions,// we get core files.pp.add("amrex.throw_exception", 1);
imin[d] = cctk_bbox[2 * d] ? 0 : cctk_nghostzones[d];imax[d] = cctk_lsh[d] - (cctk_bbox[2 * d + 1] ? 0 : cctk_nghostzones[d]);
imin[d] = grid.bbox[2 * d] ? 0 : grid.nghostzones[d];imax[d] = grid.lsh[d] - (grid.bbox[2 * d + 1] ? 0 : grid.nghostzones[d]);
<< component << "\t" << isghost << "\t" << (lbnd[0] + i) << "\t"<< (lbnd[1] + j) << "\t" << (lbnd[2] + k) << "\t" << x << "\t" << y<< "\t" << z;
<< component << "\t" << isghost << "\t" << (grid.lbnd[0] + i) << "\t"<< (grid.lbnd[1] + j) << "\t" << (grid.lbnd[2] + k) << "\t" << x<< "\t" << y << "\t" << z;
const Box &fbx = mfi.fabbox(); // allocated arrayconst Box &bx = mfi.tilebox(); // current region (without ghosts)const Box &gbx = mfi.growntilebox(); // current region (with ghosts)const Dim3 imin = lbound(gbx);// Local shapeint lsh[dim];for (int d = 0; d < dim; ++d)lsh[d] = gbx[orient(d, 1)] - gbx[orient(d, 0)] + 1;// Allocated shapeint ash[dim];for (int d = 0; d < dim; ++d)ash[d] = fbx[orient(d, 1)] - fbx[orient(d, 0)] + 1;// Local extentint lbnd[dim];for (int d = 0; d < dim; ++d)lbnd[d] = gbx[orient(d, 0)];// Boundariesint bbox[2 * dim];for (int d = 0; d < dim; ++d)for (int f = 0; f < 2; ++f)bbox[2 * d + f] = bx[orient(d, f)] == gbx[orient(d, f)];// Number of ghost zonesint nghostzones[dim];for (int d = 0; d < dim; ++d)nghostzones[d] = mfab.fb_nghost[d];
GridPtrDesc grid(leveldata, mfi);
#ifndef LOOP_HXX#define LOOP_HXX#include <cctk.h>#include <array>#include <iostream>#include <string>namespace Loop {using namespace std;constexpr int dim = 3;struct GridDescBase {array<int, dim> gsh;array<int, dim> lbnd, ubnd;array<int, dim> lsh;array<int, dim> ash;array<int, 2 * dim> bbox;array<int, dim> nghostzones;array<int, dim> tmin, tmax;template <typename T, size_t N>static void output(ostream &os, const string &str, const array<T, N> &arr) {os << str << ":[";for (size_t n = 0; n < N; ++n) {if (n > 0)os << ",";os << arr[n];}os << "]";}friend ostream &operator<<(ostream &os, const GridDescBase &grid) {os << "GridDescBase{";output(os, "gsh", grid.gsh);output(os, ",lbnd", grid.lbnd);output(os, ",ubnd", grid.ubnd);output(os, ",lsh", grid.lsh);output(os, ",bbox", grid.bbox);output(os, ",nghostzones", grid.nghostzones);output(os, ",tmin", grid.tmin);output(os, ",tmax", grid.tmax);os << "}";return os;}protected:GridDescBase();public:GridDescBase(const cGH *cctkGH);// Loop over a given boxtemplate <typename F>void loop_box(const F &f, const array<int, dim> &restrict imin,const array<int, dim> &restrict imax) const {// cout << *this;// output(cout, ",imin", imin);// output(cout, ",imax", imax);// cout << "\n";for (int d = 0; d < dim; ++d)if (imin[d] >= imax[d])return;constexpr int di = 1;const int dj = di * ash[0];const int dk = dj * ash[1];for (int k = imin[2]; k < imax[2]; ++k) {for (int j = imin[1]; j < imax[1]; ++j) {#pragma omp simdfor (int i = imin[0]; i < imax[0]; ++i) {int idx = i * di + j * dj + k * dk;f(i, j, k, idx);}}}}// Loop over all pointstemplate <typename F> void loop_all(const F &f) const {array<int, dim> imin, imax;for (int d = 0; d < dim; ++d) {imin[d] = max(tmin[d], 0);imax[d] = min(tmax[d], lsh[d]);}loop_box(f, imin, imax);}// Loop over all interior pointstemplate <typename F> void loop_int(const F &f) const {array<int, dim> imin, imax;for (int d = 0; d < dim; ++d) {imin[d] = max(tmin[d], nghostzones[d]);imax[d] = min(tmax[d], lsh[d] - nghostzones[d]);}loop_box(f, imin, imax);}// Loop over all outer boundary points. This excludes ghost faces, but// includes ghost edges/corners on non-ghost faces.template <typename F> void loop_bnd(const F &f) const {for (int dir = 0; dir < dim; ++dir) {for (int face = 0; face < 2; ++face) {if (bbox[2 * dir + face]) {array<int, dim> imin, imax;for (int d = 0; d < dim; ++d) {// by default, include interior and outer boundaries and ghostsimin[d] = 0;imax[d] = lsh[d];// avoid covering edges and corners multiple timesif (d < dir) {if (bbox[2 * d])imin[d] = nghostzones[d]; // only interiorif (bbox[2 * d + 1])imax[d] = lsh[d] - nghostzones[d]; // only interior}}// only one face on outer boundaryif (face == 0)imax[dir] = nghostzones[dir];elseimin[dir] = lsh[dir] - nghostzones[dir];for (int d = 0; d < dim; ++d) {imin[d] = max(tmin[d], imin[d]);imax[d] = min(tmax[d], imax[d]);}loop_box(f, imin, imax);}}}}};template <typename F> void loop_all(const cGH *cctkGH, const F &f) {GridDescBase(cctkGH).loop_all(f);}template <typename F> void loop_int(const cGH *cctkGH, const F &f) {GridDescBase(cctkGH).loop_int(f);}template <typename F> void loop_bnd(const cGH *cctkGH, const F &f) {GridDescBase(cctkGH).loop_bnd(f);}} // namespace Loop#endif // #ifndef LOOP_HXX
constexpr int di = 1;const int dj = di * ash[0];const int dk = dj * ash[1];for (int k = 0; k < lsh[2]; ++k) {for (int j = 0; j < lsh[1]; ++j) {#pragma omp simdfor (int i = 0; i < lsh[0]; ++i) {int idx = di * i + dj * j + dk * k;red = reduction<T>(red, reduction<T>(dV, ptr[idx]));}}}
grid.loop_int([&](int i, int j, int k, int idx) {red = reduction<T>(red, reduction<T>(dV, ptr[idx]));});
const Dim3 imin = lbound(bx);int ash[3], lsh[3];for (int d = 0; d < dim; ++d)ash[d] = fbx[orient(d, 1)] - fbx[orient(d, 0)] + 1;// Note: This excludes ghosts, it's not the proper Cactus lshfor (int d = 0; d < dim; ++d)lsh[d] = bx[orient(d, 1)] - bx[orient(d, 0)] + 1;
: min(x), max(x), sum(V * x), sum2((V * x) * (V * x)), vol(V),maxabs(fabs(x)), sumabs(fabs(V * x)), sum2abs(fabs(V * x) * fabs(V * x)) {}
: min(x), max(x), sum(V * x), sum2(pow(V * x, 2)), vol(V), maxabs(fabs(x)),sumabs(fabs(V * x)), sum2abs(pow(fabs(V * x), 2)) {}
// Set up a GridDesc} // namespace AMReXnamespace Loop {GridDescBase::GridDescBase() {}GridDescBase::GridDescBase(const cGH *restrict cctkGH) {for (int d = 0; d < dim; ++d)gsh[d] = cctkGH->cctk_gsh[d];for (int d = 0; d < dim; ++d) {lbnd[d] = cctkGH->cctk_lbnd[d];ubnd[d] = cctkGH->cctk_ubnd[d];}for (int d = 0; d < dim; ++d)lsh[d] = cctkGH->cctk_lsh[d];for (int d = 0; d < dim; ++d)ash[d] = cctkGH->cctk_ash[d];for (int d = 0; d < dim; ++d)for (int f = 0; f < 2; ++f)bbox[2 * d + f] = cctkGH->cctk_bbox[2 * d + f];for (int d = 0; d < dim; ++d)nghostzones[d] = cctkGH->cctk_nghostzones[d];// Check whether we are in local modeassert(cctkGH->cctk_bbox[0] != AMReX::undefined);int thread_num = omp_get_thread_num();const cGH *restrict threadGH = &AMReX::thread_local_cctkGH.at(thread_num);// Check whether this is the correct cGH structureassert(cctkGH == threadGH);const AMReX::TileBox &restrict tilebox =AMReX::thread_local_tilebox.at(thread_num);for (int d = 0; d < dim; ++d) {tmin[d] = tilebox.tile_min[d];tmax[d] = tilebox.tile_max[d];}}} // namespace Loopnamespace AMReX {GridDesc::GridDesc(const GHExt::LevelData &leveldata, const MFIter &mfi) {const Box &fbx = mfi.fabbox(); // allocated arrayconst Box &vbx = mfi.validbox(); // interior region (without ghosts)// const Box &bx = mfi.tilebox(); // current region (without ghosts)const Box &gbx = mfi.growntilebox(); // current region (with ghosts)const Box &domain = ghext->amrcore->Geom(leveldata.level).Domain();// The number of ghostzones in each directionfor (int d = 0; d < dim; ++d)nghostzones[d] = mfi.theFabArrayBase().nGrowVect()[d];// Global shapefor (int d = 0; d < dim; ++d)gsh[d] =domain[orient(d, 1)] - domain[orient(d, 0)] + 1 + 2 * nghostzones[d];// Local shapefor (int d = 0; d < dim; ++d)lsh[d] = fbx[orient(d, 1)] - fbx[orient(d, 0)] + 1;// Allocated shapefor (int d = 0; d < dim; ++d)ash[d] = fbx[orient(d, 1)] - fbx[orient(d, 0)] + 1;// Local extentfor (int d = 0; d < dim; ++d) {lbnd[d] = fbx[orient(d, 0)] + nghostzones[d];ubnd[d] = fbx[orient(d, 1)] + nghostzones[d];}// Boundariesfor (int d = 0; d < dim; ++d)for (int f = 0; f < 2; ++f)bbox[2 * d + f] = vbx[orient(d, f)] == domain[orient(d, f)];// Thread tile boxfor (int d = 0; d < dim; ++d) {tmin[d] = gbx[orient(d, 0)] - fbx[orient(d, 0)];tmax[d] = gbx[orient(d, 1)] + 1 - fbx[orient(d, 0)];}// Check constraintsfor (int d = 0; d < dim; ++d) {// Domain sizeassert(gsh[d] >= 0);// Local sizeassert(lbnd[d] >= 0);assert(lsh[d] >= 0);assert(lbnd[d] + lsh[d] <= gsh[d]);assert(ubnd[d] == lbnd[d] + lsh[d] - 1);
// Internal representationassert(ash[d] >= 0);assert(ash[d] >= lsh[d]);// Ghost zonesassert(nghostzones[d] >= 0);assert(2 * nghostzones[d] <= lsh[d]);// Tilesassert(tmin[d] >= 0);assert(tmax[d] >= tmin[d]);assert(tmax[d] <= lsh[d]);}}GridPtrDesc::GridPtrDesc(const GHExt::LevelData &leveldata, const MFIter &mfi): GridDesc(leveldata, mfi) {const Box &fbx = mfi.fabbox(); // allocated arraycactus_offset = lbound(fbx);}
void enter_local_mode(cGH *restrict cctkGH,GHExt::LevelData &restrict leveldata, const MFIter &mfi) {const Box &fbx = mfi.fabbox(); // allocated array// const Box &vbx = mfi.validbox(); // interior region (without ghosts)const Box &bx = mfi.tilebox(); // current region (without ghosts)const Box &gbx = mfi.growntilebox(); // current region (with ghosts)
void enter_local_mode(cGH *restrict cctkGH, TileBox &restrict tilebox,const GHExt::LevelData &restrict leveldata,const MFIter &mfi) {GridPtrDesc grid(leveldata, mfi);
// Check constraintsfor (int d = 0; d < dim; ++d) {// Domain sizeassert(cctkGH->cctk_gsh[d] >= 0);// Local sizeassert(cctkGH->cctk_lbnd[d] >= 0);assert(cctkGH->cctk_lsh[d] >= 0);assert(cctkGH->cctk_lbnd[d] + cctkGH->cctk_lsh[d] <= cctkGH->cctk_gsh[d]);assert(cctkGH->cctk_ubnd[d] ==cctkGH->cctk_lbnd[d] + cctkGH->cctk_lsh[d] - 1);// Internal representationassert(cctkGH->cctk_ash[d] >= 0);assert(cctkGH->cctk_ash[d] >= cctkGH->cctk_lsh[d]);// Ghost zonesassert(cctkGH->cctk_nghostzones[d] >= 0);assert(2 * cctkGH->cctk_nghostzones[d] <= cctkGH->cctk_lsh[d]);// Tilesassert(tilebox.tile_min[d] >= 0);assert(tilebox.tile_max[d] >= tilebox.tile_min[d]);assert(tilebox.tile_max[d] <= cctkGH->cctk_lsh[d]);}
extern "C" void AMReX_GetTileExtent(const void *restrict cctkGH_,CCTK_INT *restrict tile_min,CCTK_INT *restrict tile_max) {const cGH *restrict cctkGH = static_cast<const cGH *>(cctkGH_);// Check whether we are in local modeassert(cctkGH->cctk_bbox[0] != undefined);int thread_num = omp_get_thread_num();const cGH *restrict threadGH = &thread_local_cctkGH.at(thread_num);// Check whether this is the correct cGH structureassert(cctkGH == threadGH);
////////////////////////////////////////////////////////////////////////////////struct GridDesc : GridDescBase {GridDesc() = delete;GridDesc(const GHExt::LevelData &leveldata, const MFIter &mfi);GridDesc(const cGH *cctkGH) : GridDescBase(cctkGH) {}};struct GridPtrDesc : GridDesc {Dim3 cactus_offset;GridPtrDesc() = delete;GridPtrDesc(const GHExt::LevelData &leveldata, const MFIter &mfi);template <typename T> T *ptr(const Array4<T> &vars, int vi) const {return vars.ptr(cactus_offset.x, cactus_offset.y, cactus_offset.z, vi);}template <typename T>T &idx(const Array4<T> &vars, int i, int j, int k, int vi) const {return vars(cactus_offset.x + i, cactus_offset.y + i, cactus_offset.z + j,vi);}};
3. Scalar particlesLagrangian:S = int 1/2 \eta^ab d_a u d_b u + 1/2 m^2 u^2 + \rho uequation of motion:0 = dS/du = - \eta^ab d_a d_b u + m^2 u + \rhononsense: [0 = dS/d\rho = u]nonsense: [0 = dS/dm = m u^2]Ansatz:\rho = q \delta(x)0 = dS/dx = -q (grad u)(x)0 = dS/dq = u(x)
CCTK_REAL psi_abs "Typical value of psi to determine absolute error" STEERABLE=always{(0.0:* :: ""} 1.0
} "Set up initial conditions for the wave equation"
} "Boundary conditions for the wave equation"SCHEDULE WaveToyAMReX_Boundaries AT initial AFTER WaveToyAMReX_Sync{LANG: C} "Boundary conditions for the wave equation"SCHEDULE WaveToyAMReX_NaNCheck_current AT postinitial{LANG: C} "Check for nans in the state vector"
SCHEDULE WaveToyAMReX_NaNCheck_current AT postregridinitial AFTER WaveToyAMReX_Boundaries{LANG: C} "Check for nans in the state vector"SCHEDULE WaveToyAMReX_Sync AT postregrid{LANG: CSYNC: phi} "Boundary conditions for the wave equation"SCHEDULE WaveToyAMReX_Boundaries AT postregrid AFTER WaveToyAMReX_Sync{LANG: C} "Boundary conditions for the wave equation"SCHEDULE WaveToyAMReX_NaNCheck_current AT postregrid AFTER WaveToyAMReX_Boundaries{LANG: C} "Check for nans in the state vector"SCHEDULE WaveToyAMReX_NaNCheck_past AT prestep{LANG: C} "Check for nans in the state vector"
// Squaretemplate <typename T> T sqr(T x) { return x * x; }
// Spline with compact support of radius 1 and volume 1template <typename T> T spline(T r) {if (r >= 1.0)return 0.0;constexpr CCTK_REAL f =dim == 1 ? 1.0: dim == 2 ? 24.0 / 7.0 / M_PI : dim == 3 ? 4.0 / M_PI : -1;const T r2 = pow(r, 2);return f * (r <= 0.5 ? 1 - 2 * r2 : 2 + r * (-4 + 2 * r));}// The potential for the splinetemplate <typename T> T spline_potential(T r) {// \Laplace u = 4 \pi \rhoif (r >= 1.0)return -1 / r;static_assert(dim == 3, "");const T r2 = pow(r, 2);return r <= 0.5? -7 / T(3) + r2 * (8 / T(3) - 8 / T(5) * r2): (1 / T(15) +r * (-40 / T(15) +r2 * (80 / T(15) + r * (-80 / T(15) + r * 24 / T(15))))) /r;}
T omega = sqrt(sqr(kx) + sqr(ky) + sqr(kz));return exp(-0.5 * sqr(sin(kx * x + ky * y + kz * z - omega * t) / width));
T omega = sqrt(pow(kx, 2) + pow(ky, 2) + pow(kz, 2));return exp(-0.5 * pow(sin(kx * x + ky * y + kz * z - omega * t) / width, 2));}// Gaussiantemplate <typename T> T gaussian(T t, T x, T y, T z) {DECLARE_CCTK_PARAMETERS;// u(t,r) = (f(r-t) - f(r+t)) / rT r = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));auto f = [&](T x) { return exp(-0.5 * pow(x / width, 2)); };auto fx = [&](T x) { return -x / pow(width, 2) * f(x); };if (r < 1.0e-8)// Use L'Hôpital's rule for small rreturn fx(r - t) - fx(r + t);elsereturn (f(r - t) - f(r + t)) / r;
for (int k = imin[2]; k < imax[2]; ++k) {for (int j = imin[1]; j < imax[1]; ++j) {#pragma omp simdfor (int i = imin[0]; i < imax[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = standing(t, x, y, z);// phi_p[idx] = standing(t - dt, x, y, z);psi[idx] = timederiv(standing, dt)(t, x, y, z);}}}
Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = standing(t, x, y, z);// phi_p[idx] = standing(t - dt, x, y, z);psi[idx] = timederiv(standing, dt)(t, x, y, z);});
for (int k = imin[2]; k < imax[2]; ++k) {for (int j = imin[1]; j < imax[1]; ++j) {#pragma omp simdfor (int i = imin[0]; i < imax[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = gaussian(t, x, y, z);// phi_p[idx] = gaussian(t - dt, x, y, z);psi[idx] = timederiv(gaussian, dt)(t, x, y, z);}}}
Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = periodic_gaussian(t, x, y, z);// phi_p[idx] = periodic_gaussian(t - dt, x, y, z);psi[idx] = timederiv(periodic_gaussian, dt)(t, x, y, z);});} else if (CCTK_EQUALS(initial_condition, "Gaussian")) {Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = gaussian(t, x, y, z);// phi_p[idx] = gaussian(t - dt, x, y, z);psi[idx] = timederiv(gaussian, dt)(t, x, y, z);});} else if (CCTK_EQUALS(initial_condition, "central potential")) {Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = central_potential(t, x, y, z);// phi_p[idx] = central_potential(t - dt, x, y, z);psi[idx] = timederiv(central_potential, dt)(t, x, y, z);});
for (int k = 0; k < cctk_lsh[2]; ++k) {for (int j = 0; j < cctk_lsh[1]; ++j) {#pragma omp simdfor (int i = 0; i < cctk_lsh[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL base_phi = fabs(phi[idx]) + fabs(phi_abs);CCTK_REAL errx_phi =fabs(phi[idx - di] - 2 * phi[idx] + phi[idx + di]) / base_phi;CCTK_REAL erry_phi =fabs(phi[idx - dj] - 2 * phi[idx] + phi[idx + dj]) / base_phi;CCTK_REAL errz_phi =fabs(phi[idx - dk] - 2 * phi[idx] + phi[idx + dk]) / base_phi;regrid_error[idx] = errx_phi + erry_phi + errz_phi;}}
Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL base_phi = fabs(phi[idx]) + fabs(phi_abs);CCTK_REAL errx_phi =fabs(phi[idx - di] - 2 * phi[idx] + phi[idx + di]) / base_phi;CCTK_REAL erry_phi =fabs(phi[idx - dj] - 2 * phi[idx] + phi[idx + dj]) / base_phi;CCTK_REAL errz_phi =fabs(phi[idx - dk] - 2 * phi[idx] + phi[idx + dk]) / base_phi;CCTK_REAL base_psi = fabs(psi[idx]) + fabs(psi_abs);CCTK_REAL errx_psi =fabs(psi[idx - di] - 2 * psi[idx] + psi[idx + di]) / base_psi;CCTK_REAL erry_psi =fabs(psi[idx - dj] - 2 * psi[idx] + psi[idx + dj]) / base_psi;CCTK_REAL errz_psi =fabs(psi[idx - dk] - 2 * psi[idx] + psi[idx + dk]) / base_psi;regrid_error[idx] =errx_phi + erry_phi + errz_phi + errx_psi + erry_psi + errz_psi;});}extern "C" void WaveToyAMReX_Evolve(CCTK_ARGUMENTS) {DECLARE_CCTK_ARGUMENTS;DECLARE_CCTK_PARAMETERS;const CCTK_REAL dt = CCTK_DELTA_TIME;CCTK_REAL x0[dim], dx[dim];for (int d = 0; d < dim; ++d) {x0[d] = CCTK_ORIGIN_SPACE(d);dx[d] = CCTK_DELTA_SPACE(d);
constexpr int di = 1;const int dj = di * cctk_ash[0];const int dk = dj * cctk_ash[1];Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];CCTK_REAL r = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));CCTK_REAL ddx_phi =(phi_p[idx - di] - 2 * phi_p[idx] + phi_p[idx + di]) / pow(dx[0], 2);CCTK_REAL ddy_phi =(phi_p[idx - dj] - 2 * phi_p[idx] + phi_p[idx + dj]) / pow(dx[1], 2);CCTK_REAL ddz_phi =(phi_p[idx - dk] - 2 * phi_p[idx] + phi_p[idx + dk]) / pow(dx[2], 2);// phi[idx] = 2 * phi_p[idx] - phi_p_p[idx] +// pow(dt, 2) * (ddx_phi + ddy_phi + ddz_phi);psi[idx] = psi_p[idx] +dt * (ddx_phi + ddy_phi + ddz_phi - pow(mass, 2) * phi_p[idx] -4 * M_PI * central_point_charge *pow(central_point_radius, -dim) *spline(r / central_point_radius));phi[idx] = phi_p[idx] + dt * psi[idx];});
extern "C" void WaveToyAMReX_Evolve(CCTK_ARGUMENTS) {
// Miguel Alcubierre, Gabrielle Allen, Gerd Lanfermann//// Routines for applying radiation boundary conditions//// Taken from// <Cactus/arrangements/CactusBase/Boundary/src/RadiationBoundary.c>//// The radiative boundary condition that is implemented is://// f = f0 + u(r - v*t) / r + h(r + v*t) / r//// That is, I assume outgoing radial waves with a 1/r fall off, and// the correct asymptotic value f0, plus I include the possibility of// incoming waves as well (these incoming waves should be modeled// somehow).//// The condition above leads to the differential equation://// (x / r) d f + v d f + v x (f - f0) / r^2 = v x H / r^2// i t i i i//// where x_i is the normal direction to the given boundaries, and H =// 2 dh(s)/ds.//// So at a given boundary I only worry about derivatives in the normal// direction. Notice that u(r-v*t) has dissapeared, but we still do// not know the value of H.//// To get H what I do is the following: I evaluate the expression one// point in from the boundary and solve for H there. We now need a way// of extrapolation H to the boundary. For this I assume that H falls// off as a power law://// H = k/r**n => d H = - n H/r// i//// The value of n is is defined by the parameter "radpower". If this// parameter is negative, H is forced to be zero (this corresponds to// pure outgoing waves and is the default).//// The behaviour I have observed is the following: Using H=0 is very// stable, but has a very bad initial transient. Taking n to be 0 or// positive improves the initial behaviour considerably, but// introduces a drift that can kill the evolution at very late times.// Empirically, the best value I have found is n=2, for which the// initial behaviour is very nice, and the late time drift is quite// small.//// Another problem with this condition is that it does not use the// physical characteristic speed, but rather it assumes a wave speed// of v, so the boundaries should be out in the region where the// characteristic speed is constant. Notice that this speed does not// have to be 1. For gauge quantities {alpha, phi, trK} we can have a// different asymptotic speed, which is why the value of v is passed// as a parameter.#if 0extern "C" void WaveToyAMReX_RadiativeBoundaries(CCTK_ARGUMENTS) {
int gbox[2 * dim]; // Are the boundaries (if any) ghosts zones?for (int d = 0; d < dim; ++d) {// #error "NEED lbnd>=0 ALWAYS"gbox[2 * d] = cctk_lbnd[d] > 0;gbox[2 * d + 1] = cctk_lbnd[d] + cctk_lsh[d] < cctk_gsh[d];}// CCTK_VINFO("lbnd[%d,%d,%d]", cctk_lbnd[0], cctk_lbnd[1], cctk_lbnd[2]);// CCTK_VINFO("lsh[%d,%d,%d]", cctk_lsh[0], cctk_lsh[1], cctk_lsh[2]);// CCTK_VINFO("gsh[%d,%d,%d]", cctk_gsh[0], cctk_gsh[1], cctk_gsh[2]);// CCTK_VINFO("gbox[%d,%d,%d,%d,%d,%d]", gbox[0], gbox[1], gbox[2], gbox[3],// gbox[4], gbox[5]);
for (int k = imin[2]; k < imax[2]; ++k) {for (int j = imin[1]; j < imax[1]; ++j) {
for (int dir = 0; dir < dim; ++dir) {for (int face = 0; face < 2; ++face) {if (!gbox[2 * dir + face]) {int bmin[dim], bmax[dim];for (int d = 0; d < dim; ++d) {// Skip edges and corners if d < dirbmin[d] = d < dir ? imin[d] : 0;bmax[d] = d < dir ? imax[d] : cctk_lsh[d];}if (face == 0)bmax[dir] = imin[dir];elsebmin[dir] = imax[dir];// CCTK_VINFO("dir=%d face=%d", dir, face);// CCTK_VINFO("bmin[%d,%d,%d]", bmin[0], bmin[1], bmin[2]);// CCTK_VINFO("bmax[%d,%d,%d]", bmax[0], bmax[1], bmax[2]);for (int k = bmin[2]; k < bmax[2]; ++k) {for (int j = bmin[1]; j < bmax[1]; ++j) {
for (int i = imin[0]; i < imax[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL ddx_phi =(phi_p[idx - di] - 2 * phi_p[idx] + phi_p[idx + di]) / sqr(dx[0]);CCTK_REAL ddy_phi =(phi_p[idx - dj] - 2 * phi_p[idx] + phi_p[idx + dj]) / sqr(dx[1]);CCTK_REAL ddz_phi =(phi_p[idx - dk] - 2 * phi_p[idx] + phi_p[idx + dk]) / sqr(dx[2]);// phi[idx] = 2 * phi_p[idx] - phi_p_p[idx] +// sqr(dt) * (ddx_phi + ddy_phi + ddz_phi);psi[idx] = psi_p[idx] + dt * (ddx_phi + ddy_phi + ddz_phi);phi[idx] = phi_p[idx] + dt * psi[idx];
for (int i = bmin[0]; i < bmax[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];CCTK_REAL r = sqrt(pow(x, 2) + pow(y, 2) + pow(z, 2));int ddir = dir == 0 ? di : dir == 1 ? dj : dk;int ndir = 2 * face - 1;// CCTK_REAL gradphi =// (phi_p[idx] - phi_p[idx - ndir * ddir]) / dx[dir];// phi[idx] =// phi_p[idx] -// dt * phi_vel * (r * gradphi + (phi_p[idx] - phi_inf) / r);// CCTK_REAL gradpsi =// (psi_p[idx] - psi_p[idx - ndir * ddir]) / dx[dir];// psi[idx] =// psi_p[idx] -// dt * psi_vel * (r * gradpsi + (psi_p[idx] - psi_inf) / r);CCTK_REAL gradphi =(phi_p[idx] - phi_p[idx - ndir * ddir]) / dx[dir];psi[idx] = -phi_vel * (r * gradphi + (phi_p[idx] - phi_inf) / r);phi[idx] = phi_p[idx] + dt * psi[idx];}}}
}}#endifextern "C" void WaveToyAMReX_Sync(CCTK_ARGUMENTS) {// do nothinga}extern "C" void WaveToyAMReX_Boundaries(CCTK_ARGUMENTS) {DECLARE_CCTK_ARGUMENTS;DECLARE_CCTK_PARAMETERS;const CCTK_REAL t = cctk_time;const CCTK_REAL dt = CCTK_DELTA_TIME;CCTK_REAL x0[dim], dx[dim];for (int d = 0; d < dim; ++d) {x0[d] = CCTK_ORIGIN_SPACE(d);dx[d] = CCTK_DELTA_SPACE(d);}if (CCTK_EQUALS(boundary_condition, "none")) {// do nothing} else if (CCTK_EQUALS(boundary_condition, "initial")) {if (CCTK_EQUALS(initial_condition, "central potential")) {Loop::loop_bnd(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phi[idx] = central_potential(t, x, y, z);psi[idx] = timederiv(central_potential, dt)(t, x, y, z);});} else {assert(0);}} else {assert(0);}}extern "C" void WaveToyAMReX_NaNCheck_past(CCTK_ARGUMENTS) {DECLARE_CCTK_ARGUMENTS;DECLARE_CCTK_PARAMETERS;Loop::loop_all(cctkGH, [&](int i, int j, int k, int idx) {assert(CCTK_isfinite(phi_p[idx]));assert(CCTK_isfinite(psi_p[idx]));});}extern "C" void WaveToyAMReX_NaNCheck_current(CCTK_ARGUMENTS) {DECLARE_CCTK_ARGUMENTS;DECLARE_CCTK_PARAMETERS;int levfac = cctk_levfac[0];int lev = 0;while (levfac > 1) {levfac >>= 1;lev += 1;
bool has_error = false;Loop::loop_all(cctkGH, [&](int i, int j, int k, int idx) {if (!CCTK_isfinite(phi[idx]) || !CCTK_isfinite(psi[idx])) {CCTK_VINFO("level %d idx=[%d,%d,%d] gidx=[%d,%d,%d] (%g,%g)", lev, i, j,k, cctk_lbnd[0] + i, cctk_lbnd[1] + j, cctk_lbnd[2] + k,phi[idx], psi[idx]);has_error = true;}});if (has_error)CCTK_VERROR("NaNs found");
for (int k = imin[2]; k < imax[2]; ++k) {for (int j = imin[1]; j < imax[1]; ++j) {#pragma omp simdfor (int i = imin[0]; i < imax[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL ddx_phi =(phi[idx - di] - 2 * phi[idx] + phi[idx + di]) / sqr(dx[0]);CCTK_REAL ddy_phi =(phi[idx - dj] - 2 * phi[idx] + phi[idx + dj]) / sqr(dx[1]);CCTK_REAL ddz_phi =(phi[idx - dk] - 2 * phi[idx] + phi[idx + dk]) / sqr(dx[2]);CCTK_REAL psi_n = psi[idx] + dt * (ddx_phi + ddy_phi + ddz_phi);CCTK_REAL dt_phi_p = psi_n;CCTK_REAL dt_phi_m = psi_p[idx];CCTK_REAL dx_phi_p = (phi[idx + di] - phi[idx]) / dx[0];CCTK_REAL dx_phi_m = (phi[idx] - phi[idx - di]) / dx[0];CCTK_REAL dy_phi_p = (phi[idx + dj] - phi[idx]) / dx[1];CCTK_REAL dy_phi_m = (phi[idx] - phi[idx - dj]) / dx[1];CCTK_REAL dz_phi_p = (phi[idx + dk] - phi[idx]) / dx[2];CCTK_REAL dz_phi_m = (phi[idx] - phi[idx - dk]) / dx[2];eps[idx] =(sqr(dt_phi_p) + sqr(dt_phi_m) + sqr(dx_phi_p) + sqr(dx_phi_m) +sqr(dy_phi_p) + sqr(dy_phi_m) + sqr(dz_phi_p) + sqr(dz_phi_m)) /4;}}}
Loop::loop_int(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL ddx_phi =(phi[idx - di] - 2 * phi[idx] + phi[idx + di]) / pow(dx[0], 2);CCTK_REAL ddy_phi =(phi[idx - dj] - 2 * phi[idx] + phi[idx + dj]) / pow(dx[1], 2);CCTK_REAL ddz_phi =(phi[idx - dk] - 2 * phi[idx] + phi[idx + dk]) / pow(dx[2], 2);CCTK_REAL psi_n = psi[idx] + dt * (ddx_phi + ddy_phi + ddz_phi);CCTK_REAL dt_phi_p = psi_n;CCTK_REAL dt_phi_m = psi_p[idx];CCTK_REAL dx_phi_p = (phi[idx + di] - phi[idx]) / dx[0];CCTK_REAL dx_phi_m = (phi[idx] - phi[idx - di]) / dx[0];CCTK_REAL dy_phi_p = (phi[idx + dj] - phi[idx]) / dx[1];CCTK_REAL dy_phi_m = (phi[idx] - phi[idx - dj]) / dx[1];CCTK_REAL dz_phi_p = (phi[idx + dk] - phi[idx]) / dx[2];CCTK_REAL dz_phi_m = (phi[idx] - phi[idx - dk]) / dx[2];eps[idx] = (pow(dt_phi_p, 2) + pow(dt_phi_m, 2) + pow(dx_phi_p, 2) +pow(dx_phi_m, 2) + pow(dy_phi_p, 2) + pow(dy_phi_m, 2) +pow(dz_phi_p, 2) + pow(dz_phi_m, 2)) /4;});
for (int k = 0; k < cctk_lsh[2]; ++k) {for (int j = 0; j < cctk_lsh[1]; ++j) {#pragma omp simdfor (int i = 0; i < cctk_lsh[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phierr[idx] = phi[idx] - standing(t, x, y, z);psierr[idx] = psi[idx] - timederiv(standing, dt)(t, x, y, z);}}}
Loop::loop_all(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phierr[idx] = phi[idx] - standing(t, x, y, z);psierr[idx] = psi[idx] - timederiv(standing, dt)(t, x, y, z);});
for (int k = 0; k < cctk_lsh[2]; ++k) {for (int j = 0; j < cctk_lsh[1]; ++j) {#pragma omp simdfor (int i = 0; i < cctk_lsh[0]; ++i) {const int idx = CCTK_GFINDEX3D(cctkGH, i, j, k);CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phierr[idx] = phi[idx] - gaussian(t, x, y, z);psierr[idx] = psi[idx] - timederiv(gaussian, dt)(t, x, y, z);}}}
Loop::loop_all(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phierr[idx] = phi[idx] - periodic_gaussian(t, x, y, z);psierr[idx] = psi[idx] - timederiv(periodic_gaussian, dt)(t, x, y, z);});} else if (CCTK_EQUALS(initial_condition, "Gaussian")) {Loop::loop_all(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phierr[idx] = phi[idx] - gaussian(t, x, y, z);psierr[idx] = psi[idx] - timederiv(gaussian, dt)(t, x, y, z);});} else if (CCTK_EQUALS(initial_condition, "central potential")) {Loop::loop_all(cctkGH, [&](int i, int j, int k, int idx) {CCTK_REAL x = x0[0] + (cctk_lbnd[0] + i) * dx[0];CCTK_REAL y = x0[1] + (cctk_lbnd[1] + j) * dx[1];CCTK_REAL z = x0[2] + (cctk_lbnd[2] + k) * dx[2];phierr[idx] = phi[idx] - central_potential(t, x, y, z);psierr[idx] = psi[idx] - timederiv(central_potential, dt)(t, x, y, z);});} else {assert(0);