From fee27c95cbf1ac53eb2e7612293e90716548f761 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 3 Nov 2023 11:51:03 +0100 Subject: [PATCH 001/461] Dump field before and after rhs evaluation for debugging --- src/solver/impls/pvode/pvode.cxx | 43 ++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index b2cfd233a9..8a51a9cb19 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -293,6 +293,49 @@ BoutReal PvodeSolver::run(BoutReal tout) { // Check return flag if (flag != SUCCESS) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); + CVodeMemRec* cv_mem = (CVodeMem)cvode_mem; + if (f2d.empty() and v2d.empty() and v3d.empty()) { + Options debug{}; + using namespace std::string_literals; + Mesh* mesh{}; + for (const auto& prefix : {"pre_"s, "residuum_"s}) { + std::vector ffs{}; + std::vector evolve_bndrys{}; + for (const auto& f : f3d) { + Field3D ff{0.}; + ff.allocate(); + ff.setLocation(f.location); + mesh = ff.getMesh(); + debug[fmt::format("{:s}{:s}", prefix, f.name)] = ff; + ffs.push_back(ff); + evolve_bndrys.push_back(f.evolve_bndry); + } + pvode_load_data_f3d(evolve_bndrys, ffs, + prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); + } + + for (auto& f : f3d) { + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); + setName(f.var, f.name); + } + run_rhs(simtime); + + for (auto& f : f3d) { + debug[f.name] = *f.var; + } + + if (mesh) { + mesh->outputVars(debug); + debug["BOUT_VERSION"].force(bout::version::as_double); + } + + std::string outname = fmt::format( + "{}/BOUT.debug.{}.nc", + Options::root()["datadir"].withDefault("data"), BoutComm::rank()); + + bout::OptionsNetCDF(outname).write(debug); + MPI_Barrier(BoutComm::get()); + } return (-1.0); } From 96da2e9f88e313f4dd388b2c83d701046aedf96c Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 3 Nov 2023 11:49:32 +0100 Subject: [PATCH 002/461] Add setName function --- include/bout/field.hxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index c0693ec0fb..0867560c3b 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -683,4 +683,12 @@ inline T floor(const T& var, BoutReal f, const std::string& rgn = "RGN_ALL") { #undef FIELD_FUNC +template , class... Types> +inline T setName(T&& f, const std::string& name, Types... args) { +#if BOUT_USE_TRACK + f.name = fmt::format(name, args...); +#endif + return f; +} + #endif /* FIELD_H */ From e56981ceeb0409d7d48d81e1225e7332c0346519 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 19 Jun 2023 09:33:00 +0200 Subject: [PATCH 003/461] Set div_par and grad_par names --- src/mesh/coordinates.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 01f0fe46ca..3948c75b94 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1542,7 +1542,11 @@ Field3D Coordinates::Grad_par(const Field3D& var, CELL_LOC outloc, TRACE("Coordinates::Grad_par( Field3D )"); ASSERT1(location == outloc || outloc == CELL_DEFAULT); - return ::DDY(var, outloc, method) * invSg(); + if (invSg == nullptr) { + invSg = std::make_unique(); + (*invSg) = 1.0 / sqrt(g_22); + } + return setName(::DDY(var, outloc, method) * invSg(), "Grad_par({:s})", var.name); } ///////////////////////////////////////////////////////// @@ -1601,7 +1605,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, f_B.yup(i) = f.yup(i) / Bxy_floc.yup(i); f_B.ydown(i) = f.ydown(i) / Bxy_floc.ydown(i); } - return Bxy * Grad_par(f_B, outloc, method); + return setName(Bxy * Grad_par(f_B, outloc, method), "Div_par({:s})", f.name); } ///////////////////////////////////////////////////////// From 6b2c132e3db61fa4f453e43a6988e8534f6c0a43 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 19 Jun 2023 09:32:25 +0200 Subject: [PATCH 004/461] Dump debug file if PVODE fails Use the new track feature (better name required) to dump the different components of the ddt() as well as the residuum for the evolved fields. --- src/solver/impls/pvode/pvode.cxx | 27 +++++++++++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 8a51a9cb19..7283b7d0eb 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -42,12 +42,39 @@ #include // contains the enum for types of preconditioning #include // band preconditioner function prototypes +#include + using namespace pvode; void solver_f(integer N, BoutReal t, N_Vector u, N_Vector udot, void* f_data); void solver_gloc(integer N, BoutReal t, BoutReal* u, BoutReal* udot, void* f_data); void solver_cfn(integer N, BoutReal t, N_Vector u, void* f_data); +namespace { +// local only +void pvode_load_data_f3d(const std::vector& evolve_bndrys, + std::vector& ffs, BoutReal* udata) { + int p = 0; + Mesh* mesh = ffs[0].getMesh(); + const int nz = mesh->LocalNz; + for (const auto& bndry : {true, false}) { + for (const auto& i2d : mesh->getRegion2D(bndry ? "RGN_BNDRY" : "RGN_NOBNDRY")) { + for (int jz = 0; jz < nz; jz++) { + // Loop over 3D variables + std::vector::const_iterator evolve_bndry = evolve_bndrys.begin(); + for (std::vector::iterator ff = ffs.begin(); ff != ffs.end(); ++ff) { + if (bndry && !*evolve_bndry) + continue; + (*ff)[mesh->ind2Dto3D(i2d, jz)] = udata[p]; + p++; + } + ++evolve_bndry; + } + } + } +} +} // namespace + const BoutReal ZERO = 0.0; long int iopt[OPT_SIZE]; From df2d66189f4d2e87cc024521ad287936b9c0ba85 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 19 Jun 2023 09:27:19 +0200 Subject: [PATCH 005/461] Add tracking to Field3D This keeps track of all the changes done to the field and stores them to a OptionsObject. --- include/bout/field3d.hxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index ba8c8e879e..8992575be6 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -514,6 +514,13 @@ private: /// RegionID over which the field is valid std::optional regionID; + + int tracking_state{0}; + Options* tracking{nullptr}; + std::string selfname{""}; + template + Options* track(const T& change, std::string op); + Options* track(const BoutReal& change, std::string op); }; // Non-member overloaded operators From 263f9fedaad854832bfdbf6a5ac9322eb492c71e Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 19 Jun 2023 09:27:19 +0200 Subject: [PATCH 006/461] Add tracking to Field3D This keeps track of all the changes done to the field and stores them to a OptionsObject. --- include/bout/field3d.hxx | 4 + src/field/field3d.cxx | 44 ++++ src/field/gen_fieldops.jinja | 14 ++ src/field/generated_fieldops.cxx | 348 +++++++++++++++++++++++++++++++ 4 files changed, 410 insertions(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 8992575be6..bf7a9cc180 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -295,6 +295,10 @@ public: /// cuts on closed field lines? bool requiresTwistShift(bool twist_shift_enabled); + /// Enable a special tracking mode for debugging + /// Save all changes that, are done to the field, to tracking + Field3D& enableTracking(const std::string& name, Options& tracking); + ///////////////////////////////////////////////////////// // Data access diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 011353f34a..f0f088b656 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -243,6 +243,7 @@ Field3D& Field3D::operator=(const Field3D& rhs) { } TRACE("Field3D: Assignment from Field3D"); + track(rhs, "operator="); // Copy base slice Field::operator=(rhs); @@ -263,6 +264,7 @@ Field3D& Field3D::operator=(const Field3D& rhs) { Field3D& Field3D::operator=(Field3D&& rhs) { TRACE("Field3D: Assignment from Field3D"); + track(rhs, "operator="); // Move parallel slices or delete existing ones. yup_fields = std::move(rhs.yup_fields); @@ -283,6 +285,7 @@ Field3D& Field3D::operator=(Field3D&& rhs) { Field3D& Field3D::operator=(const Field2D& rhs) { TRACE("Field3D = Field2D"); + track(rhs, "operator="); /// Check that the data is allocated ASSERT1(rhs.isAllocated()); @@ -327,6 +330,7 @@ void Field3D::operator=(const FieldPerp& rhs) { Field3D& Field3D::operator=(const BoutReal val) { TRACE("Field3D = BoutReal"); + track(val, "operator="); // Delete existing parallel slices. We don't copy parallel slices, so any // that currently exist will be incorrect. @@ -831,3 +835,43 @@ Field3D::getValidRegionWithDefault(const std::string& region_name) const { void Field3D::setRegion(const std::string& region_name) { regionID = fieldmesh->getRegionID(region_name); } + +Field3D& Field3D::enableTracking(const std::string& name, Options& _tracking) { + tracking = &_tracking; + tracking_state = 1; + selfname = name; + return *this; +} + +template +Options* Field3D::track(const T& change, std::string op) { + if (tracking and tracking_state) { + const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; + tracking->set(outname, change, "tracking"); + (*tracking)[outname].setAttributes({ + {"operation", op}, +#if BOUT_USE_TRACK + {"rhs.name", change.name}, +#endif + }); + return &(*tracking)[outname]; + } + return nullptr; +} + +template Options* Field3D::track(const Field3D&, std::string); +template Options* Field3D::track(const Field2D&, std::string); +template Options* Field3D::track(const FieldPerp&, std::string); + +Options* Field3D::track(const BoutReal& change, std::string op) { + if (tracking and tracking_state) { + const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; + tracking->set(outname, change, "tracking"); + (*tracking)[outname].setAttributes({ + {"operation", op}, + {"rhs.name", "BR"}, + }); + return &(*tracking)[outname]; + } + return nullptr; +} diff --git a/src/field/gen_fieldops.jinja b/src/field/gen_fieldops.jinja index ecd4e628cc..58b1ae28ba 100644 --- a/src/field/gen_fieldops.jinja +++ b/src/field/gen_fieldops.jinja @@ -61,6 +61,10 @@ } {% endif %} +#if BOUT_USE_TRACK + {{out.name}}.name = fmt::format("{:s} {{operator}} {:s}", {{'"BR"' if lhs == "BoutReal" else lhs.name + ".name"}} + , {{'"BR"' if rhs == "BoutReal" else rhs.name + ".name"}}); +#endif checkData({{out.name}}); return {{out.name}}; } @@ -129,9 +133,19 @@ } {% endif %} + {% if lhs == "Field3D" %} + track(rhs, "operator{{operator}}="); + {% endif %} +#if BOUT_USE_TRACK + name = fmt::format("{:s} {{operator}}= {:s}", this->name, {{'"BR"' if rhs == "BoutReal" else rhs.name + ".name"}}); +#endif + checkData(*this); } else { + {% if lhs == "Field3D" %} + track(rhs, "operator{{operator}}="); + {% endif %} (*this) = (*this) {{operator}} {{rhs.name}}; } return *this; diff --git a/src/field/generated_fieldops.cxx b/src/field/generated_fieldops.cxx index 6b778acee3..3495d87dbc 100644 --- a/src/field/generated_fieldops.cxx +++ b/src/field/generated_fieldops.cxx @@ -20,6 +20,9 @@ Field3D operator*(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -42,9 +45,15 @@ Field3D& Field3D::operator*=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } + track(rhs, "operator*="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator*="); (*this) = (*this) * rhs; } return *this; @@ -64,6 +73,9 @@ Field3D operator/(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -86,9 +98,15 @@ Field3D& Field3D::operator/=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } + track(rhs, "operator/="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator/="); (*this) = (*this) / rhs; } return *this; @@ -108,6 +126,9 @@ Field3D operator+(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -130,9 +151,15 @@ Field3D& Field3D::operator+=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } + track(rhs, "operator+="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator+="); (*this) = (*this) + rhs; } return *this; @@ -152,6 +179,9 @@ Field3D operator-(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -174,9 +204,15 @@ Field3D& Field3D::operator-=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } + track(rhs, "operator-="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator-="); (*this) = (*this) - rhs; } return *this; @@ -201,6 +237,9 @@ Field3D operator*(const Field3D& lhs, const Field2D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -226,9 +265,15 @@ Field3D& Field3D::operator*=(const Field2D& rhs) { } } + track(rhs, "operator*="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator*="); (*this) = (*this) * rhs; } return *this; @@ -254,6 +299,9 @@ Field3D operator/(const Field3D& lhs, const Field2D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -280,9 +328,15 @@ Field3D& Field3D::operator/=(const Field2D& rhs) { } } + track(rhs, "operator/="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator/="); (*this) = (*this) / rhs; } return *this; @@ -307,6 +361,9 @@ Field3D operator+(const Field3D& lhs, const Field2D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -332,9 +389,15 @@ Field3D& Field3D::operator+=(const Field2D& rhs) { } } + track(rhs, "operator+="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator+="); (*this) = (*this) + rhs; } return *this; @@ -359,6 +422,9 @@ Field3D operator-(const Field3D& lhs, const Field2D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -384,9 +450,15 @@ Field3D& Field3D::operator-=(const Field2D& rhs) { } } + track(rhs, "operator-="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { + track(rhs, "operator-="); (*this) = (*this) - rhs; } return *this; @@ -408,6 +480,9 @@ FieldPerp operator*(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -428,6 +503,9 @@ FieldPerp operator/(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -448,6 +526,9 @@ FieldPerp operator+(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -468,6 +549,9 @@ FieldPerp operator-(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -485,6 +569,9 @@ Field3D operator*(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] * rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -504,9 +591,15 @@ Field3D& Field3D::operator*=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } + track(rhs, "operator*="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { + track(rhs, "operator*="); (*this) = (*this) * rhs; } return *this; @@ -526,6 +619,9 @@ Field3D operator/(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] * tmp; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -546,9 +642,15 @@ Field3D& Field3D::operator/=(const BoutReal rhs) { const auto tmp = 1.0 / rhs; BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } + track(rhs, "operator/="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { + track(rhs, "operator/="); (*this) = (*this) / rhs; } return *this; @@ -567,6 +669,9 @@ Field3D operator+(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] + rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -586,9 +691,15 @@ Field3D& Field3D::operator+=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } + track(rhs, "operator+="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, "BR"); +#endif + checkData(*this); } else { + track(rhs, "operator+="); (*this) = (*this) + rhs; } return *this; @@ -607,6 +718,9 @@ Field3D operator-(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] - rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -626,9 +740,15 @@ Field3D& Field3D::operator-=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } + track(rhs, "operator-="); +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { + track(rhs, "operator-="); (*this) = (*this) - rhs; } return *this; @@ -653,6 +773,9 @@ Field3D operator*(const Field2D& lhs, const Field3D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -676,6 +799,9 @@ Field3D operator/(const Field2D& lhs, const Field3D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -699,6 +825,9 @@ Field3D operator+(const Field2D& lhs, const Field3D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -722,6 +851,9 @@ Field3D operator-(const Field2D& lhs, const Field3D& rhs) { } } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -738,6 +870,9 @@ Field2D operator*(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -754,6 +889,10 @@ Field2D& Field2D::operator*=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -774,6 +913,9 @@ Field2D operator/(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -790,6 +932,10 @@ Field2D& Field2D::operator/=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -810,6 +956,9 @@ Field2D operator+(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -826,6 +975,10 @@ Field2D& Field2D::operator+=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -846,6 +999,9 @@ Field2D operator-(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -862,6 +1018,10 @@ Field2D& Field2D::operator-=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -886,6 +1046,9 @@ FieldPerp operator*(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -906,6 +1069,9 @@ FieldPerp operator/(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -926,6 +1092,9 @@ FieldPerp operator+(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -946,6 +1115,9 @@ FieldPerp operator-(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -961,6 +1133,9 @@ Field2D operator*(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] * rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -976,6 +1151,10 @@ Field2D& Field2D::operator*=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -996,6 +1175,9 @@ Field2D operator/(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] * tmp; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1012,6 +1194,10 @@ Field2D& Field2D::operator/=(const BoutReal rhs) { const auto tmp = 1.0 / rhs; BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1031,6 +1217,9 @@ Field2D operator+(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] + rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1046,6 +1235,10 @@ Field2D& Field2D::operator+=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1065,6 +1258,9 @@ Field2D operator-(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] - rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1080,6 +1276,10 @@ Field2D& Field2D::operator-=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1104,6 +1304,9 @@ FieldPerp operator*(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] * rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1126,6 +1329,10 @@ FieldPerp& FieldPerp::operator*=(const Field3D& rhs) { (*this)[index] *= rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1150,6 +1357,9 @@ FieldPerp operator/(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] / rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1172,6 +1382,10 @@ FieldPerp& FieldPerp::operator/=(const Field3D& rhs) { (*this)[index] /= rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1196,6 +1410,9 @@ FieldPerp operator+(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] + rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1218,6 +1435,10 @@ FieldPerp& FieldPerp::operator+=(const Field3D& rhs) { (*this)[index] += rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1242,6 +1463,9 @@ FieldPerp operator-(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] - rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1264,6 +1488,10 @@ FieldPerp& FieldPerp::operator-=(const Field3D& rhs) { (*this)[index] -= rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1288,6 +1516,9 @@ FieldPerp operator*(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] * rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1310,6 +1541,10 @@ FieldPerp& FieldPerp::operator*=(const Field2D& rhs) { (*this)[index] *= rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1334,6 +1569,9 @@ FieldPerp operator/(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] / rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1356,6 +1594,10 @@ FieldPerp& FieldPerp::operator/=(const Field2D& rhs) { (*this)[index] /= rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1380,6 +1622,9 @@ FieldPerp operator+(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] + rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1402,6 +1647,10 @@ FieldPerp& FieldPerp::operator+=(const Field2D& rhs) { (*this)[index] += rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1426,6 +1675,9 @@ FieldPerp operator-(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] - rhs[base_ind]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1448,6 +1700,10 @@ FieldPerp& FieldPerp::operator-=(const Field2D& rhs) { (*this)[index] -= rhs[base_ind]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1468,6 +1724,9 @@ FieldPerp operator*(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1484,6 +1743,10 @@ FieldPerp& FieldPerp::operator*=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1504,6 +1767,9 @@ FieldPerp operator/(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1520,6 +1786,10 @@ FieldPerp& FieldPerp::operator/=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1540,6 +1810,9 @@ FieldPerp operator+(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1556,6 +1829,10 @@ FieldPerp& FieldPerp::operator+=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1576,6 +1853,9 @@ FieldPerp operator-(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); +#endif checkData(result); return result; } @@ -1592,6 +1872,10 @@ FieldPerp& FieldPerp::operator-=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, rhs.name); +#endif + checkData(*this); } else { @@ -1611,6 +1895,9 @@ FieldPerp operator*(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] * rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1626,6 +1913,10 @@ FieldPerp& FieldPerp::operator*=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} *= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1646,6 +1937,9 @@ FieldPerp operator/(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] * tmp; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1661,6 +1955,10 @@ FieldPerp& FieldPerp::operator/=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} /= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1680,6 +1978,9 @@ FieldPerp operator+(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] + rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1695,6 +1996,10 @@ FieldPerp& FieldPerp::operator+=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} += {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1714,6 +2019,9 @@ FieldPerp operator-(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] - rhs; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", lhs.name, "BR"); +#endif checkData(result); return result; } @@ -1729,6 +2037,10 @@ FieldPerp& FieldPerp::operator-=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } +#if BOUT_USE_TRACK + name = fmt::format("{:s} -= {:s}", this->name, "BR"); +#endif + checkData(*this); } else { @@ -1750,6 +2062,9 @@ Field3D operator*(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1767,6 +2082,9 @@ Field3D operator/(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1784,6 +2102,9 @@ Field3D operator+(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1801,6 +2122,9 @@ Field3D operator-(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1816,6 +2140,9 @@ Field2D operator*(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1831,6 +2158,9 @@ Field2D operator/(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1846,6 +2176,9 @@ Field2D operator+(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1861,6 +2194,9 @@ Field2D operator-(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1876,6 +2212,9 @@ FieldPerp operator*(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs * rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} * {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1891,6 +2230,9 @@ FieldPerp operator/(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs / rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} / {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1906,6 +2248,9 @@ FieldPerp operator+(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs + rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} + {:s}", "BR", rhs.name); +#endif checkData(result); return result; } @@ -1921,6 +2266,9 @@ FieldPerp operator-(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs - rhs[index]; } +#if BOUT_USE_TRACK + result.name = fmt::format("{:s} - {:s}", "BR", rhs.name); +#endif checkData(result); return result; } From bac4ca92a31b60c40e87dd47b2ef764f571a58be Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 25 Apr 2023 09:16:38 +0200 Subject: [PATCH 007/461] cvode: Add option to use Adams Moulton solver instead of BDF --- src/solver/impls/pvode/pvode.cxx | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 7283b7d0eb..ae5cd783a8 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -214,7 +214,34 @@ int PvodeSolver::init() { } iopt[MXSTEP] = pvode_mxstep; - cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, BDF, NEWTON, SS, &reltol, &abstol, + { + /* ropt[H0] : initial step size. Optional input. */ + + /* ropt[HMAX] : maximum absolute value of step size allowed. * + * Optional input. (Default is infinity). */ + const BoutReal hmax( + (*options)["max_timestep"].doc("Maximum internal timestep").withDefault(-1.)); + if (hmax > 0) { + ropt[HMAX] = hmax; + } + /* ropt[HMIN] : minimum absolute value of step size allowed. * + * Optional input. (Default is 0.0). */ + const BoutReal hmin( + (*options)["min_timestep"].doc("Minimum internal timestep").withDefault(-1.)); + if (hmin > 0) { + ropt[HMIN] = hmin; + } + /* iopt[MAXORD] : maximum lmm order to be used by the solver. * + * Optional input. (Default = 12 for ADAMS, 5 for * + * BDF). */ + const int maxOrder((*options)["max_order"].doc("Maximum order").withDefault(-1)); + if (maxOrder > 0) { + iopt[MAXORD] = maxOrder; + } + } + const bool use_adam((*options)["adams_moulton"].doc("Use Adams Moulton solver instead of BDF").withDefault(false)); + + cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, use_adam ? ADAMS : BDF, NEWTON, SS, &reltol, &abstol, this, nullptr, optIn, iopt, ropt, machEnv); if (cvode_mem == nullptr) { From 708bdcb2ff0a5c346edb43f7e609949b7c7afbd9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 29 Mar 2023 12:59:22 +0200 Subject: [PATCH 008/461] Expose more pvode option to user --- src/solver/impls/pvode/pvode.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index ae5cd783a8..ac6e981b50 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -212,6 +212,9 @@ int PvodeSolver::init() { for (i = 0; i < OPT_SIZE; i++) { ropt[i] = ZERO; } + /* iopt[MXSTEP] : maximum number of internal steps to be taken by * + * the solver in its attempt to reach tout. * + * Optional input. (Default = 500). */ iopt[MXSTEP] = pvode_mxstep; { From d88b454aa2b2924a26a180440f956911c05a0abc Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 19 Mar 2024 16:04:48 +0100 Subject: [PATCH 009/461] Fix bad cherry-pick --- src/mesh/coordinates.cxx | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 3948c75b94..32774d6229 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1542,10 +1542,6 @@ Field3D Coordinates::Grad_par(const Field3D& var, CELL_LOC outloc, TRACE("Coordinates::Grad_par( Field3D )"); ASSERT1(location == outloc || outloc == CELL_DEFAULT); - if (invSg == nullptr) { - invSg = std::make_unique(); - (*invSg) = 1.0 / sqrt(g_22); - } return setName(::DDY(var, outloc, method) * invSg(), "Grad_par({:s})", var.name); } From 023bc41730de39040a50ae245363945d2447d63b Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 19 Mar 2024 16:35:43 +0100 Subject: [PATCH 010/461] Update to new API --- include/bout/field.hxx | 7 +++++++ src/solver/impls/pvode/pvode.cxx | 4 ++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 0867560c3b..04035f5b76 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -683,6 +683,13 @@ inline T floor(const T& var, BoutReal f, const std::string& rgn = "RGN_ALL") { #undef FIELD_FUNC +template , class... Types> +inline void setName(T& f, const std::string& name, Types... args) { +#if BOUT_USE_TRACK + f.name = fmt::format(name, args...); +#endif +} + template , class... Types> inline T setName(T&& f, const std::string& name, Types... args) { #if BOUT_USE_TRACK diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index ac6e981b50..762fba32d1 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -373,7 +373,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { for (auto& f : f3d) { f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); - setName(f.var, f.name); + setName(*f.var, f.name); } run_rhs(simtime); @@ -390,7 +390,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { "{}/BOUT.debug.{}.nc", Options::root()["datadir"].withDefault("data"), BoutComm::rank()); - bout::OptionsNetCDF(outname).write(debug); + bout::OptionsIO::create(outname)->write(debug); MPI_Barrier(BoutComm::get()); } return (-1.0); From affc995c4ba482c79677c890cc44ccb47d45b648 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 19 Mar 2024 16:35:53 +0100 Subject: [PATCH 011/461] Fix documentation --- manual/sphinx/user_docs/bout_options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/sphinx/user_docs/bout_options.rst b/manual/sphinx/user_docs/bout_options.rst index 85a8a17d59..330a0dad7e 100644 --- a/manual/sphinx/user_docs/bout_options.rst +++ b/manual/sphinx/user_docs/bout_options.rst @@ -889,7 +889,7 @@ Fields can also be stored and written:: Options fields; fields["f2d"] = Field2D(1.0); fields["f3d"] = Field3D(2.0); - bout::OptionsIO::create("fields.nc").write(fields); + bout::OptionsIO::create("fields.nc")->write(fields); This allows the input settings and evolving variables to be combined into a single tree (see above on joining trees) and written From 71f5b6adb6a8ad7b8941ba783773897906d870d2 Mon Sep 17 00:00:00 2001 From: dschwoerer Date: Tue, 19 Mar 2024 15:50:33 +0000 Subject: [PATCH 012/461] Apply clang-format changes --- src/solver/impls/pvode/pvode.cxx | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 762fba32d1..a12f330964 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -242,10 +242,12 @@ int PvodeSolver::init() { iopt[MAXORD] = maxOrder; } } - const bool use_adam((*options)["adams_moulton"].doc("Use Adams Moulton solver instead of BDF").withDefault(false)); + const bool use_adam((*options)["adams_moulton"] + .doc("Use Adams Moulton solver instead of BDF") + .withDefault(false)); - cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, use_adam ? ADAMS : BDF, NEWTON, SS, &reltol, &abstol, - this, nullptr, optIn, iopt, ropt, machEnv); + cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, use_adam ? ADAMS : BDF, NEWTON, SS, + &reltol, &abstol, this, nullptr, optIn, iopt, ropt, machEnv); if (cvode_mem == nullptr) { throw BoutException("\tError: CVodeMalloc failed.\n"); @@ -373,12 +375,12 @@ BoutReal PvodeSolver::run(BoutReal tout) { for (auto& f : f3d) { f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); - setName(*f.var, f.name); + setName(*f.var, f.name); } run_rhs(simtime); for (auto& f : f3d) { - debug[f.name] = *f.var; + debug[f.name] = *f.var; } if (mesh) { From 31fd46153fad6977524394179d0b83ac51f26b9e Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 20 Mar 2024 09:59:13 +0100 Subject: [PATCH 013/461] Apply recomendations from code-review --- include/bout/field3d.hxx | 6 +++--- src/field/field3d.cxx | 10 +++++----- src/solver/impls/pvode/pvode.cxx | 5 +++-- 3 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index bf7a9cc180..cfde9e5328 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -521,10 +521,10 @@ private: int tracking_state{0}; Options* tracking{nullptr}; - std::string selfname{""}; + std::string selfname; template - Options* track(const T& change, std::string op); - Options* track(const BoutReal& change, std::string op); + Options* track(const T& change, std::string operation); + Options* track(const BoutReal& change, std::string operation); }; // Non-member overloaded operators diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index f0f088b656..2196d6eea4 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -844,12 +844,12 @@ Field3D& Field3D::enableTracking(const std::string& name, Options& _tracking) { } template -Options* Field3D::track(const T& change, std::string op) { - if (tracking and tracking_state) { +Options* Field3D::track(const T& change, std::string operation) { + if (tracking != nullptr and tracking_state != 0) { const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; tracking->set(outname, change, "tracking"); (*tracking)[outname].setAttributes({ - {"operation", op}, + {"operation", operation}, #if BOUT_USE_TRACK {"rhs.name", change.name}, #endif @@ -863,12 +863,12 @@ template Options* Field3D::track(const Field3D&, std::string); template Options* Field3D::track(const Field2D&, std::string); template Options* Field3D::track(const FieldPerp&, std::string); -Options* Field3D::track(const BoutReal& change, std::string op) { +Options* Field3D::track(const BoutReal& change, std::string operation) { if (tracking and tracking_state) { const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; tracking->set(outname, change, "tracking"); (*tracking)[outname].setAttributes({ - {"operation", op}, + {"operation", operation}, {"rhs.name", "BR"}, }); return &(*tracking)[outname]; diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index a12f330964..f3a96b03af 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -53,7 +53,7 @@ void solver_cfn(integer N, BoutReal t, N_Vector u, void* f_data); namespace { // local only void pvode_load_data_f3d(const std::vector& evolve_bndrys, - std::vector& ffs, BoutReal* udata) { + std::vector& ffs, const BoutReal* udata) { int p = 0; Mesh* mesh = ffs[0].getMesh(); const int nz = mesh->LocalNz; @@ -63,8 +63,9 @@ void pvode_load_data_f3d(const std::vector& evolve_bndrys, // Loop over 3D variables std::vector::const_iterator evolve_bndry = evolve_bndrys.begin(); for (std::vector::iterator ff = ffs.begin(); ff != ffs.end(); ++ff) { - if (bndry && !*evolve_bndry) + if (bndry && !*evolve_bndry) { continue; + } (*ff)[mesh->ind2Dto3D(i2d, jz)] = udata[p]; p++; } From 17e46cfc1c0a835fea474ac68f64a9addbf4379f Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 20 Mar 2024 10:02:28 +0100 Subject: [PATCH 014/461] Use more meaningful names --- src/solver/impls/pvode/pvode.cxx | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index f3a96b03af..c389a3c0d1 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -359,18 +359,18 @@ BoutReal PvodeSolver::run(BoutReal tout) { using namespace std::string_literals; Mesh* mesh{}; for (const auto& prefix : {"pre_"s, "residuum_"s}) { - std::vector ffs{}; + std::vector list_of_fields{}; std::vector evolve_bndrys{}; for (const auto& f : f3d) { - Field3D ff{0.}; - ff.allocate(); - ff.setLocation(f.location); - mesh = ff.getMesh(); - debug[fmt::format("{:s}{:s}", prefix, f.name)] = ff; - ffs.push_back(ff); + mesh = f.var->getMesh(); + Field3D to_load{0., mesh}; + to_load.allocate(); + to_load.setLocation(f.location); + debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; + list_of_fields.push_back(to_load); evolve_bndrys.push_back(f.evolve_bndry); } - pvode_load_data_f3d(evolve_bndrys, ffs, + pvode_load_data_f3d(evolve_bndrys, list_of_fields, prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); } From 9c0ae16ed905588b50f3e4fe634dcedf47de22b5 Mon Sep 17 00:00:00 2001 From: dschwoerer Date: Wed, 20 Mar 2024 09:03:10 +0000 Subject: [PATCH 015/461] Apply clang-format changes --- src/solver/impls/pvode/pvode.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index c389a3c0d1..fe231e1086 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -65,7 +65,7 @@ void pvode_load_data_f3d(const std::vector& evolve_bndrys, for (std::vector::iterator ff = ffs.begin(); ff != ffs.end(); ++ff) { if (bndry && !*evolve_bndry) { continue; - } + } (*ff)[mesh->ind2Dto3D(i2d, jz)] = udata[p]; p++; } From 4a17b4982df9788fc26407db70f14d0ce16098e3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 20 Mar 2024 10:04:52 +0100 Subject: [PATCH 016/461] Apply suggestions from code review --- src/solver/impls/pvode/pvode.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index fe231e1086..db28f64d86 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -384,12 +384,12 @@ BoutReal PvodeSolver::run(BoutReal tout) { debug[f.name] = *f.var; } - if (mesh) { + if (mesh != nullptr) { mesh->outputVars(debug); debug["BOUT_VERSION"].force(bout::version::as_double); } - std::string outname = fmt::format( + const std::string outname = fmt::format( "{}/BOUT.debug.{}.nc", Options::root()["datadir"].withDefault("data"), BoutComm::rank()); From 2f7c3c0664c954c016b949ea8c199f6f35ac289f Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 20 Mar 2024 16:02:22 +0100 Subject: [PATCH 017/461] Workaround for gcc 9.4 gcc 9.4 is unable to correctly parse the construction for the function argument, if name.change is used directly. First making a copy seems to work around that issue. --- src/field/field3d.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 2196d6eea4..e0d4dda01d 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -848,10 +848,14 @@ Options* Field3D::track(const T& change, std::string operation) { if (tracking != nullptr and tracking_state != 0) { const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; tracking->set(outname, change, "tracking"); + // Workaround for bug in gcc9.4 +#if BOUT_USE_TRACK + const std::string changename = change.name; +#endif (*tracking)[outname].setAttributes({ {"operation", operation}, #if BOUT_USE_TRACK - {"rhs.name", change.name}, + {"rhs.name", changename}, #endif }); return &(*tracking)[outname]; From d611758ae6b02a93e51eeeaebe025b628a23c9b3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 9 Apr 2024 15:15:30 +0200 Subject: [PATCH 018/461] Add option to debug on failure --- src/solver/impls/pvode/pvode.cxx | 80 ++++++++++++++++++-------------- src/solver/impls/pvode/pvode.hxx | 9 +++- 2 files changed, 52 insertions(+), 37 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index db28f64d86..9dce5d357f 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -247,6 +247,11 @@ int PvodeSolver::init() { .doc("Use Adams Moulton solver instead of BDF") .withDefault(false)); + debug_on_failure = + (*options)["debug_on_failure"] + .doc("Run an aditional rhs if the solver fails with extra tracking") + .withDefault(false); + cvode_mem = CVodeMalloc(neq, solver_f, simtime, u, use_adam ? ADAMS : BDF, NEWTON, SS, &reltol, &abstol, this, nullptr, optIn, iopt, ropt, machEnv); @@ -354,47 +359,52 @@ BoutReal PvodeSolver::run(BoutReal tout) { if (flag != SUCCESS) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); CVodeMemRec* cv_mem = (CVodeMem)cvode_mem; - if (f2d.empty() and v2d.empty() and v3d.empty()) { - Options debug{}; - using namespace std::string_literals; - Mesh* mesh{}; - for (const auto& prefix : {"pre_"s, "residuum_"s}) { - std::vector list_of_fields{}; - std::vector evolve_bndrys{}; - for (const auto& f : f3d) { - mesh = f.var->getMesh(); - Field3D to_load{0., mesh}; - to_load.allocate(); - to_load.setLocation(f.location); - debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; - list_of_fields.push_back(to_load); - evolve_bndrys.push_back(f.evolve_bndry); + if (debug_on_failure) { + if (f2d.empty() and v2d.empty() and v3d.empty()) { + Options debug{}; + using namespace std::string_literals; + Mesh* mesh{}; + for (const auto& prefix : {"pre_"s, "residuum_"s}) { + std::vector list_of_fields{}; + std::vector evolve_bndrys{}; + for (const auto& f : f3d) { + mesh = f.var->getMesh(); + Field3D to_load{0., mesh}; + to_load.allocate(); + to_load.setLocation(f.location); + debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; + list_of_fields.push_back(to_load); + evolve_bndrys.push_back(f.evolve_bndry); + } + pvode_load_data_f3d(evolve_bndrys, list_of_fields, + prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); } - pvode_load_data_f3d(evolve_bndrys, list_of_fields, - prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); - } - for (auto& f : f3d) { - f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); - setName(*f.var, f.name); - } - run_rhs(simtime); + for (auto& f : f3d) { + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); + setName(*f.var, f.name); + } + run_rhs(simtime); - for (auto& f : f3d) { - debug[f.name] = *f.var; - } + for (auto& f : f3d) { + debug[f.name] = *f.var; + } - if (mesh != nullptr) { - mesh->outputVars(debug); - debug["BOUT_VERSION"].force(bout::version::as_double); - } + if (mesh != nullptr) { + mesh->outputVars(debug); + debug["BOUT_VERSION"].force(bout::version::as_double); + } - const std::string outname = fmt::format( - "{}/BOUT.debug.{}.nc", - Options::root()["datadir"].withDefault("data"), BoutComm::rank()); + const std::string outname = + fmt::format("{}/BOUT.debug.{}.nc", + Options::root()["datadir"].withDefault("data"), + BoutComm::rank()); - bout::OptionsIO::create(outname)->write(debug); - MPI_Barrier(BoutComm::get()); + bout::OptionsIO::create(outname)->write(debug); + MPI_Barrier(BoutComm::get()); + } else { + output_warn.write("debug_on_failure is currently only supported for Field3Ds"); + } } return (-1.0); } diff --git a/src/solver/impls/pvode/pvode.hxx b/src/solver/impls/pvode/pvode.hxx index d29135d02e..3b385af99d 100644 --- a/src/solver/impls/pvode/pvode.hxx +++ b/src/solver/impls/pvode/pvode.hxx @@ -75,10 +75,15 @@ private: pvode::machEnvType machEnv{nullptr}; void* cvode_mem{nullptr}; - BoutReal abstol, reltol; // addresses passed in init must be preserved + BoutReal abstol, reltol; + // addresses passed in init must be preserved pvode::PVBBDData pdata{nullptr}; - bool pvode_initialised = false; + /// is pvode already initialised? + bool pvode_initialised{false}; + + /// Add debugging data if solver fails + bool debug_on_failure{false}; }; #endif // BOUT_PVODE_SOLVER_H From db96b7ecc3df8613a80203d59a421bf4186f25d1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 9 Apr 2024 15:56:25 +0200 Subject: [PATCH 019/461] Add option to euler solver to dump debug info --- src/solver/impls/euler/euler.cxx | 36 +++++++++++++++++++++++++++++++- src/solver/impls/euler/euler.hxx | 2 ++ 2 files changed, 37 insertions(+), 1 deletion(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 3976f4402c..45ba5ccdbf 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -20,7 +20,10 @@ EulerSolver::EulerSolver(Options* options) .withDefault(2.)), timestep((*options)["timestep"] .doc("Internal timestep (defaults to output timestep)") - .withDefault(getOutputTimestep())) {} + .withDefault(getOutputTimestep())), + dump_at_time((*options)["dump_at_time"] + .doc("Dump debug info about the simulation") + .withDefault(-1)) {} void EulerSolver::setMaxTimestep(BoutReal dt) { if (dt >= cfl_factor * timestep) { @@ -141,7 +144,38 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star Array& result) { load_vars(std::begin(start)); + const bool dump_now = dump_at_time > 0 && std::abs(dump_at_time - curtime) < dt; + std::unique_ptr debug_ptr; + if (dump_now) { + debug_ptr = std::make_unique(); + Options& debug = *debug_ptr; + for (auto& f : f3d) { + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); + setName(*f.var, f.name); + } + } + run_rhs(curtime); + if (dump_now) { + Options& debug = *debug_ptr; + Mesh* mesh; + for (auto& f : f3d) { + debug[f.name] = *f.var; + mesh = f.var->getMesh(); + } + + if (mesh != nullptr) { + mesh->outputVars(debug); + debug["BOUT_VERSION"].force(bout::version::as_double); + } + + const std::string outname = fmt::format( + "{}/BOUT.debug.{}.nc", + Options::root()["datadir"].withDefault("data"), BoutComm::rank()); + + bout::OptionsIO::create(outname)->write(debug); + MPI_Barrier(BoutComm::get()); + } save_derivs(std::begin(result)); BOUT_OMP_PERF(parallel for) diff --git a/src/solver/impls/euler/euler.hxx b/src/solver/impls/euler/euler.hxx index 0ee81a3d33..4b6dc60a62 100644 --- a/src/solver/impls/euler/euler.hxx +++ b/src/solver/impls/euler/euler.hxx @@ -64,6 +64,8 @@ private: /// Take a single step to calculate f1 void take_step(BoutReal curtime, BoutReal dt, Array& start, Array& result); + + BoutReal dump_at_time{-1.}; }; #endif // BOUT_KARNIADAKIS_SOLVER_H From 84bfcef2d10dc2dabf280c792a548519158414df Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 15 Apr 2024 11:25:59 +0200 Subject: [PATCH 020/461] Disable tracking once we are done --- include/bout/field3d.hxx | 7 +++++++ src/solver/impls/euler/euler.cxx | 5 ++++- 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index cfde9e5328..97118cb70a 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -299,6 +299,13 @@ public: /// Save all changes that, are done to the field, to tracking Field3D& enableTracking(const std::string& name, Options& tracking); + /// Disable tracking + Field3D& disableTracking(){ + tracking = nullptr; + tracking_state = 0; + return *this; + } + ///////////////////////////////////////////////////////// // Data access diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 45ba5ccdbf..227d7ba454 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -158,7 +158,7 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star run_rhs(curtime); if (dump_now) { Options& debug = *debug_ptr; - Mesh* mesh; + Mesh* mesh{nullptr}; for (auto& f : f3d) { debug[f.name] = *f.var; mesh = f.var->getMesh(); @@ -175,6 +175,9 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star bout::OptionsIO::create(outname)->write(debug); MPI_Barrier(BoutComm::get()); + for (auto& f : f3d) { + f.F_var->disableTracking(); + } } save_derivs(std::begin(result)); From 73265df7ebafd0a4d63763765977dd90cda2ce69 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 15 Apr 2024 11:27:07 +0200 Subject: [PATCH 021/461] Allow to dump every timestep with euler --- src/solver/impls/euler/euler.cxx | 14 ++++++++++---- src/solver/impls/euler/euler.hxx | 1 + 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 227d7ba454..ded28984aa 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -144,7 +144,8 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star Array& result) { load_vars(std::begin(start)); - const bool dump_now = dump_at_time > 0 && std::abs(dump_at_time - curtime) < dt; + const bool dump_now = + (dump_at_time >= 0 && std::abs(dump_at_time - curtime) < dt) || dump_at_time < -3; std::unique_ptr debug_ptr; if (dump_now) { debug_ptr = std::make_unique(); @@ -152,6 +153,8 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star for (auto& f : f3d) { f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); setName(*f.var, f.name); + debug[fmt::format("pre_{:s}", f.name)] = *f.var; + f.var->allocate(); } } @@ -169,9 +172,12 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star debug["BOUT_VERSION"].force(bout::version::as_double); } - const std::string outname = fmt::format( - "{}/BOUT.debug.{}.nc", - Options::root()["datadir"].withDefault("data"), BoutComm::rank()); + const std::string outnumber = + dump_at_time < -3 ? fmt::format(".{}", debug_counter++) : ""; + const std::string outname = + fmt::format("{}/BOUT.debug{}.{}.nc", + Options::root()["datadir"].withDefault("data"), + outnumber, BoutComm::rank()); bout::OptionsIO::create(outname)->write(debug); MPI_Barrier(BoutComm::get()); diff --git a/src/solver/impls/euler/euler.hxx b/src/solver/impls/euler/euler.hxx index 4b6dc60a62..fc9b7f53bb 100644 --- a/src/solver/impls/euler/euler.hxx +++ b/src/solver/impls/euler/euler.hxx @@ -66,6 +66,7 @@ private: Array& result); BoutReal dump_at_time{-1.}; + int debug_counter{0}; }; #endif // BOUT_KARNIADAKIS_SOLVER_H From 8ff388aeb9f74c23fb6cc8a073565cc19276d268 Mon Sep 17 00:00:00 2001 From: dschwoerer Date: Mon, 15 Apr 2024 09:30:12 +0000 Subject: [PATCH 022/461] Apply clang-format changes --- include/bout/field3d.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 97118cb70a..395e3201e4 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -300,7 +300,7 @@ public: Field3D& enableTracking(const std::string& name, Options& tracking); /// Disable tracking - Field3D& disableTracking(){ + Field3D& disableTracking() { tracking = nullptr; tracking_state = 0; return *this; From 6724e7548bf88a7824845a9caf1c77c49bef269b Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 9 Aug 2024 12:02:43 +0200 Subject: [PATCH 023/461] Dump also parallel fields by default --- src/solver/impls/pvode/pvode.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 9dce5d357f..66344f7cde 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -358,8 +358,8 @@ BoutReal PvodeSolver::run(BoutReal tout) { // Check return flag if (flag != SUCCESS) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); - CVodeMemRec* cv_mem = (CVodeMem)cvode_mem; if (debug_on_failure) { + CVodeMemRec* cv_mem = (CVodeMem)cvode_mem; if (f2d.empty() and v2d.empty() and v3d.empty()) { Options debug{}; using namespace std::string_literals; @@ -388,6 +388,9 @@ BoutReal PvodeSolver::run(BoutReal tout) { for (auto& f : f3d) { debug[f.name] = *f.var; + if (f.var->hasParallelSlices()) { + saveParallel(debug, f.name, *f.var); + } } if (mesh != nullptr) { From 28212c2e97c725f20099fd302fb17efc86243c7b Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 9 Aug 2024 12:01:16 +0200 Subject: [PATCH 024/461] Also dump parallel fields --- src/solver/impls/euler/euler.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index ded28984aa..5477b5760b 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -163,7 +163,7 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star Options& debug = *debug_ptr; Mesh* mesh{nullptr}; for (auto& f : f3d) { - debug[f.name] = *f.var; + saveParallel(debug, f.name, *f.var); mesh = f.var->getMesh(); } From 9cf0fba6971c1c6367b3aee3c5d68f9bcf4121ed Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 9 Aug 2024 11:53:26 +0200 Subject: [PATCH 025/461] Clarify which div_par has been used --- src/mesh/coordinates.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 32774d6229..483b3e458c 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1601,7 +1601,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, f_B.yup(i) = f.yup(i) / Bxy_floc.yup(i); f_B.ydown(i) = f.ydown(i) / Bxy_floc.ydown(i); } - return setName(Bxy * Grad_par(f_B, outloc, method), "Div_par({:s})", f.name); + return setName(Bxy * Grad_par(f_B, outloc, method), "C:Div_par({:s})", f.name); } ///////////////////////////////////////////////////////// From 97b67e187149d5c7e6624ffd1222ef8d242d5270 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 9 Aug 2024 10:30:32 +0200 Subject: [PATCH 026/461] Expose tracking Useful for physics module to record additional data if the solver is failing --- include/bout/field3d.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 395e3201e4..0f227a8da3 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -510,6 +510,8 @@ public: int size() const override { return nx * ny * nz; }; + Options* getTracking() { return tracking; }; + private: /// Array sizes (from fieldmesh). These are valid only if fieldmesh is not null int nx{-1}, ny{-1}, nz{-1}; From 77e08ec6f7e4e38978e1ec85218af8475991ce4b Mon Sep 17 00:00:00 2001 From: dschwoerer Date: Tue, 22 Oct 2024 09:57:45 +0000 Subject: [PATCH 027/461] Apply clang-format changes --- src/solver/impls/pvode/pvode.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 66344f7cde..d4ac14fef2 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -388,9 +388,9 @@ BoutReal PvodeSolver::run(BoutReal tout) { for (auto& f : f3d) { debug[f.name] = *f.var; - if (f.var->hasParallelSlices()) { - saveParallel(debug, f.name, *f.var); - } + if (f.var->hasParallelSlices()) { + saveParallel(debug, f.name, *f.var); + } } if (mesh != nullptr) { From ba6fc6cea816d0c2163dcbd3af4f64824a25f446 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 9 Aug 2024 10:32:17 +0200 Subject: [PATCH 028/461] Add simple interface to store parallel fields Just dumping the parallel slices does in general not work, as then collect discards that, especially if NYPE==ny --- include/bout/options.hxx | 3 +++ src/sys/options.cxx | 16 ++++++++++++++++ 2 files changed, 19 insertions(+) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index 839c847289..e1f5ae68fa 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -946,6 +946,9 @@ Tensor Options::as>(const Tensor& similar_t /// Convert \p value to string std::string toString(const Options& value); +/// Save the parallel fields +void saveParallel(Options& opt, const std::string name, const Field3D& tosave); + /// Output a stringified \p value to a stream /// /// This is templated to avoid implict casting: anything is diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 49a81cfa88..71339b6089 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -306,6 +306,22 @@ void Options::assign<>(Tensor val, std::string source) { _set_no_check(std::move(val), std::move(source)); } +void saveParallel(Options& opt, const std::string name, const Field3D& tosave){ + ASSERT2(tosave.hasParallelSlices()); + opt[name] = tosave; + for (size_t i0=1 ; i0 <= tosave.numberParallelSlices(); ++i0) { + for (int i: {i0, -i0} ) { + Field3D tmp; + tmp.allocate(); + const auto& fpar = tosave.ynext(i); + for (auto j: fpar.getValidRegionWithDefault("RGN_NO_BOUNDARY")){ + tmp[j.yp(-i)] = fpar[j]; + } + opt[fmt::format("{}_y{:+d}", name, i)] = tmp; + } + } +} + namespace { /// Use FieldFactory to evaluate expression double parseExpression(const Options::ValueType& value, const Options* options, From 3c0d6a8f6a4d20bdee9d140b9f90af9432a6810c Mon Sep 17 00:00:00 2001 From: dschwoerer Date: Tue, 22 Oct 2024 10:26:41 +0000 Subject: [PATCH 029/461] Apply clang-format changes --- src/sys/options.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 71339b6089..1193deb013 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -306,16 +306,16 @@ void Options::assign<>(Tensor val, std::string source) { _set_no_check(std::move(val), std::move(source)); } -void saveParallel(Options& opt, const std::string name, const Field3D& tosave){ +void saveParallel(Options& opt, const std::string name, const Field3D& tosave) { ASSERT2(tosave.hasParallelSlices()); opt[name] = tosave; - for (size_t i0=1 ; i0 <= tosave.numberParallelSlices(); ++i0) { - for (int i: {i0, -i0} ) { + for (size_t i0 = 1; i0 <= tosave.numberParallelSlices(); ++i0) { + for (int i : {i0, -i0}) { Field3D tmp; tmp.allocate(); const auto& fpar = tosave.ynext(i); - for (auto j: fpar.getValidRegionWithDefault("RGN_NO_BOUNDARY")){ - tmp[j.yp(-i)] = fpar[j]; + for (auto j : fpar.getValidRegionWithDefault("RGN_NO_BOUNDARY")) { + tmp[j.yp(-i)] = fpar[j]; } opt[fmt::format("{}_y{:+d}", name, i)] = tmp; } From d0669cdcafa76bec00116abb2d934e05405eae35 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 22 Oct 2024 15:37:56 +0200 Subject: [PATCH 030/461] Do not use numberParallelSlices() --- src/sys/options.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/sys/options.cxx b/src/sys/options.cxx index df1dee56f4..3f7a0c4071 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -338,9 +338,10 @@ void Options::assign<>(Tensor val, std::string source) { } void saveParallel(Options& opt, const std::string name, const Field3D& tosave) { - ASSERT2(tosave.hasParallelSlices()); opt[name] = tosave; - for (size_t i0 = 1; i0 <= tosave.numberParallelSlices(); ++i0) { + const size_t numberParallelSlices = + tosave.hasParallelSlices() ? 0 : tosave.getMesh()->ystart; + for (size_t i0 = 1; i0 <= numberParallelSlices; ++i0) { for (int i : {i0, -i0}) { Field3D tmp; tmp.allocate(); From 42fc8ff5350b8da32518a0bd449b8e3dc149bb9f Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 12:25:13 +0100 Subject: [PATCH 031/461] prefer const ref --- include/bout/options.hxx | 2 +- src/sys/options.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index e207c6b4d4..bc43bda3ef 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -991,7 +991,7 @@ Tensor Options::as>(const Tensor& similar_t std::string toString(const Options& value); /// Save the parallel fields -void saveParallel(Options& opt, const std::string name, const Field3D& tosave); +void saveParallel(Options& opt, const std::string& name, const Field3D& tosave); /// Output a stringified \p value to a stream /// diff --git a/src/sys/options.cxx b/src/sys/options.cxx index d49f014f25..e674667d7b 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -343,7 +343,7 @@ Options& Options::assign<>(Tensor val, std::string source) { return *this; } -void saveParallel(Options& opt, const std::string name, const Field3D& tosave) { +void saveParallel(Options& opt, const std::string& name, const Field3D& tosave) { opt[name] = tosave; const size_t numberParallelSlices = tosave.hasParallelSlices() ? 0 : tosave.getMesh()->ystart; From c9c13d4ee451234385be78ff3159b9a82a4ee3dd Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 12:25:53 +0100 Subject: [PATCH 032/461] Update for move of bout/version.hxx --- src/solver/impls/euler/euler.cxx | 1 + src/solver/impls/pvode/pvode.cxx | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 5477b5760b..ce2a594cb8 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -1,6 +1,7 @@ #include "euler.hxx" +#include "bout/version.hxx" #include #include #include diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 5cc41e31c5..6db3b6ccfe 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -35,8 +35,8 @@ #include #include #include - -#include "bout/unused.hxx" +#include +#include #include // use CVSPGMR linear solver each internal step #include // contains the enum for types of preconditioning From ff93406d32ec28d84c6957191d787db5c25a16fb Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 12:26:07 +0100 Subject: [PATCH 033/461] Prefer auto --- src/solver/impls/pvode/pvode.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 6db3b6ccfe..3fc6b6866c 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -61,8 +61,8 @@ void pvode_load_data_f3d(const std::vector& evolve_bndrys, for (const auto& i2d : mesh->getRegion2D(bndry ? "RGN_BNDRY" : "RGN_NOBNDRY")) { for (int jz = 0; jz < nz; jz++) { // Loop over 3D variables - std::vector::const_iterator evolve_bndry = evolve_bndrys.begin(); - for (std::vector::iterator ff = ffs.begin(); ff != ffs.end(); ++ff) { + auto evolve_bndry = evolve_bndrys.cbegin(); + for (auto ff = ffs.begin(); ff != ffs.end(); ++ff) { if (bndry && !*evolve_bndry) { continue; } From a096cc5f812509857617529523727a96a17bc1ef Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 12:26:29 +0100 Subject: [PATCH 034/461] Avoid warning for 3d metrics --- src/physics/smoothing.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/physics/smoothing.cxx b/src/physics/smoothing.cxx index 0a1391907f..036c475cd7 100644 --- a/src/physics/smoothing.cxx +++ b/src/physics/smoothing.cxx @@ -350,7 +350,7 @@ BoutReal Average_XY(const Field2D& var) { return Vol_Glb; } -BoutReal Vol_Integral(const Field2D& var) { +BoutReal Vol_Integral([[maybe_unused]] const Field2D& var) { #if BOUT_USE_METRIC_3D AUTO_TRACE(); throw BoutException("Vol_Intregral currently incompatible with 3D metrics"); From 27db46d503b98eb0fe8682595cd1cb7a115367d9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 12:29:36 +0100 Subject: [PATCH 035/461] Apply recommendations from code review * switch to std::weak_ptr * reorder branches in pvode debug * make track a trivial inline function * ensure _track template is only used for fields --- include/bout/field3d.hxx | 20 +++++--- src/field/field3d.cxx | 66 ++++++++++++++----------- src/solver/impls/euler/euler.cxx | 6 +-- src/solver/impls/pvode/pvode.cxx | 83 +++++++++++++++----------------- 4 files changed, 94 insertions(+), 81 deletions(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index d1b5a0813d..90ea7020db 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -293,11 +293,11 @@ public: /// Enable a special tracking mode for debugging /// Save all changes that, are done to the field, to tracking - Field3D& enableTracking(const std::string& name, Options& tracking); + Field3D& enableTracking(const std::string& name, std::weak_ptr tracking); /// Disable tracking Field3D& disableTracking() { - tracking = nullptr; + tracking.reset(); tracking_state = 0; return *this; } @@ -506,7 +506,7 @@ public: int size() const override { return nx * ny * nz; }; - Options* getTracking() { return tracking; }; + std::weak_ptr getTracking() { return tracking; }; private: /// Array sizes (from fieldmesh). These are valid only if fieldmesh is not null @@ -525,11 +525,17 @@ private: std::optional regionID; int tracking_state{0}; - Options* tracking{nullptr}; + std::weak_ptr tracking; std::string selfname; - template - Options* track(const T& change, std::string operation); - Options* track(const BoutReal& change, std::string operation); + template + inline void track(const T& change, const std::string& operation) { + if (tracking_state != 0) { + _track(change, operation); + } + } + template > + void _track(const T& change, std::string operation); + void _track(const BoutReal& change, std::string operation); }; // Non-member overloaded operators diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 3807b54314..17571ea4c1 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -844,46 +844,56 @@ void Field3D::setRegion(const std::string& region_name) { regionID = fieldmesh->getRegionID(region_name); } -Field3D& Field3D::enableTracking(const std::string& name, Options& _tracking) { - tracking = &_tracking; +Field3D& Field3D::enableTracking(const std::string& name, + std::weak_ptr _tracking) { + tracking = _tracking; tracking_state = 1; selfname = name; return *this; } -template -Options* Field3D::track(const T& change, std::string operation) { - if (tracking != nullptr and tracking_state != 0) { - const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; - tracking->set(outname, change, "tracking"); - // Workaround for bug in gcc9.4 +template +void Field3D::_track(const T& change, std::string operation) { + if (tracking_state == 0) { + return; + } + auto locked = tracking.lock(); + if (locked == nullptr) { + return; + } + const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; + + locked->set(outname, change, "tracking"); + // Workaround for bug in gcc9.4 #if BOUT_USE_TRACK - const std::string changename = change.name; + const std::string changename = change.name; #endif - (*tracking)[outname].setAttributes({ + (*locked)[outname].setAttributes({ {"operation", operation}, #if BOUT_USE_TRACK - {"rhs.name", changename}, + {"rhs.name", changename}, #endif - }); - return &(*tracking)[outname]; - } - return nullptr; + }); } -template Options* Field3D::track(const Field3D&, std::string); -template Options* Field3D::track(const Field2D&, std::string); -template Options* Field3D::track(const FieldPerp&, std::string); +template void +Field3D::_track>(const Field3D&, + std::string); +template void Field3D::_track(const Field2D&, std::string); +template void Field3D::_track<>(const FieldPerp&, std::string); -Options* Field3D::track(const BoutReal& change, std::string operation) { - if (tracking and tracking_state) { - const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; - tracking->set(outname, change, "tracking"); - (*tracking)[outname].setAttributes({ - {"operation", operation}, - {"rhs.name", "BR"}, - }); - return &(*tracking)[outname]; +void Field3D::_track(const BoutReal& change, std::string operation) { + if (tracking_state == 0) { + return; } - return nullptr; + auto locked = tracking.lock(); + if (locked == nullptr) { + return; + } + const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; + locked->set(outname, change, "tracking"); + (*locked)[outname].setAttributes({ + {"operation", operation}, + {"rhs.name", "BoutReal"}, + }); } diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index ce2a594cb8..110331eeb1 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -147,12 +147,12 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star load_vars(std::begin(start)); const bool dump_now = (dump_at_time >= 0 && std::abs(dump_at_time - curtime) < dt) || dump_at_time < -3; - std::unique_ptr debug_ptr; + std::shared_ptr debug_ptr; if (dump_now) { - debug_ptr = std::make_unique(); + debug_ptr = std::make_shared(); Options& debug = *debug_ptr; for (auto& f : f3d) { - f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug_ptr); setName(*f.var, f.name); debug[fmt::format("pre_{:s}", f.name)] = *f.var; f.var->allocate(); diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 3fc6b6866c..4815f0153b 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -360,56 +360,53 @@ BoutReal PvodeSolver::run(BoutReal tout) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); if (debug_on_failure) { CVodeMemRec* cv_mem = (CVodeMem)cvode_mem; - if (f2d.empty() and v2d.empty() and v3d.empty()) { - Options debug{}; - using namespace std::string_literals; - Mesh* mesh{}; - for (const auto& prefix : {"pre_"s, "residuum_"s}) { - std::vector list_of_fields{}; - std::vector evolve_bndrys{}; - for (const auto& f : f3d) { - mesh = f.var->getMesh(); - Field3D to_load{0., mesh}; - to_load.allocate(); - to_load.setLocation(f.location); - debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; - list_of_fields.push_back(to_load); - evolve_bndrys.push_back(f.evolve_bndry); - } - pvode_load_data_f3d(evolve_bndrys, list_of_fields, - prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); + if (!(f2d.empty() and v2d.empty() and v3d.empty())) { + output_warn.write("debug_on_failure is currently only supported for Field3Ds"); + return -1.0; + } + auto debug_ptr = std::make_shared(); + Options& debug = *debug_ptr; + using namespace std::string_literals; + Mesh* mesh{}; + for (const auto& prefix : {"pre_"s, "residuum_"s}) { + std::vector list_of_fields{}; + std::vector evolve_bndrys{}; + for (const auto& f : f3d) { + mesh = f.var->getMesh(); + Field3D to_load{0., mesh}; + to_load.allocate(); + to_load.setLocation(f.location); + debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; + list_of_fields.push_back(to_load); + evolve_bndrys.push_back(f.evolve_bndry); } + pvode_load_data_f3d(evolve_bndrys, list_of_fields, + prefix == "pre_"s ? udata : N_VDATA(cv_mem->cv_acor)); + } - for (auto& f : f3d) { - f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug); - setName(*f.var, f.name); - } - run_rhs(simtime); + for (auto& f : f3d) { + f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug_ptr); + setName(*f.var, f.name); + } + run_rhs(simtime); - for (auto& f : f3d) { - debug[f.name] = *f.var; - if (f.var->hasParallelSlices()) { - saveParallel(debug, f.name, *f.var); - } - } + for (auto& f : f3d) { + saveParallel(debug, f.name, *f.var); + } - if (mesh != nullptr) { - mesh->outputVars(debug); - debug["BOUT_VERSION"].force(bout::version::as_double); - } + if (mesh != nullptr) { + mesh->outputVars(debug); + debug["BOUT_VERSION"].force(bout::version::as_double); + } - const std::string outname = - fmt::format("{}/BOUT.debug.{}.nc", - Options::root()["datadir"].withDefault("data"), - BoutComm::rank()); + const std::string outname = fmt::format( + "{}/BOUT.debug.{}.nc", + Options::root()["datadir"].withDefault("data"), BoutComm::rank()); - bout::OptionsIO::create(outname)->write(debug); - MPI_Barrier(BoutComm::get()); - } else { - output_warn.write("debug_on_failure is currently only supported for Field3Ds"); - } + bout::OptionsIO::create(outname)->write(debug); + MPI_Barrier(BoutComm::get()); } - return (-1.0); + return -1.0; } return simtime; From 24610c3682c43934e6560c73130d2030da234218 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 12:29:52 +0100 Subject: [PATCH 036/461] Explain purpose of variables --- include/bout/field3d.hxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 90ea7020db..8866f17b49 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -524,8 +524,11 @@ private: /// RegionID over which the field is valid std::optional regionID; + /// counter for tracking, to assign unique names to the variable names int tracking_state{0}; std::weak_ptr tracking; + // name is changed if we assign to the variable, while selfname is a + // non-changing copy that is used for the variable names in the dump files std::string selfname; template inline void track(const T& change, const std::string& operation) { From f6db2df5dc97f1f2047e3328503f8728a337977b Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Thu, 16 Jan 2025 11:30:30 +0000 Subject: [PATCH 037/461] Apply clang-format changes --- src/field/field3d.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 17571ea4c1..1eeb0dd344 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -869,9 +869,9 @@ void Field3D::_track(const T& change, std::string operation) { const std::string changename = change.name; #endif (*locked)[outname].setAttributes({ - {"operation", operation}, + {"operation", operation}, #if BOUT_USE_TRACK - {"rhs.name", changename}, + {"rhs.name", changename}, #endif }); } From 81d929d7fc37697ad791e4334a7f16f372491a70 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 17:17:04 +0100 Subject: [PATCH 038/461] Add delay on error --- include/bout/physicsmodel.hxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index 9fa25d8b0f..876f51940d 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -47,6 +47,8 @@ class PhysicsModel; #include "bout/unused.hxx" #include "bout/utils.hxx" +#include +#include #include #include @@ -435,6 +437,7 @@ private: } catch (const BoutException& e) { \ output << "Error encountered: " << e.what(); \ output << e.getBacktrace() << endl; \ + std::this_thread::sleep_for(std::chrono::milliseconds(100)); \ MPI_Abort(BoutComm::get(), 1); \ } \ BoutFinalise(); \ From 5d375970bcf2c99ca39077226c09e8ee0d802fcc Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Mon, 23 Jun 2025 15:04:37 +0200 Subject: [PATCH 039/461] Move the `EXCLUDE_FROM_ALL` inwards in the manual --- CMakeLists.txt | 2 +- manual/CMakeLists.txt | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 60d34e1ad7..b3326c6742 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -733,7 +733,7 @@ endif() option(BOUT_BUILD_DOCS "Build the documentation" OFF) if (BOUT_BUILD_DOCS) - add_subdirectory(manual EXCLUDE_FROM_ALL) + add_subdirectory(manual) endif() diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt index c8e22b1dcf..2e4e612d9f 100644 --- a/manual/CMakeLists.txt +++ b/manual/CMakeLists.txt @@ -24,10 +24,12 @@ set_target_properties(sphinx-html sphinx-pdf PROPERTIES ENVIRONMENT PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} ) -add_custom_target(docs ALL) +add_custom_target(docs) add_dependencies(docs sphinx-html) install(DIRECTORY ${BOUT_SPHINX_BUILD}/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/bout++/ + EXCLUDE_FROM_ALL + COMPONENT docs PATTERN .* EXCLUDE ) From 51bef96fd6c7d1d1713389c72d2e9e3603b030d3 Mon Sep 17 00:00:00 2001 From: Cristian Le Date: Mon, 23 Jun 2025 15:14:45 +0200 Subject: [PATCH 040/461] Fix the environment setting --- manual/CMakeLists.txt | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt index 2e4e612d9f..b5224440bf 100644 --- a/manual/CMakeLists.txt +++ b/manual/CMakeLists.txt @@ -6,7 +6,13 @@ find_package(Sphinx REQUIRED) set(BOUT_SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/sphinx) set(BOUT_SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs) +set(env_command + ${CMAKE_COMMAND} -E env + PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} +) + add_custom_target(sphinx-html + COMMAND ${env_command} COMMAND ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} COMMAND ${CMAKE_COMMAND} -E echo "Generated HTML docs in file://${BOUT_SPHINX_BUILD}/index.html" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} @@ -14,16 +20,13 @@ add_custom_target(sphinx-html ) add_custom_target(sphinx-pdf + COMMAND ${env_command} COMMAND ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} COMMAND ${CMAKE_COMMAND} -E echo "Generated PDF docs in file://${BOUT_SPHINX_BUILD}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating PDF documentation with Sphinx in ${BOUT_SPHINX_BUILD}" ) -set_target_properties(sphinx-html sphinx-pdf PROPERTIES - ENVIRONMENT PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} -) - add_custom_target(docs) add_dependencies(docs sphinx-html) From 78538d0dbc1fbc6226614065376a7fb3e3c09390 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 3 Oct 2025 09:39:42 -0700 Subject: [PATCH 041/461] Add a periodicY function to FieldFactory Provides a variable "periodicY" that is 1 in the core and 0 outside. --- src/field/field_factory.cxx | 7 +++++-- src/field/fieldgenerators.hxx | 36 ++++++++++++++++++++++++++++++++--- 2 files changed, 38 insertions(+), 5 deletions(-) diff --git a/src/field/field_factory.cxx b/src/field/field_factory.cxx index f65f2e7f55..c49106cc1d 100644 --- a/src/field/field_factory.cxx +++ b/src/field/field_factory.cxx @@ -1,7 +1,7 @@ /************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010-2025 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -176,6 +176,9 @@ FieldFactory::FieldFactory(Mesh* localmesh, Options* opt) // Where switch function addGenerator("where", std::make_shared(nullptr, nullptr, nullptr)); + + // Periodic in the Y direction? + addGenerator("periodicY", std::make_shared(nullptr)); } Field2D FieldFactory::create2D(const std::string& value, const Options* opt, diff --git a/src/field/fieldgenerators.hxx b/src/field/fieldgenerators.hxx index 2485b4b82d..133d1d1e8a 100644 --- a/src/field/fieldgenerators.hxx +++ b/src/field/fieldgenerators.hxx @@ -1,4 +1,4 @@ -/*! +/*!A * \file fieldgenerators.hxx * * These classes are used by FieldFactory @@ -307,7 +307,7 @@ public: // Constructor FieldTanhHat(FieldGeneratorPtr xin, FieldGeneratorPtr widthin, FieldGeneratorPtr centerin, FieldGeneratorPtr steepnessin) - : X(xin), width(widthin), center(centerin), steepness(steepnessin){}; + : X(xin), width(widthin), center(centerin), steepness(steepnessin) {}; // Clone containing the list of arguments FieldGeneratorPtr clone(const std::list args) override; BoutReal generate(const bout::generator::Context& pos) override; @@ -322,7 +322,7 @@ private: class FieldWhere : public FieldGenerator { public: FieldWhere(FieldGeneratorPtr test, FieldGeneratorPtr gt0, FieldGeneratorPtr lt0) - : test(test), gt0(gt0), lt0(lt0){}; + : test(test), gt0(gt0), lt0(lt0) {}; FieldGeneratorPtr clone(const std::list args) override { if (args.size() != 3) { @@ -352,4 +352,34 @@ private: FieldGeneratorPtr test, gt0, lt0; }; +/// Function that evaluates to 1 when Y is periodic (i.e. in the core), 0 otherwise +class FieldPeriodicY : public FieldGenerator { +public: + FieldPeriodicY(Mesh* mesh) : mesh(mesh) { + // Note: Assumes symmetricGlobalX + local_inner_boundary = + 0.5 * (mesh->GlobalX(mesh->xstart - 1) + mesh->GlobalX(mesh->xstart)); + local_outer_boundary = + 0.5 * (mesh->GlobalX(mesh->xend + 1) + mesh->GlobalX(mesh->xend)); + } + FieldGeneratorPtr clone(const std::list UNUSED(args)) override { + return std::make_shared(ctx.getMesh()); + } + BoutReal generate(const bout::generator::Context& ctx) override { + int local_index = mesh->xstart + + int(((ctx.x() - local_inner_boundary) + / (local_outer_boundary - local_inner_boundary)) + * (mesh->xend - mesh->xstart + 1)); + if (mesh->periodicY(local_index)) { + return 1.0; + } + return 0.0; + } + +private: + Mesh* mesh; + BoutReal local_inner_boundary; + BoutReal local_outer_boundary; +}; + #endif // BOUT_FIELDGENERATORS_H From 06c4599dae6d0201d5ce7f6f2641b9b0c4db39da Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Fri, 3 Oct 2025 16:41:58 +0000 Subject: [PATCH 042/461] Apply clang-format changes --- src/field/fieldgenerators.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/field/fieldgenerators.hxx b/src/field/fieldgenerators.hxx index 133d1d1e8a..ea3682053a 100644 --- a/src/field/fieldgenerators.hxx +++ b/src/field/fieldgenerators.hxx @@ -307,7 +307,7 @@ public: // Constructor FieldTanhHat(FieldGeneratorPtr xin, FieldGeneratorPtr widthin, FieldGeneratorPtr centerin, FieldGeneratorPtr steepnessin) - : X(xin), width(widthin), center(centerin), steepness(steepnessin) {}; + : X(xin), width(widthin), center(centerin), steepness(steepnessin){}; // Clone containing the list of arguments FieldGeneratorPtr clone(const std::list args) override; BoutReal generate(const bout::generator::Context& pos) override; @@ -322,7 +322,7 @@ private: class FieldWhere : public FieldGenerator { public: FieldWhere(FieldGeneratorPtr test, FieldGeneratorPtr gt0, FieldGeneratorPtr lt0) - : test(test), gt0(gt0), lt0(lt0) {}; + : test(test), gt0(gt0), lt0(lt0){}; FieldGeneratorPtr clone(const std::list args) override { if (args.size() != 3) { From a8512395bade559a6b7ae10ff3ad06bab4cacb66 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 3 Oct 2025 09:55:39 -0700 Subject: [PATCH 043/461] Fixes for periodicY field generator --- src/field/field_factory.cxx | 2 +- src/field/fieldgenerators.hxx | 31 +++++++++++++------------------ 2 files changed, 14 insertions(+), 19 deletions(-) diff --git a/src/field/field_factory.cxx b/src/field/field_factory.cxx index c49106cc1d..08a7f14fa3 100644 --- a/src/field/field_factory.cxx +++ b/src/field/field_factory.cxx @@ -178,7 +178,7 @@ FieldFactory::FieldFactory(Mesh* localmesh, Options* opt) addGenerator("where", std::make_shared(nullptr, nullptr, nullptr)); // Periodic in the Y direction? - addGenerator("periodicY", std::make_shared(nullptr)); + addGenerator("periodicY", std::make_shared()); } Field2D FieldFactory::create2D(const std::string& value, const Options* opt, diff --git a/src/field/fieldgenerators.hxx b/src/field/fieldgenerators.hxx index ea3682053a..050e335448 100644 --- a/src/field/fieldgenerators.hxx +++ b/src/field/fieldgenerators.hxx @@ -1,4 +1,4 @@ -/*!A +/*! * \file fieldgenerators.hxx * * These classes are used by FieldFactory @@ -353,33 +353,28 @@ private: }; /// Function that evaluates to 1 when Y is periodic (i.e. in the core), 0 otherwise +/// Note: Assumes symmetricGlobalX class FieldPeriodicY : public FieldGenerator { public: - FieldPeriodicY(Mesh* mesh) : mesh(mesh) { - // Note: Assumes symmetricGlobalX - local_inner_boundary = - 0.5 * (mesh->GlobalX(mesh->xstart - 1) + mesh->GlobalX(mesh->xstart)); - local_outer_boundary = - 0.5 * (mesh->GlobalX(mesh->xend + 1) + mesh->GlobalX(mesh->xend)); - } + FieldPeriodicY() = default; FieldGeneratorPtr clone(const std::list UNUSED(args)) override { - return std::make_shared(ctx.getMesh()); + return std::make_shared(); } BoutReal generate(const bout::generator::Context& ctx) override { - int local_index = mesh->xstart - + int(((ctx.x() - local_inner_boundary) - / (local_outer_boundary - local_inner_boundary)) - * (mesh->xend - mesh->xstart + 1)); + const Mesh* mesh = ctx.getMesh(); + const BoutReal local_inner_boundary = + 0.5 * (mesh->GlobalX(mesh->xstart - 1) + mesh->GlobalX(mesh->xstart)); + const BoutReal local_outer_boundary = + 0.5 * (mesh->GlobalX(mesh->xend + 1) + mesh->GlobalX(mesh->xend)); + const int local_index = mesh->xstart + + int(((ctx.x() - local_inner_boundary) + / (local_outer_boundary - local_inner_boundary)) + * (mesh->xend - mesh->xstart + 1)); if (mesh->periodicY(local_index)) { return 1.0; } return 0.0; } - -private: - Mesh* mesh; - BoutReal local_inner_boundary; - BoutReal local_outer_boundary; }; #endif // BOUT_FIELDGENERATORS_H From 5ded72d4803f460e92b418040e9af58280c75acc Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Sun, 12 Oct 2025 11:22:47 -0700 Subject: [PATCH 044/461] snes solver: Pseudo Transient Continuation method Setting `pseudo_time = true` now enables Pseudo-timestepping, in which each cell has a separate timestep. The timestep in each cell is set inversely proportional to the residual (RMS time-derivative of all quantities in cell). Recommend enabling `pid_controller` that multiplies all timesteps by the same factor, to control the number of nonlinear iterations. Output time uses the minimum timestep in the domain, so simulations shouldn't need to run for as long as when all cells are evolved at the same speed. --- src/solver/impls/snes/snes.cxx | 522 +++++++++++++++++++++++++++------ src/solver/impls/snes/snes.hxx | 22 +- 2 files changed, 456 insertions(+), 88 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index de6c54388d..49650b7b97 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -8,6 +8,7 @@ #include #include #include +#include #include #include @@ -83,8 +84,7 @@ PetscErrorCode FormFunctionForDifferencing(void* ctx, Vec x, Vec f) { * * This can be a linearised and simplified form of FormFunction */ -PetscErrorCode FormFunctionForColoring(void* UNUSED(snes), Vec x, Vec f, - void* ctx) { +PetscErrorCode FormFunctionForColoring(void* UNUSED(snes), Vec x, Vec f, void* ctx) { return static_cast(ctx)->snes_function(x, f, true); } @@ -96,7 +96,7 @@ PetscErrorCode snesPCapply(PC pc, Vec x, Vec y) { PetscFunctionReturn(s->precon(x, y)); } -} +} // namespace SNESSolver::SNESSolver(Options* opts) : Solver(opts), @@ -138,6 +138,21 @@ SNESSolver::SNESSolver(Options* opts) (*options)["timestep_factor_on_lower_its"] .doc("Multiply timestep if iterations are below lower_its") .withDefault(1.4)), + pseudo_time((*options)["pseudo_time"] + .doc("Pseudo-Transient Continuation method?") + .withDefault(false)), + pseudo_alpha((*options)["pseudo_alpha"] + .doc("Sets timestep using dt = alpha / residual") + .withDefault(100. * atol * timestep)), + pseudo_growth_factor((*options)["pseudo_growth_factor"] + .doc("PTC growth factor on success") + .withDefault(1.1)), + pseudo_reduction_factor((*options)["pseudo_reduction_factor"] + .doc("PTC reduction factor on failure") + .withDefault(0.5)), + pseudo_max_ratio((*options)["pseudo_max_ratio"] + .doc("PTC maximum timestep ratio between neighbors") + .withDefault(2.)), pid_controller( (*options)["pid_controller"].doc("Use PID controller?").withDefault(false)), target_its((*options)["target_its"].doc("Target snes iterations").withDefault(7)), @@ -277,6 +292,21 @@ int SNESSolver::init() { CHKERRQ(ierr); } + if (pseudo_time) { + // Storage for per-variable timestep + PetscCall(VecDuplicate(snes_x, &dt_vec)); + // Starting timestep + PetscCall(VecSet(dt_vec, timestep)); + + // Storage for previous residual. Used to compare + // residuals before and after step, and adjust timestep accordingly + PetscCall(VecDuplicate(snes_x, &previous_f)); + + // Diagnostic outputs + pseudo_timestep = timestep; + pseudo_residual = 0.0; + } + // Nonlinear solver interface (SNES) output_info.write("Create SNES\n"); SNESCreate(BoutComm::get(), &snes); @@ -767,6 +797,19 @@ int SNESSolver::run() { CHKERRQ(ierr); } + if (pseudo_time) { + // Calculate the initial residual in snes_f + run_rhs(simtime); + { + BoutReal* fdata = nullptr; + ierr = VecGetArray(snes_f, &fdata); + CHKERRQ(ierr); + save_derivs(fdata); + ierr = VecRestoreArray(snes_f, &fdata); + CHKERRQ(ierr); + } + } + BoutReal target = simtime; for (int s = 0; s < getNumberOutputSteps(); s++) { target += getOutputTimestep(); @@ -837,46 +880,67 @@ int SNESSolver::run() { // Copy the state (snes_x) into initial values (x0) VecCopy(snes_x, x0); - if (timestep < dt_min_reset) { - // Hit the minimum timestep, probably through repeated failures + if (pseudo_time) { + // Pseudo-Transient Continuation + // Each evolving quantity may have its own timestep + // Set timestep and dt scalars to the minimum of dt_vec + + PetscCall(VecMin(dt_vec, nullptr, ×tep)); + dt = timestep; - if (saved_jacobian_lag != 0) { - // Already tried this and it didn't work - throw BoutException("Solver failed after many attempts"); + // Copy residual from snes_f to previous_f + // The value of snes_f is set before the timestepping loop + // then calculated after the timestep + PetscCall(VecCopy(snes_f, previous_f)); + + looping = true; + if (simtime + timestep >= target) { + looping = false; } + } else { + // Timestepping - // Try resetting the preconditioner, turn off predictor, and use a large timestep - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); - timestep = getOutputTimestep(); - predictor = false; // Predictor can cause problems in near steady-state. - } + if (timestep < dt_min_reset) { + // Hit the minimum timestep, probably through repeated failures - // Set the timestep - dt = timestep; - looping = true; - if (simtime + dt >= target) { - // Note: When the timestep is changed the preconditioner needs to be updated - // => Step over the output time and interpolate if not matrix free - - if (matrix_free) { - // Ensure that the timestep goes to the next output time and then stops. - // This avoids the need to interpolate - dt = target - simtime; + if (saved_jacobian_lag != 0) { + // Already tried this and it didn't work + throw BoutException("Solver failed after many attempts"); + } + + // Try resetting the preconditioner, turn off predictor, and use a large timestep + SNESGetLagJacobian(snes, &saved_jacobian_lag); + SNESSetLagJacobian(snes, 1); + timestep = getOutputTimestep(); + predictor = false; // Predictor can cause problems in near steady-state. } - looping = false; - } - if (predictor and (time1 > 0.0)) { - // Use (time1, x1) and (simtime, x0) to make prediction - // snes_x <- x0 + (dt / (simtime - time1)) * (x0 - x1) - // snes_x <- -β * x1 + (1 + β) * snes_x - BoutReal beta = dt / (simtime - time1); - VecAXPBY(snes_x, -beta, (1. + beta), x1); - } + // Set the timestep + dt = timestep; + looping = true; + if (simtime + dt >= target) { + // Note: When the timestep is changed the preconditioner needs to be updated + // => Step over the output time and interpolate if not matrix free + + if (matrix_free) { + // Ensure that the timestep goes to the next output time and then stops. + // This avoids the need to interpolate + dt = target - simtime; + } + looping = false; + } + + if (predictor and (time1 > 0.0)) { + // Use (time1, x1) and (simtime, x0) to make prediction + // snes_x <- x0 + (dt / (simtime - time1)) * (x0 - x1) + // snes_x <- -β * x1 + (1 + β) * snes_x + BoutReal beta = dt / (simtime - time1); + VecAXPBY(snes_x, -beta, (1. + beta), x1); + } - if (pid_controller) { - SNESSetLagJacobian(snes, lag_jacobian); + if (pid_controller) { + SNESSetLagJacobian(snes, lag_jacobian); + } } // Run the solver @@ -917,8 +981,16 @@ int SNESSolver::run() { ++snes_failures; steps_since_snes_failure = 0; - // Try a smaller timestep - timestep *= timestep_factor_on_failure; + if (pseudo_time) { + // Global scaling of timesteps + // Note: A better strategy might be to reduce timesteps + // in problematic cells. + PetscCall(VecScale(dt_vec, timestep_factor_on_failure)); + + } else { + // Try a smaller timestep + timestep *= timestep_factor_on_failure; + } // Restore state VecCopy(x0, snes_x); @@ -972,7 +1044,8 @@ int SNESSolver::run() { if (nl_its == 0) { // This can occur even with SNESSetForceIteration - // Results in simulation state freezing and rapidly going to the end + // Results in simulation state freezing and rapidly going + // to the end if (scale_vars) { // scaled_x <- snes_x * var_scaling_factors @@ -1016,8 +1089,14 @@ int SNESSolver::run() { // Gather and print diagnostic information output.print("\r"); // Carriage return for printing to screen + + // if (pseudo_time) { + // output.write("\tTime {} alpha {} min_timestep {} nl_iter {} lin_iter {} reason {}", + // simtime, pseudo_alpha, timestep, nl_its, lin_its, static_cast(reason)); + // } else { output.write("Time: {}, timestep: {}, nl iter: {}, lin iter: {}, reason: {}", simtime, timestep, nl_its, lin_its, static_cast(reason)); + // } if (snes_failures > 0) { output.write(", SNES failures: {}", snes_failures); } @@ -1069,62 +1148,256 @@ int SNESSolver::run() { } #endif // PETSC_VERSION_GE(3,20,0) - if (looping) { + if (pseudo_time) { + // Adjust local timesteps - if (pid_controller) { - // Changing the timestep. - // Note: The preconditioner depends on the timestep, - // so we recalculate the jacobian and the preconditioner - // every time the timestep changes + // Calculate residuals + if (scale_vars) { + // scaled_x <- snes_x * var_scaling_factors + PetscCall(VecPointwiseMult(scaled_x, snes_x, var_scaling_factors)); + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &xdata)); + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + } else { + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(snes_x, &xdata)); + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(snes_x, &xdata)); + } + run_rhs(simtime); + { + BoutReal* fdata = nullptr; + PetscCall(VecGetArray(snes_f, &fdata)); + save_derivs(fdata); + PetscCall(VecRestoreArray(snes_f, &fdata)); + } - timestep = pid(timestep, nl_its); + // Compare previous_f with snes_f + // Use a per-cell timestep so that e.g density and pressure + // evolve in a way consistent with the equation of state. + + // Reading the residual vectors + const BoutReal* previous_residual = nullptr; + PetscCall(VecGetArrayRead(previous_f, &previous_residual)); + const BoutReal* current_residual = nullptr; + PetscCall(VecGetArrayRead(snes_f, ¤t_residual)); + // Modifying the dt_vec values + BoutReal* dt_data = nullptr; + PetscCall(VecGetArray(dt_vec, &dt_data)); + + // Note: The ordering of quantities in the PETSc vectors + // depends on the Solver::loop_vars function + Mesh* mesh = bout::globals::mesh; + int idx = 0; // Index into PETSc Vecs + + // Boundary cells + for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { + // Field2D quantities evolved together + int count = 0; + BoutReal R0 = 0.0; + BoutReal R1 = 0.0; - // NOTE(malamast): Do we really need this? - // Recompute Jacobian (for now) - if (saved_jacobian_lag == 0) { - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); + for (const auto& f : f2d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + R0 += SQ(previous_residual[idx + count]); + R1 += SQ(current_residual[idx + count]); + ++count; + } + if (count > 0) { + R0 = sqrt(R0 / count); + R1 = sqrt(R1 / count); + + // Adjust timestep for these quantities + BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); + for (int i = 0; i != count; ++i) { + dt_data[idx++] = new_timestep; + } } - } else { + // Field3D quantities evolved together within a cell + for (int jz = 0; jz < mesh->LocalNz; jz++) { + count = 0; + R0 = 0.0; + R1 = 0.0; + for (const auto& f : f3d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + R0 += SQ(previous_residual[idx + count]); + R1 += SQ(current_residual[idx + count]); + ++count; + } + if (count > 0) { + R0 = sqrt(R0 / count); + R1 = sqrt(R1 / count); + + BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); + + auto i3d = mesh->ind2Dto3D(i2d, jz); + pseudo_residual[i3d] = R1; + pseudo_timestep[i3d] = new_timestep; + + for (int i = 0; i != count; ++i) { + dt_data[idx++] = new_timestep; + } + } + } + } + + // Bulk of domain. + // These loops don't check the boundary flags + for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { + // Field2D quantities evolved together + if (f2d.size() > 0) { + BoutReal R0 = 0.0; + BoutReal R1 = 0.0; + for (std::size_t i = 0; i != f2d.size(); ++i) { + R0 += SQ(previous_residual[idx + i]); + R1 += SQ(current_residual[idx + i]); + } + R0 = sqrt(R0 / f2d.size()); + R1 = sqrt(R1 / f2d.size()); + + // Adjust timestep for these quantities + BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); + for (std::size_t i = 0; i != f2d.size(); ++i) { + dt_data[idx++] = new_timestep; + } + } - // Consider changing the timestep. - // Note: The preconditioner depends on the timestep, - // so if it is not recalculated the it will be less - // effective. - if ((nl_its <= lower_its) && (timestep < max_timestep) - && (steps_since_snes_failure > 2)) { - // Increase timestep slightly - timestep *= timestep_factor_on_lower_its; - - timestep = std::min(timestep, max_timestep); - - // Note: Setting the SNESJacobianFn to NULL retains - // previously set evaluation function. - // - // The SNES Jacobian is a combination of the RHS Jacobian - // and a factor involving the timestep. - // Depends on equation_form - // -> Probably call SNESSetJacobian(snes, Jfd, Jfd, NULL, fdcoloring); - - if (static_cast(lin_its) / nl_its > 4) { - // Recompute Jacobian (for now) - if (saved_jacobian_lag == 0) { - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); + // Field3D quantities evolved together within a cell + if (f3d.size() > 0) { + for (int jz = 0; jz < mesh->LocalNz; jz++) { + BoutReal R0 = 0.0; + BoutReal R1 = 0.0; + for (std::size_t i = 0; i != f3d.size(); ++i) { + R0 += SQ(previous_residual[idx + i]); + R1 += SQ(current_residual[idx + i]); + } + R0 = sqrt(R0 / f3d.size()); + R1 = sqrt(R1 / f3d.size()); + BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); + + auto i3d = mesh->ind2Dto3D(i2d, jz); + pseudo_residual[i3d] = R1; + + // Compare to neighbors + BoutReal min_neighboring_dt = max_timestep; + BoutReal mean_neighboring_dt = 0.0; + int neighbor_count = 0; + if (i3d.x() != 0) { + BoutReal val = pseudo_timestep[i3d.xm()]; + min_neighboring_dt = std::min(min_neighboring_dt, val); + mean_neighboring_dt += val; + ++neighbor_count; + } + if (i3d.x() != mesh->LocalNx - 1) { + BoutReal val = pseudo_timestep[i3d.xp()]; + min_neighboring_dt = std::min(min_neighboring_dt, val); + mean_neighboring_dt += val; + ++neighbor_count; + } + if (i3d.y() != 0) { + BoutReal val = pseudo_timestep[i3d.ym()]; + min_neighboring_dt = std::min(min_neighboring_dt, val); + mean_neighboring_dt += val; + ++neighbor_count; + } + if (i3d.x() != mesh->LocalNy - 1) { + BoutReal val = pseudo_timestep[i3d.yp()]; + min_neighboring_dt = std::min(min_neighboring_dt, val); + mean_neighboring_dt += val; + ++neighbor_count; + } + mean_neighboring_dt /= neighbor_count; + + // Smooth + //new_timestep = 0.1 * mean_neighboring_dt + 0.9 * new_timestep; + + // Limit ratio of timestep between neighboring cells + new_timestep = + std::min(new_timestep, pseudo_max_ratio * min_neighboring_dt); + + pseudo_timestep[i3d] = new_timestep; + + for (std::size_t i = 0; i != f3d.size(); ++i) { + dt_data[idx++] = new_timestep; } } + } + } + + // Restore Vec data arrays + PetscCall(VecRestoreArrayRead(previous_f, &previous_residual)); + PetscCall(VecRestoreArrayRead(snes_f, ¤t_residual)); + PetscCall(VecRestoreArray(dt_vec, &dt_data)); + + // Need timesteps on neighboring processors + // to limit ratio + mesh->communicate(pseudo_timestep); + // Neumann boundary so timestep isn't pinned at boundaries + pseudo_timestep.applyBoundary("neumann"); + + if (pid_controller) { + // Adjust pseudo_alpha based on nonlinear iterations + pseudo_alpha = pid(pseudo_alpha, nl_its, max_timestep * atol * 100); + } - } else if (nl_its >= upper_its) { - // Reduce timestep slightly - timestep *= timestep_factor_on_upper_its; + } else if (pid_controller) { + // Changing the timestep using a PID controller. + // Note: The preconditioner depends on the timestep, + // so we recalculate the jacobian and the preconditioner + // every time the timestep changes - // Recompute Jacobian + timestep = pid(timestep, nl_its, max_timestep); + + // NOTE(malamast): Do we really need this? + // Recompute Jacobian (for now) + if (saved_jacobian_lag == 0) { + SNESGetLagJacobian(snes, &saved_jacobian_lag); + SNESSetLagJacobian(snes, 1); + } + + } else { + // Consider changing the timestep. + // Note: The preconditioner depends on the timestep, + // so if it is not recalculated the it will be less + // effective. + if ((nl_its <= lower_its) && (timestep < max_timestep) + && (steps_since_snes_failure > 2)) { + // Increase timestep slightly + timestep *= timestep_factor_on_lower_its; + + timestep = std::min(timestep, max_timestep); + + // Note: Setting the SNESJacobianFn to NULL retains + // previously set evaluation function. + // + // The SNES Jacobian is a combination of the RHS Jacobian + // and a factor involving the timestep. + // Depends on equation_form + // -> Probably call SNESSetJacobian(snes, Jfd, Jfd, NULL, fdcoloring); + + if (static_cast(lin_its) / nl_its > 4) { + // Recompute Jacobian (for now) if (saved_jacobian_lag == 0) { SNESGetLagJacobian(snes, &saved_jacobian_lag); SNESSetLagJacobian(snes, 1); } } + + } else if (nl_its >= upper_its) { + // Reduce timestep slightly + timestep *= timestep_factor_on_upper_its; + + // Recompute Jacobian + if (saved_jacobian_lag == 0) { + SNESGetLagJacobian(snes, &saved_jacobian_lag); + SNESSetLagJacobian(snes, 1); + } } } snes_failures = 0; @@ -1132,7 +1405,7 @@ int SNESSolver::run() { if (!matrix_free) { ASSERT2(simtime >= target); - ASSERT2(simtime - dt < target); + ASSERT2(simtime - dt <= target); // Stepped over output timestep => Interpolate // snes_x is the solution at t = simtime // x0 is the solution at t = simtime - dt @@ -1181,6 +1454,56 @@ int SNESSolver::run() { return 0; } +/// Switched Evolution Relaxation (SER) +BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual) { + + // Timestep inversely proportional to the residual, clipped to avoid + // rapid changes in timestep. + return std::min( + {std::max({pseudo_alpha / current_residual, previous_timestep / 1.5, dt_min_reset}), + 1.5 * previous_timestep, max_timestep}); + + /* + + // Alternative strategy based on history of residuals + // A hybrid strategy may be most effective, in which the timestep is + // inversely proportional to residual initially, or when residuals are large, + // and then the method transitions to being history-based + + const BoutReal converged_threshold = 100 * atol; + const BoutReal transition_threshold = 1000 * atol; + + if (current_residual < converged_threshold) { + return max_timestep; + } + + // Smoothly transition from SER updates to frozen timestep + if (current_residual < transition_threshold) { + BoutReal reduction_ratio = current_residual / previous_residual; + + if (reduction_ratio < 0.8) { + return std::min(0.5*(pseudo_growth_factor + 1.) * previous_timestep, + max_timestep); + } else if (reduction_ratio > 1.2) { + return std::max(0.5*(pseudo_reduction_factor + 1) * previous_timestep, dt_min_reset); + } + // Leave unchanged if residual changes are small + return previous_timestep; + } + + if (current_residual <= previous_residual) { + // Success + return std::min(pseudo_growth_factor * previous_timestep, + max_timestep); + } + // Failed + // Consider rejecting the step + return std::max(pseudo_reduction_factor * previous_timestep, dt_min_reset); + */ +} + // f = rhs PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { // Get data from PETSc into BOUT++ fields @@ -1240,13 +1563,24 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { // f = (x0 - x)/Δt + f // First calculate x - x0 to minimise floating point issues VecWAXPY(delta_x, -1.0, x0, x); // delta_x = x - x0 - VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt + if (pseudo_time) { + // dt can be different for each quantity + VecPointwiseDivide(delta_x, delta_x, dt_vec); // delta_x /= dt + VecAXPY(f, -1., delta_x); // f <- f - delta_x + } else { + VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt + } break; } case BoutSnesEquationForm::backward_euler: { // Backward Euler // Set f = x - x0 - Δt*f - VecAYPX(f, -dt, x); // f <- x - Δt*f + if (pseudo_time) { + VecPointwiseMult(f, f, dt_vec); // f <- Δt*f + VecAYPX(f, -1, x); // f <- x - f + } else { + VecAYPX(f, -dt, x); // f <- x - Δt*f + } VecAXPY(f, -1.0, x0); // f <- f - x0 break; } @@ -1443,7 +1777,7 @@ void SNESSolver::updateColoring() { } } -BoutReal SNESSolver::pid(BoutReal timestep, int nl_its) { +BoutReal SNESSolver::pid(BoutReal timestep, int nl_its, BoutReal max_dt) { /* ---------- multiplicative PID factors ---------- */ const BoutReal facP = std::pow(double(target_its) / double(nl_its), kP); @@ -1452,11 +1786,11 @@ BoutReal SNESSolver::pid(BoutReal timestep, int nl_its) { / double(nl_its) / double(nl_its_prev2), kD); - // clamp groth factor to avoid huge changes + // clamp growth factor to avoid huge changes const BoutReal fac = std::clamp(facP * facI * facD, 0.2, 5.0); /* ---------- update timestep and history ---------- */ - const BoutReal dt_new = std::min(timestep * fac, max_timestep); + const BoutReal dt_new = std::min(timestep * fac, max_dt); nl_its_prev2 = nl_its_prev; nl_its_prev = nl_its; @@ -1464,4 +1798,20 @@ BoutReal SNESSolver::pid(BoutReal timestep, int nl_its) { return dt_new; } +void SNESSolver::outputVars(Options& output_options, bool save_repeat) { + // Call base class function + Solver::outputVars(output_options, save_repeat); + + if (!save_repeat) { + return; // Don't save diagnostics to restart files + } + + if (pseudo_time) { + output_options["snes_pseudo_residual"].assignRepeat(pseudo_residual, "t", save_repeat, + "SNESSolver"); + output_options["snes_pseudo_timestep"].assignRepeat(pseudo_timestep, "t", save_repeat, + "SNESSolver"); + } +} + #endif // BOUT_HAS_PETSC diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 31deae6f06..c1bb3ecc56 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -39,6 +39,7 @@ class SNESSolver; #include #include +#include #include #include @@ -88,9 +89,12 @@ public: /// finite difference approximated Jacobian. PetscErrorCode scaleJacobian(Mat B); + /// Save diagnostics to output + void outputVars(Options& output_options, bool save_repeat = true) override; + private: BoutReal timestep; ///< Internal timestep - BoutReal dt; ///< Current timestep used in snes_function + BoutReal dt; ///< Current timestep used in snes_function. BoutReal dt_min_reset; ///< If dt falls below this, reset solve BoutReal max_timestep; ///< Maximum timestep @@ -107,6 +111,20 @@ private: BoutReal timestep_factor_on_upper_its; BoutReal timestep_factor_on_lower_its; + // Pseudo-Transient Continuation (PTC) variables + bool pseudo_time; ///< Use Pseudo time-stepping + BoutReal pseudo_alpha; ///< dt = alpha / residual + BoutReal pseudo_growth_factor; ///< Timestep increase 1.1 - 1.2 + BoutReal pseudo_reduction_factor; ///< Timestep decrease 0.5 + BoutReal pseudo_max_ratio; ///< Maximum timestep ratio between neighboring cells + Vec dt_vec; ///< Each quantity can have its own timestep + Vec previous_f; ///< Previous residual + /// Decide the next pseudo-timestep + BoutReal updatePseudoTimestep(BoutReal previous_timestep, BoutReal previous_residual, + BoutReal current_residual); + Field3D pseudo_residual; ///< Diagnostic output + Field3D pseudo_timestep; + ///< PID controller parameters bool pid_controller; ///< Use PID controller? int target_its; ///< Target number of nonlinear iterations for the PID controller. @@ -118,7 +136,7 @@ private: int nl_its_prev; int nl_its_prev2; - BoutReal pid(BoutReal timestep, int nl_its); ///< Updates the timestep + BoutReal pid(BoutReal timestep, int nl_its, BoutReal max_dt); ///< Updates the timestep bool diagnose; ///< Output additional diagnostics bool diagnose_failures; ///< Print diagnostics on SNES failures From e9b8d12c45b8047f5b5c6a4da881db0c8df0f803 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 13 Oct 2025 10:16:15 -0700 Subject: [PATCH 045/461] snes: Remove pseudo_time switch, use equation_form=pseudo_transient Rather than having a separate switch, just use equation_form to select the Pseudo-Transient Continuation method. --- include/bout/mesh.hxx | 2 +- manual/sphinx/user_docs/time_integration.rst | 186 ++++++++++++++----- src/solver/impls/snes/snes.cxx | 62 +++---- src/solver/impls/snes/snes.hxx | 8 +- 4 files changed, 168 insertions(+), 90 deletions(-) diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 02e2a23905..1db25adf78 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -757,7 +757,7 @@ public: void addRegionPerp(const std::string& region_name, const Region& region); /// Converts an Ind2D to an Ind3D using calculation - Ind3D ind2Dto3D(const Ind2D& ind2D, int jz = 0) { + Ind3D ind2Dto3D(const Ind2D& ind2D, int jz = 0) const { return {ind2D.ind * LocalNz + jz, LocalNy, LocalNz}; } diff --git a/manual/sphinx/user_docs/time_integration.rst b/manual/sphinx/user_docs/time_integration.rst index c1526c14e2..373b5cdbe6 100644 --- a/manual/sphinx/user_docs/time_integration.rst +++ b/manual/sphinx/user_docs/time_integration.rst @@ -361,16 +361,145 @@ And the adaptive timestepping options: Backward Euler - SNES --------------------- -The `beuler` or `snes` solver type (either name can be used) is -intended mainly for solving steady-state problems, so integrates in -time using a stable but low accuracy method (Backward Euler). It uses -PETSc's SNES solvers to solve the nonlinear system at each timestep, -and adjusts the internal timestep to keep the number of SNES -iterations within a given range. +The `beuler` or `snes` solver type (either name can be used) is a PETSc-based implicit +solver for finding steady-state solutions to systems of partial differential equations. +It supports multiple solution strategies including backward Euler timestepping, +direct Newton iteration, and Pseudo-Transient Continuation (PTC) with Switched +Evolution Relaxation (SER). + +Basic Configuration +~~~~~~~~~~~~~~~~~~~ + +The SNES solver is configured through the ``[solver]`` section of the input file: + +.. code-block:: ini + + [solver] + type = snes + + # Nonlinear solver settings + snes_type = newtonls # anderson, newtonls, newtontr, nrichardson + atol = 1e-7 # Absolute tolerance + rtol = 1e-6 # Relative tolerance + stol = 1e-12 # Solution change tolerance + max_nonlinear_iterations = 20 # Maximum SNES iterations per solve + + # Linear solver settings + ksp_type = fgmres # Linear solver: gmres, bicgstab, etc. + maxl = 20 # Maximum linear iterations + pc_type = ilu # Preconditioner: ilu, bjacobi, hypre, etc. + +Timestepping Modes +------------------ + +The solver supports several timestepping strategies controlled by ``equation_form``: + +**Backward Euler (default)** + Standard implicit backward Euler method. Good for general timestepping. + + .. code-block:: ini + + equation_form = rearranged_backward_euler # Default + + This method has low accuracy in time but its dissipative properties + are helpful when evolving to steady state solutions. + +**Direct Newton** + Solves the steady-state problem F(u) = 0 directly without timestepping. + + .. code-block:: ini + + equation_form = direct_newton + + This method is unlikely to converge unless the system is very close + to steady state. + +**Pseudo-Transient Continuation** + Uses pseudo-time to guide the solution to steady state. Recommended for + highly nonlinear problems where Newton's method fails. + + .. code-block:: ini + + equation_form = pseudo_transient + + This uses the same form as rearranged_backward_euler, but the time step + can be different for each cell. + + +Jacobian Finite Difference with Coloring +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +The default and recommended approach for most problems: + +.. code-block:: ini + + [solver] + use_coloring = true # Enable (default) + lag_jacobian = 50 # Reuse Jacobian for this many iterations + + # Stencil shape (determines Jacobian sparsity pattern) + stencil:taxi = 2 # Taxi-cab distance (default) + stencil:square = 0 # Square stencil extent + stencil:cross = 0 # Cross stencil extent + +The coloring algorithm exploits the sparse structure of the Jacobian to reduce +the number of function evaluations needed for finite differencing. + +Jacobian coloring stencil +^^^^^^^^^^^^^^^^^^^^^^^^^ + +The stencil used to create the Jacobian colouring can be varied, +depending on which numerical operators are in use. + +``solver:stencil:cross = N`` +e.g. for N == 2 + +.. code-block:: bash + + * + * + * * x * * + * + * + + +``solver:stencil:square = N`` +e.g. for N == 2 + +.. code-block:: bash + + * * * * * + * * * * * + * * x * * + * * * * * + * * * * * + +``solver:stencil:taxi = N`` +e.g. for N == 2 + +.. code-block:: bash + + * + * * * + * * x * * + * * * + * + +Setting ``solver:force_symmetric_coloring = true``, will make sure +that the jacobian colouring matrix is symmetric. This will often +include a few extra non-zeros that the stencil will miss otherwise + + + +---------------------------+---------------+----------------------------------------------------+ | Option | Default |Description | +===========================+===============+====================================================+ +| pseudo_time | false | Pseudo-Transient Continuation (PTC) method, using | +| | | a different timestep for each cell. | ++---------------------------+---------------+----------------------------------------------------+ +| pseudo_max_ratio | 2. | Maximum timestep ratio between neighboring cells | ++---------------------------+---------------+----------------------------------------------------+ | snes_type | newtonls | PETSc SNES nonlinear solver (try anderson, qn) | +---------------------------+---------------+----------------------------------------------------+ | ksp_type | gmres | PETSc KSP linear solver | @@ -423,6 +552,8 @@ iterations within a given range. The predictor is linear extrapolation from the last two timesteps. It seems to be effective, but can be disabled by setting ``predictor = false``. + + The default `newtonls` SNES type can be very effective if combined with Jacobian coloring: The coloring enables the Jacobian to be calculated relatively efficiently; once a Jacobian matrix has been @@ -456,50 +587,13 @@ Preconditioner types: Enable with command-line args ``-pc_type hypre -pc_hypre_type euclid -pc_hypre_euclid_levels k`` where ``k`` is the level (1-8 typically). -Jacobian coloring stencil -~~~~~~~~~~~~~~~~~~~~~~~~~ - -The stencil used to create the Jacobian colouring can be varied, -depending on which numerical operators are in use. - - -``solver:stencil:cross = N`` -e.g. for N == 2 - -.. code-block:: bash - - * - * - * * x * * - * - * +Pseudo-Transient Continuation (PTC) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ -``solver:stencil:square = N`` -e.g. for N == 2 -.. code-block:: bash - - * * * * * - * * * * * - * * x * * - * * * * * - * * * * * +Saves diagnostic ``Field3D`` output variables ``snes_pseudo_residual`` and ``snes_pseudo_timestep``. -``solver:stencil:taxi = N`` -e.g. for N == 2 - -.. code-block:: bash - - * - * * * - * * x * * - * * * - * - -Setting ``solver:force_symmetric_coloring = true``, will make sure -that the jacobian colouring matrix is symmetric. This will often -include a few extra non-zeros that the stencil will miss otherwise ODE integration --------------- diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 49650b7b97..b48afccc48 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -106,6 +106,11 @@ SNESSolver::SNESSolver(Options* opts) .doc("If dt falls below this, reset to starting dt") .withDefault(1e-6)), max_timestep((*options)["max_timestep"].doc("Maximum timestep").withDefault(1e37)), + equation_form( + (*options)["equation_form"] + .doc("Form of equation to solve: rearranged_backward_euler (default);" + " pseudo_transient; backward_euler; direct_newton") + .withDefault(BoutSnesEquationForm::rearranged_backward_euler)), snes_type((*options)["snes_type"] .doc("PETSc nonlinear solver method to use") .withDefault("anderson")), @@ -138,9 +143,6 @@ SNESSolver::SNESSolver(Options* opts) (*options)["timestep_factor_on_lower_its"] .doc("Multiply timestep if iterations are below lower_its") .withDefault(1.4)), - pseudo_time((*options)["pseudo_time"] - .doc("Pseudo-Transient Continuation method?") - .withDefault(false)), pseudo_alpha((*options)["pseudo_alpha"] .doc("Sets timestep using dt = alpha / residual") .withDefault(100. * atol * timestep)), @@ -164,11 +166,6 @@ SNESSolver::SNESSolver(Options* opts) diagnose_failures((*options)["diagnose_failures"] .doc("Print more diagnostics when SNES fails") .withDefault(false)), - equation_form( - (*options)["equation_form"] - .doc("Form of equation to solve: rearranged_backward_euler (default);" - " pseudo_transient; backward_euler; direct_newton") - .withDefault(BoutSnesEquationForm::rearranged_backward_euler)), predictor((*options)["predictor"].doc("Use linear predictor?").withDefault(true)), use_precon((*options)["use_precon"] .doc("Use user-supplied preconditioner?") @@ -292,7 +289,7 @@ int SNESSolver::init() { CHKERRQ(ierr); } - if (pseudo_time) { + if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Storage for per-variable timestep PetscCall(VecDuplicate(snes_x, &dt_vec)); // Starting timestep @@ -797,7 +794,7 @@ int SNESSolver::run() { CHKERRQ(ierr); } - if (pseudo_time) { + if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Calculate the initial residual in snes_f run_rhs(simtime); { @@ -880,7 +877,7 @@ int SNESSolver::run() { // Copy the state (snes_x) into initial values (x0) VecCopy(snes_x, x0); - if (pseudo_time) { + if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Pseudo-Transient Continuation // Each evolving quantity may have its own timestep // Set timestep and dt scalars to the minimum of dt_vec @@ -981,7 +978,7 @@ int SNESSolver::run() { ++snes_failures; steps_since_snes_failure = 0; - if (pseudo_time) { + if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Global scaling of timesteps // Note: A better strategy might be to reduce timesteps // in problematic cells. @@ -1089,14 +1086,8 @@ int SNESSolver::run() { // Gather and print diagnostic information output.print("\r"); // Carriage return for printing to screen - - // if (pseudo_time) { - // output.write("\tTime {} alpha {} min_timestep {} nl_iter {} lin_iter {} reason {}", - // simtime, pseudo_alpha, timestep, nl_its, lin_its, static_cast(reason)); - // } else { output.write("Time: {}, timestep: {}, nl iter: {}, lin iter: {}, reason: {}", simtime, timestep, nl_its, lin_its, static_cast(reason)); - // } if (snes_failures > 0) { output.write(", SNES failures: {}", snes_failures); } @@ -1148,7 +1139,7 @@ int SNESSolver::run() { } #endif // PETSC_VERSION_GE(3,20,0) - if (pseudo_time) { + if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Adjust local timesteps // Calculate residuals @@ -1552,35 +1543,27 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { CHKERRQ(ierr); switch (equation_form) { - case BoutSnesEquationForm::pseudo_transient: { - // Pseudo-transient timestepping (as in UEDGE) - // f <- f - x/Δt - VecAXPY(f, -1. / dt, x); - break; - } case BoutSnesEquationForm::rearranged_backward_euler: { // Rearranged Backward Euler // f = (x0 - x)/Δt + f // First calculate x - x0 to minimise floating point issues VecWAXPY(delta_x, -1.0, x0, x); // delta_x = x - x0 - if (pseudo_time) { - // dt can be different for each quantity - VecPointwiseDivide(delta_x, delta_x, dt_vec); // delta_x /= dt - VecAXPY(f, -1., delta_x); // f <- f - delta_x - } else { - VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt - } + VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt + break; + } + case BoutSnesEquationForm::pseudo_transient: { + // Pseudo-transient timestepping. Same as Rearranged Backward Euler + // except that Δt is a vector + // f = (x0 - x)/Δt + f + VecWAXPY(delta_x, -1.0, x0, x); + VecPointwiseDivide(delta_x, delta_x, dt_vec); // delta_x /= dt + VecAXPY(f, -1., delta_x); // f <- f - delta_x break; } case BoutSnesEquationForm::backward_euler: { // Backward Euler // Set f = x - x0 - Δt*f - if (pseudo_time) { - VecPointwiseMult(f, f, dt_vec); // f <- Δt*f - VecAYPX(f, -1, x); // f <- x - f - } else { - VecAYPX(f, -dt, x); // f <- x - Δt*f - } + VecAYPX(f, -dt, x); // f <- x - Δt*f VecAXPY(f, -1.0, x0); // f <- f - x0 break; } @@ -1806,7 +1789,8 @@ void SNESSolver::outputVars(Options& output_options, bool save_repeat) { return; // Don't save diagnostics to restart files } - if (pseudo_time) { + if (equation_form == BoutSnesEquationForm::pseudo_transient) { + output_options["snes_pseudo_alpha"].assignRepeat(pseudo_alpha, "t", save_repeat, "SNESSolver"); output_options["snes_pseudo_residual"].assignRepeat(pseudo_residual, "t", save_repeat, "SNESSolver"); output_options["snes_pseudo_timestep"].assignRepeat(pseudo_timestep, "t", save_repeat, diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index c1bb3ecc56..257588e2a9 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -98,6 +98,9 @@ private: BoutReal dt_min_reset; ///< If dt falls below this, reset solve BoutReal max_timestep; ///< Maximum timestep + /// Form of the equation to solve + BoutSnesEquationForm equation_form; + std::string snes_type; BoutReal atol; ///< Absolute tolerance BoutReal rtol; ///< Relative tolerance @@ -112,7 +115,7 @@ private: BoutReal timestep_factor_on_lower_its; // Pseudo-Transient Continuation (PTC) variables - bool pseudo_time; ///< Use Pseudo time-stepping + // These are used if equation_form = pseudo_transient BoutReal pseudo_alpha; ///< dt = alpha / residual BoutReal pseudo_growth_factor; ///< Timestep increase 1.1 - 1.2 BoutReal pseudo_reduction_factor; ///< Timestep decrease 0.5 @@ -144,9 +147,6 @@ private: int nlocal; ///< Number of variables on local processor int neq; ///< Number of variables in total - /// Form of the equation to solve - BoutSnesEquationForm equation_form; - PetscLib lib; ///< Handles initialising, finalising PETSc Vec snes_f; ///< Used by SNES to store function Vec snes_x; ///< Result of SNES From a024d5858789f697c0a654e97dfcb99840a2b2ab Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 13 Oct 2025 11:14:11 -0700 Subject: [PATCH 046/461] snes: Refactoring, split rhs_function from snes_function The rhs_function returns the time derivatives (residuals) that are used in the PTC method. The snes_function calls rhs_function, then modifies the function depending on equation_form. --- src/solver/impls/snes/snes.cxx | 137 +++++++++++++-------------------- src/solver/impls/snes/snes.hxx | 9 +++ 2 files changed, 63 insertions(+), 83 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index b48afccc48..cec0c3e058 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -18,6 +18,8 @@ #include #include "petscsnes.h" +#include "petscsys.h" +#include "petscvec.h" class ColoringStencil { private: @@ -253,8 +255,9 @@ int SNESSolver::init() { VecDuplicate(snes_x, &snes_f); VecDuplicate(snes_x, &x0); - if (equation_form == BoutSnesEquationForm::rearranged_backward_euler) { - // Need an intermediate vector for rearranged Backward Euler + if ((equation_form == BoutSnesEquationForm::rearranged_backward_euler) + || (equation_form == BoutSnesEquationForm::pseudo_transient)) { + // Need an intermediate vector for rearranged Backward Euler or Pseudo-Transient Continuation ierr = VecDuplicate(snes_x, &delta_x); CHKERRQ(ierr); } @@ -931,7 +934,7 @@ int SNESSolver::run() { // Use (time1, x1) and (simtime, x0) to make prediction // snes_x <- x0 + (dt / (simtime - time1)) * (x0 - x1) // snes_x <- -β * x1 + (1 + β) * snes_x - BoutReal beta = dt / (simtime - time1); + const BoutReal beta = dt / (simtime - time1); VecAXPBY(snes_x, -beta, (1. + beta), x1); } @@ -1142,27 +1145,7 @@ int SNESSolver::run() { if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Adjust local timesteps - // Calculate residuals - if (scale_vars) { - // scaled_x <- snes_x * var_scaling_factors - PetscCall(VecPointwiseMult(scaled_x, snes_x, var_scaling_factors)); - const BoutReal* xdata = nullptr; - PetscCall(VecGetArrayRead(scaled_x, &xdata)); - load_vars(const_cast(xdata)); - PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); - } else { - const BoutReal* xdata = nullptr; - PetscCall(VecGetArrayRead(snes_x, &xdata)); - load_vars(const_cast(xdata)); - PetscCall(VecRestoreArrayRead(snes_x, &xdata)); - } - run_rhs(simtime); - { - BoutReal* fdata = nullptr; - PetscCall(VecGetArray(snes_f, &fdata)); - save_derivs(fdata); - PetscCall(VecRestoreArray(snes_f, &fdata)); - } + PetscCall(rhs_function(snes_x, snes_f, false)); // Compare previous_f with snes_f // Use a per-cell timestep so that e.g density and pressure @@ -1280,25 +1263,25 @@ int SNESSolver::run() { BoutReal mean_neighboring_dt = 0.0; int neighbor_count = 0; if (i3d.x() != 0) { - BoutReal val = pseudo_timestep[i3d.xm()]; + const BoutReal val = pseudo_timestep[i3d.xm()]; min_neighboring_dt = std::min(min_neighboring_dt, val); mean_neighboring_dt += val; ++neighbor_count; } if (i3d.x() != mesh->LocalNx - 1) { - BoutReal val = pseudo_timestep[i3d.xp()]; + const BoutReal val = pseudo_timestep[i3d.xp()]; min_neighboring_dt = std::min(min_neighboring_dt, val); mean_neighboring_dt += val; ++neighbor_count; } if (i3d.y() != 0) { - BoutReal val = pseudo_timestep[i3d.ym()]; + const BoutReal val = pseudo_timestep[i3d.ym()]; min_neighboring_dt = std::min(min_neighboring_dt, val); mean_neighboring_dt += val; ++neighbor_count; } if (i3d.x() != mesh->LocalNy - 1) { - BoutReal val = pseudo_timestep[i3d.yp()]; + const BoutReal val = pseudo_timestep[i3d.yp()]; min_neighboring_dt = std::min(min_neighboring_dt, val); mean_neighboring_dt += val; ++neighbor_count; @@ -1495,29 +1478,23 @@ BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, */ } -// f = rhs -PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { +PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { // Get data from PETSc into BOUT++ fields if (scale_vars) { // scaled_x <- x * var_scaling_factors - int ierr = VecPointwiseMult(scaled_x, x, var_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(scaled_x, x, var_scaling_factors)); const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); - load_vars(const_cast( - xdata)); // const_cast needed due to load_vars API. Not writing to xdata. - ierr = VecRestoreArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(scaled_x, &xdata)); + // const_cast needed due to load_vars API. Not writing to xdata. + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); } else { const BoutReal* xdata = nullptr; - int ierr = VecGetArrayRead(x, &xdata); - CHKERRQ(ierr); - load_vars(const_cast( - xdata)); // const_cast needed due to load_vars API. Not writing to xdata. - ierr = VecRestoreArrayRead(x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(x, &xdata)); + // const_cast needed due to load_vars API. Not writing to xdata. + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(x, &xdata)); } try { @@ -1527,28 +1504,35 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { // Simulation might fail, e.g. negative densities // if timestep too large output_warn.write("WARNING: BoutException thrown: {}\n", e.what()); + return 2; + } + + // Copy derivatives back + BoutReal* fdata = nullptr; + PetscCall(VecGetArray(f, &fdata)); + save_derivs(fdata); + PetscCall(VecRestoreArray(f, &fdata)); + return PETSC_SUCCESS; +} +// Result in f depends on equation_form +PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { + + // Call the RHS function + if (rhs_function(x, f, linear) != PETSC_SUCCESS) { // Tell SNES that the input was out of domain SNESSetFunctionDomainError(snes); // Note: Returning non-zero error here leaves vectors in locked state return 0; } - // Copy derivatives back - BoutReal* fdata = nullptr; - int ierr = VecGetArray(f, &fdata); - CHKERRQ(ierr); - save_derivs(fdata); - ierr = VecRestoreArray(f, &fdata); - CHKERRQ(ierr); - switch (equation_form) { case BoutSnesEquationForm::rearranged_backward_euler: { // Rearranged Backward Euler // f = (x0 - x)/Δt + f // First calculate x - x0 to minimise floating point issues VecWAXPY(delta_x, -1.0, x0, x); // delta_x = x - x0 - VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt + VecAXPY(f, -1. / dt, delta_x); // f <- f - delta_x / dt break; } case BoutSnesEquationForm::pseudo_transient: { @@ -1563,7 +1547,7 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { case BoutSnesEquationForm::backward_euler: { // Backward Euler // Set f = x - x0 - Δt*f - VecAYPX(f, -dt, x); // f <- x - Δt*f + VecAYPX(f, -dt, x); // f <- x - Δt*f VecAXPY(f, -1.0, x0); // f <- f - x0 break; } @@ -1578,8 +1562,7 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { if (scale_rhs) { // f <- f * rhs_scaling_factors - ierr = VecPointwiseMult(f, f, rhs_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(f, f, rhs_scaling_factors)); } return 0; @@ -1594,36 +1577,28 @@ PetscErrorCode SNESSolver::precon(Vec x, Vec f) { throw BoutException("No user preconditioner"); } - int ierr; - // Get data from PETSc into BOUT++ fields Vec solution; - SNESGetSolution(snes, &solution); + PetscCall(SNESGetSolution(snes, &solution)); BoutReal* soldata; - ierr = VecGetArray(solution, &soldata); - CHKERRQ(ierr); + PetscCall(VecGetArray(solution, &soldata)); load_vars(soldata); - ierr = VecRestoreArray(solution, &soldata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(solution, &soldata)); // Load vector to be inverted into ddt() variables const BoutReal* xdata; - ierr = VecGetArrayRead(x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(x, &xdata)); load_derivs(const_cast(xdata)); // Note: load_derivs does not modify data - ierr = VecRestoreArrayRead(x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(x, &xdata)); // Run the preconditioner runPreconditioner(simtime + dt, dt, 0.0); // Save the solution from F_vars BoutReal* fdata; - ierr = VecGetArray(f, &fdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(f, &fdata)); save_derivs(fdata); - ierr = VecRestoreArray(f, &fdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(f, &fdata)); return 0; } @@ -1635,8 +1610,6 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { return 0; // Not scaling the RHS values } - int ierr; - // Get index of rows owned by this processor int rstart, rend; MatGetOwnershipRange(Jac_new, &rstart, &rend); @@ -1651,8 +1624,7 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { // Calculate the norm of each row of the Jacobian PetscScalar* row_inv_norm_data; - ierr = VecGetArray(jac_row_inv_norms, &row_inv_norm_data); - CHKERRQ(ierr); + PetscCall(VecGetArray(jac_row_inv_norms, &row_inv_norm_data)); PetscInt ncols; const PetscScalar* vals; @@ -1676,12 +1648,11 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { MatRestoreRow(Jac_new, row, &ncols, nullptr, &vals); } - ierr = VecRestoreArray(jac_row_inv_norms, &row_inv_norm_data); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(jac_row_inv_norms, &row_inv_norm_data)); // Modify the RHS scaling: factor = factor / norm - ierr = VecPointwiseMult(rhs_scaling_factors, rhs_scaling_factors, jac_row_inv_norms); - CHKERRQ(ierr); + PetscCall( + VecPointwiseMult(rhs_scaling_factors, rhs_scaling_factors, jac_row_inv_norms)); if (diagnose) { // Print maximum and minimum scaling factors @@ -1692,10 +1663,9 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { } // Scale the Jacobian rows by multiplying on the left by 1/norm - ierr = MatDiagonalScale(Jac_new, jac_row_inv_norms, nullptr); - CHKERRQ(ierr); + PetscCall(MatDiagonalScale(Jac_new, jac_row_inv_norms, nullptr)); - return 0; + return PETSC_SUCCESS; } /// @@ -1790,7 +1760,8 @@ void SNESSolver::outputVars(Options& output_options, bool save_repeat) { } if (equation_form == BoutSnesEquationForm::pseudo_transient) { - output_options["snes_pseudo_alpha"].assignRepeat(pseudo_alpha, "t", save_repeat, "SNESSolver"); + output_options["snes_pseudo_alpha"].assignRepeat(pseudo_alpha, "t", save_repeat, + "SNESSolver"); output_options["snes_pseudo_residual"].assignRepeat(pseudo_residual, "t", save_repeat, "SNESSolver"); output_options["snes_pseudo_timestep"].assignRepeat(pseudo_timestep, "t", save_repeat, diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 257588e2a9..53b87d6c28 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -69,6 +69,8 @@ public: /// /// f = (x - gamma*G(x)) - rhs /// + /// The form depends on equation_form + /// /// /// @param[in] x The state vector /// @param[out] f The vector for the result f(x) @@ -93,6 +95,13 @@ public: void outputVars(Options& output_options, bool save_repeat = true) override; private: + /// Call the physics model RHS function + /// + /// @param[in] x The state vector. Will be scaled if scale_vars=true + /// @param[out] f The vector for the result f(x) + /// @param[in] linear Specifies that the SNES solver is in a linear (KSP) inner loop + PetscErrorCode rhs_function(Vec x, Vec f, bool linear); + BoutReal timestep; ///< Internal timestep BoutReal dt; ///< Current timestep used in snes_function. BoutReal dt_min_reset; ///< If dt falls below this, reset solve From 37187e332893dea215871337258db3c1d812e314 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 13 Oct 2025 12:23:27 -0700 Subject: [PATCH 047/461] snes: Add pseudo_strategy option Switches between different strategies for choosing cell time steps. --- src/solver/impls/snes/snes.cxx | 78 +++++++++++++++++++++------------- src/solver/impls/snes/snes.hxx | 11 +++++ 2 files changed, 59 insertions(+), 30 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index cec0c3e058..1adb2c4b46 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -12,6 +12,7 @@ #include #include +#include #include #include @@ -145,6 +146,9 @@ SNESSolver::SNESSolver(Options* opts) (*options)["timestep_factor_on_lower_its"] .doc("Multiply timestep if iterations are below lower_its") .withDefault(1.4)), + pseudo_strategy((*options)["pseudo_strategy"] + .doc("PTC strategy to use when setting timesteps") + .withDefault(BoutPTCStrategy::inverse_residual)), pseudo_alpha((*options)["pseudo_alpha"] .doc("Sets timestep using dt = alpha / residual") .withDefault(100. * atol * timestep)), @@ -401,18 +405,13 @@ int SNESSolver::init() { auto n_square = (*options)["stencil:square"] .doc("Extent of stencil (square)") .withDefault(0); - auto n_taxi = (*options)["stencil:taxi"] - .doc("Extent of stencil (taxi-cab norm)") - .withDefault(0); auto n_cross = (*options)["stencil:cross"] .doc("Extent of stencil (cross)") .withDefault(0); // Set n_taxi 2 if nothing else is set - // Probably a better way to do this - if (n_square == 0 && n_taxi == 0 && n_cross == 0) { - output_info.write("Setting solver:stencil:taxi = 2\n"); - n_taxi = 2; - } + auto n_taxi = (*options)["stencil:taxi"] + .doc("Extent of stencil (taxi-cab norm)") + .withDefault((n_square == 0 && n_cross == 0) ? 2 : 0); auto const xy_offsets = ColoringStencil::getOffsets(n_square, n_taxi, n_cross); { @@ -1428,26 +1427,21 @@ int SNESSolver::run() { return 0; } -/// Switched Evolution Relaxation (SER) -BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, - BoutReal previous_residual, - BoutReal current_residual) { - - // Timestep inversely proportional to the residual, clipped to avoid - // rapid changes in timestep. +/// Timestep inversely proportional to the residual, clipped to avoid +/// rapid changes in timestep. +BoutReal SNESSolver::updatePseudoTimestep_inverse_residual(BoutReal previous_timestep, + BoutReal current_residual) { return std::min( {std::max({pseudo_alpha / current_residual, previous_timestep / 1.5, dt_min_reset}), 1.5 * previous_timestep, max_timestep}); +} - /* - - // Alternative strategy based on history of residuals - // A hybrid strategy may be most effective, in which the timestep is - // inversely proportional to residual initially, or when residuals are large, - // and then the method transitions to being history-based - - const BoutReal converged_threshold = 100 * atol; - const BoutReal transition_threshold = 1000 * atol; +// Strategy based on history of residuals +BoutReal SNESSolver::updatePseudoTimestep_history_based(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual) { + const BoutReal converged_threshold = 10 * atol; + const BoutReal transition_threshold = 100 * atol; if (current_residual < converged_threshold) { return max_timestep; @@ -1455,13 +1449,14 @@ BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, // Smoothly transition from SER updates to frozen timestep if (current_residual < transition_threshold) { - BoutReal reduction_ratio = current_residual / previous_residual; + const BoutReal reduction_ratio = current_residual / previous_residual; if (reduction_ratio < 0.8) { - return std::min(0.5*(pseudo_growth_factor + 1.) * previous_timestep, + return std::min(0.5 * (pseudo_growth_factor + 1.) * previous_timestep, max_timestep); } else if (reduction_ratio > 1.2) { - return std::max(0.5*(pseudo_reduction_factor + 1) * previous_timestep, dt_min_reset); + return std::max(0.5 * (pseudo_reduction_factor + 1) * previous_timestep, + dt_min_reset); } // Leave unchanged if residual changes are small return previous_timestep; @@ -1469,13 +1464,36 @@ BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, if (current_residual <= previous_residual) { // Success - return std::min(pseudo_growth_factor * previous_timestep, - max_timestep); + return std::min(pseudo_growth_factor * previous_timestep, max_timestep); } // Failed // Consider rejecting the step return std::max(pseudo_reduction_factor * previous_timestep, dt_min_reset); - */ +} + +/// Switched Evolution Relaxation (SER) +BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual) { + switch (pseudo_strategy) { + case BoutPTCStrategy::inverse_residual: + return updatePseudoTimestep_inverse_residual(previous_timestep, current_residual); + + case BoutPTCStrategy::history_based: + return updatePseudoTimestep_history_based(previous_timestep, previous_residual, + current_residual); + + case BoutPTCStrategy::hybrid: + // A hybrid strategy may be most effective, in which the timestep is + // inversely proportional to residual initially, or when residuals are large, + // and then the method transitions to being history-based + if (current_residual > 1000. * atol) { + return updatePseudoTimestep_inverse_residual(previous_timestep, current_residual); + } else { + return updatePseudoTimestep_history_based(previous_timestep, previous_residual, + current_residual); + } + }; } PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 53b87d6c28..a7ed032a8f 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -53,6 +53,11 @@ RegisterSolver registersolverbeuler("beuler"); BOUT_ENUM_CLASS(BoutSnesEquationForm, pseudo_transient, rearranged_backward_euler, backward_euler, direct_newton); +BOUT_ENUM_CLASS(BoutPTCStrategy, + inverse_residual, ///< dt = pseudo_alpha / residual + history_based, ///< Grow/shrink dt based on residual decrease/increase + hybrid); ///< Combine inverse_residual and history_based strategies + /// Uses PETSc's SNES interface to find a steady state solution to a /// nonlinear ODE by integrating in time with Backward Euler class SNESSolver : public Solver { @@ -125,6 +130,7 @@ private: // Pseudo-Transient Continuation (PTC) variables // These are used if equation_form = pseudo_transient + BoutPTCStrategy pseudo_strategy; ///< Strategy to use when setting timesteps BoutReal pseudo_alpha; ///< dt = alpha / residual BoutReal pseudo_growth_factor; ///< Timestep increase 1.1 - 1.2 BoutReal pseudo_reduction_factor; ///< Timestep decrease 0.5 @@ -134,6 +140,11 @@ private: /// Decide the next pseudo-timestep BoutReal updatePseudoTimestep(BoutReal previous_timestep, BoutReal previous_residual, BoutReal current_residual); + BoutReal updatePseudoTimestep_inverse_residual(BoutReal previous_timestep, + BoutReal current_residual); + BoutReal updatePseudoTimestep_history_based(BoutReal previous_timestep, + BoutReal previous_residual, + BoutReal current_residual); Field3D pseudo_residual; ///< Diagnostic output Field3D pseudo_timestep; From 07c2882ef08b26b0f8f0c7405e372c0b47c9247e Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 13 Oct 2025 13:03:58 -0700 Subject: [PATCH 048/461] snes: Replace CHKERRQ with PetscCall macro Minor tidying --- src/solver/impls/snes/snes.cxx | 135 ++++++++++++--------------------- 1 file changed, 47 insertions(+), 88 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 1adb2c4b46..dbcaa0fa6a 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -94,8 +94,7 @@ PetscErrorCode FormFunctionForColoring(void* UNUSED(snes), Vec x, Vec f, void* c PetscErrorCode snesPCapply(PC pc, Vec x, Vec y) { // Get the context SNESSolver* s; - int ierr = PCShellGetContext(pc, reinterpret_cast(&s)); - CHKERRQ(ierr); + PetscCall(PCShellGetContext(pc, reinterpret_cast(&s))); PetscFunctionReturn(s->precon(x, y)); } @@ -245,55 +244,43 @@ int SNESSolver::init() { n3Dvars(), n2Dvars(), neq, nlocal); // Initialise PETSc components - int ierr; // Vectors output_info.write("Creating vector\n"); - ierr = VecCreate(BoutComm::get(), &snes_x); - CHKERRQ(ierr); - ierr = VecSetSizes(snes_x, nlocal, PETSC_DECIDE); - CHKERRQ(ierr); - ierr = VecSetFromOptions(snes_x); - CHKERRQ(ierr); + PetscCall(VecCreate(BoutComm::get(), &snes_x)); + PetscCall(VecSetSizes(snes_x, nlocal, PETSC_DECIDE)); + PetscCall(VecSetFromOptions(snes_x)); - VecDuplicate(snes_x, &snes_f); - VecDuplicate(snes_x, &x0); + PetscCall(VecDuplicate(snes_x, &snes_f)); + PetscCall(VecDuplicate(snes_x, &x0)); if ((equation_form == BoutSnesEquationForm::rearranged_backward_euler) || (equation_form == BoutSnesEquationForm::pseudo_transient)) { // Need an intermediate vector for rearranged Backward Euler or Pseudo-Transient Continuation - ierr = VecDuplicate(snes_x, &delta_x); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &delta_x)); } if (predictor) { // Storage for previous solution - ierr = VecDuplicate(snes_x, &x1); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &x1)); } if (scale_rhs) { // Storage for rhs factors, one per evolving variable - ierr = VecDuplicate(snes_x, &rhs_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &rhs_scaling_factors)); // Set all factors to 1 to start with - ierr = VecSet(rhs_scaling_factors, 1.0); - CHKERRQ(ierr); + PetscCall(VecSet(rhs_scaling_factors, 1.0)); // Array to store inverse Jacobian row norms - ierr = VecDuplicate(snes_x, &jac_row_inv_norms); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &jac_row_inv_norms)); } if (scale_vars) { // Storage for var factors, one per evolving variable - ierr = VecDuplicate(snes_x, &var_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &var_scaling_factors)); // Set all factors to 1 to start with - ierr = VecSet(var_scaling_factors, 1.0); - CHKERRQ(ierr); + PetscCall(VecSet(var_scaling_factors, 1.0)); // Storage for scaled 'x' state vectors - ierr = VecDuplicate(snes_x, &scaled_x); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &scaled_x)); } if (equation_form == BoutSnesEquationForm::pseudo_transient) { @@ -360,8 +347,7 @@ int SNESSolver::init() { // Create a vector to store interpolated output solution // Used so that the timestep does not have to be adjusted, // because that would require updating the preconditioner. - ierr = VecDuplicate(snes_x, &output_x); - CHKERRQ(ierr); + PetscCall(VecDuplicate(snes_x, &output_x)); if (use_coloring) { // Use matrix coloring. @@ -560,8 +546,7 @@ int SNESSolver::init() { // Depends on all variables on this cell for (int j = 0; j < n2d; j++) { PetscInt col = ind2 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - CHKERRQ(ierr); + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); } } } @@ -578,8 +563,7 @@ int SNESSolver::init() { // Depends on 2D fields for (int j = 0; j < n2d; j++) { PetscInt col = ind0 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - CHKERRQ(ierr); + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); } // Star pattern @@ -604,9 +588,9 @@ int SNESSolver::init() { // 3D fields on this cell for (int j = 0; j < n3d; j++) { PetscInt col = ind2 + j; - ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); + PetscErrorCode ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - if (ierr != 0) { + if (ierr != PETSC_SUCCESS) { output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", row, x, y, xi, yi, ind2, ind2 + n3d - 1); } @@ -631,8 +615,7 @@ int SNESSolver::init() { // Test if the matrix is symmetric // Values are 0 or 1 so tolerance (1e-5) shouldn't matter PetscBool symmetric; - ierr = MatIsSymmetric(Jfd, 1e-5, &symmetric); - CHKERRQ(ierr); + PetscCall(MatIsSymmetric(Jfd, 1e-5, &symmetric)); if (!symmetric) { output_warn.write("Jacobian pattern is not symmetric\n"); } @@ -657,8 +640,7 @@ int SNESSolver::init() { if (prune_jacobian) { // Will remove small elements from the Jacobian. // Save a copy to recover from over-pruning - ierr = MatDuplicate(Jfd, MAT_SHARE_NONZERO_PATTERN, &Jfd_original); - CHKERRQ(ierr); + PetscCall(MatDuplicate(Jfd, MAT_SHARE_NONZERO_PATTERN, &Jfd_original)); } } else { // Brute force calculation @@ -785,15 +767,13 @@ int SNESSolver::init() { int SNESSolver::run() { TRACE("SNESSolver::run()"); - int ierr; + // Set initial guess at the solution from variables { BoutReal* xdata = nullptr; - int ierr = VecGetArray(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(snes_x, &xdata)); save_vars(xdata); - ierr = VecRestoreArray(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(snes_x, &xdata)); } if (equation_form == BoutSnesEquationForm::pseudo_transient) { @@ -801,11 +781,9 @@ int SNESSolver::run() { run_rhs(simtime); { BoutReal* fdata = nullptr; - ierr = VecGetArray(snes_f, &fdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(snes_f, &fdata)); save_derivs(fdata); - ierr = VecRestoreArray(snes_f, &fdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(snes_f, &fdata)); } } @@ -834,14 +812,11 @@ int SNESSolver::run() { // Take ownership of snes_x and var_scaling_factors data PetscScalar* snes_x_data = nullptr; - ierr = VecGetArray(snes_x, &snes_x_data); - CHKERRQ(ierr); + PetscCall(VecGetArray(snes_x, &snes_x_data)); PetscScalar* x1_data; - ierr = VecGetArray(x1, &x1_data); - CHKERRQ(ierr); + PetscCall(VecGetArray(x1, &x1_data)); PetscScalar* var_scaling_factors_data; - ierr = VecGetArray(var_scaling_factors, &var_scaling_factors_data); - CHKERRQ(ierr); + PetscCall(VecGetArray(var_scaling_factors, &var_scaling_factors_data)); // Normalise each value in the state // Limit normalisation so scaling factor is never smaller than rtol @@ -854,12 +829,9 @@ int SNESSolver::run() { } // Restore vector underlying data - ierr = VecRestoreArray(var_scaling_factors, &var_scaling_factors_data); - CHKERRQ(ierr); - ierr = VecRestoreArray(x1, &x1_data); - CHKERRQ(ierr); - ierr = VecRestoreArray(snes_x, &snes_x_data); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(var_scaling_factors, &var_scaling_factors_data)); + PetscCall(VecRestoreArray(x1, &x1_data)); + PetscCall(VecRestoreArray(snes_x, &snes_x_data)); if (diagnose) { // Print maximum and minimum scaling factors @@ -955,7 +927,7 @@ int SNESSolver::run() { int lin_its; SNESGetLinearSolveIterations(snes, &lin_its); - if ((ierr != 0) or (reason < 0)) { + if ((ierr != PETSC_SUCCESS) or (reason < 0)) { // Diverged or SNES failed if (diagnose_failures) { @@ -1001,8 +973,7 @@ int SNESSolver::run() { if (diagnose) { output.write("\nRestoring Jacobian\n"); } - ierr = MatCopy(Jfd_original, Jfd, DIFFERENT_NONZERO_PATTERN); - CHKERRQ(ierr); + PetscCall(MatCopy(Jfd_original, Jfd, DIFFERENT_NONZERO_PATTERN)); // The non-zero pattern has changed, so update coloring updateColoring(); jacobian_pruned = false; // Reset flag. Will be set after pruning. @@ -1048,33 +1019,26 @@ int SNESSolver::run() { if (scale_vars) { // scaled_x <- snes_x * var_scaling_factors - ierr = VecPointwiseMult(scaled_x, snes_x, var_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(scaled_x, snes_x, var_scaling_factors)); const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(scaled_x, &xdata)); load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); } else { const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(snes_x, &xdata)); load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(snes_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(snes_x, &xdata)); } run_rhs(simtime); // Copy derivatives back { BoutReal* fdata = nullptr; - ierr = VecGetArray(snes_f, &fdata); - CHKERRQ(ierr); + PetscCall(VecGetArray(snes_f, &fdata)); save_derivs(fdata); - ierr = VecRestoreArray(snes_f, &fdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArray(snes_f, &fdata)); } // Forward Euler @@ -1130,7 +1094,7 @@ int SNESSolver::run() { } // Prune Jacobian, keeping diagonal elements - ierr = MatFilter(Jfd, prune_abstol, PETSC_TRUE, PETSC_TRUE); + PetscCall(MatFilter(Jfd, prune_abstol, PETSC_TRUE, PETSC_TRUE)); // Update the coloring from Jfd matrix updateColoring(); @@ -1400,22 +1364,17 @@ int SNESSolver::run() { // Put the result into variables if (scale_vars) { // scaled_x <- output_x * var_scaling_factors - int ierr = VecPointwiseMult(scaled_x, output_x, var_scaling_factors); - CHKERRQ(ierr); + PetscCall(VecPointwiseMult(scaled_x, output_x, var_scaling_factors)); const BoutReal* xdata = nullptr; - ierr = VecGetArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(scaled_x, &xdata)); load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(scaled_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); } else { const BoutReal* xdata = nullptr; - int ierr = VecGetArrayRead(output_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecGetArrayRead(output_x, &xdata)); load_vars(const_cast(xdata)); - ierr = VecRestoreArrayRead(output_x, &xdata); - CHKERRQ(ierr); + PetscCall(VecRestoreArrayRead(output_x, &xdata)); } run_rhs(target); // Run RHS to calculate auxilliary variables From cc58791f15881b4fdf722916f672029d28bee584 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 13 Oct 2025 15:06:10 -0700 Subject: [PATCH 049/461] snes: Split PTC implementation into separate functions Trying to simplify the `run()` function by splitting out pieces into separate functions. --- src/solver/impls/snes/snes.cxx | 353 +++++++++++++++------------------ src/solver/impls/snes/snes.hxx | 9 +- 2 files changed, 172 insertions(+), 190 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index dbcaa0fa6a..1840dd21c1 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -284,18 +284,7 @@ int SNESSolver::init() { } if (equation_form == BoutSnesEquationForm::pseudo_transient) { - // Storage for per-variable timestep - PetscCall(VecDuplicate(snes_x, &dt_vec)); - // Starting timestep - PetscCall(VecSet(dt_vec, timestep)); - - // Storage for previous residual. Used to compare - // residuals before and after step, and adjust timestep accordingly - PetscCall(VecDuplicate(snes_x, &previous_f)); - - // Diagnostic outputs - pseudo_timestep = timestep; - pseudo_residual = 0.0; + PetscCall(initPseudoTimestepping()); } // Nonlinear solver interface (SNES) @@ -588,7 +577,8 @@ int SNESSolver::init() { // 3D fields on this cell for (int j = 0; j < n3d; j++) { PetscInt col = ind2 + j; - PetscErrorCode ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); + PetscErrorCode ierr = + MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); if (ierr != PETSC_SUCCESS) { output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", @@ -859,11 +849,6 @@ int SNESSolver::run() { PetscCall(VecMin(dt_vec, nullptr, ×tep)); dt = timestep; - // Copy residual from snes_f to previous_f - // The value of snes_f is set before the timestepping loop - // then calculated after the timestep - PetscCall(VecCopy(snes_f, previous_f)); - looping = true; if (simtime + timestep >= target) { looping = false; @@ -1107,176 +1092,7 @@ int SNESSolver::run() { if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Adjust local timesteps - - PetscCall(rhs_function(snes_x, snes_f, false)); - - // Compare previous_f with snes_f - // Use a per-cell timestep so that e.g density and pressure - // evolve in a way consistent with the equation of state. - - // Reading the residual vectors - const BoutReal* previous_residual = nullptr; - PetscCall(VecGetArrayRead(previous_f, &previous_residual)); - const BoutReal* current_residual = nullptr; - PetscCall(VecGetArrayRead(snes_f, ¤t_residual)); - // Modifying the dt_vec values - BoutReal* dt_data = nullptr; - PetscCall(VecGetArray(dt_vec, &dt_data)); - - // Note: The ordering of quantities in the PETSc vectors - // depends on the Solver::loop_vars function - Mesh* mesh = bout::globals::mesh; - int idx = 0; // Index into PETSc Vecs - - // Boundary cells - for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { - // Field2D quantities evolved together - int count = 0; - BoutReal R0 = 0.0; - BoutReal R1 = 0.0; - - for (const auto& f : f2d) { - if (!f.evolve_bndry) { - continue; // Not evolving boundary => Skip - } - R0 += SQ(previous_residual[idx + count]); - R1 += SQ(current_residual[idx + count]); - ++count; - } - if (count > 0) { - R0 = sqrt(R0 / count); - R1 = sqrt(R1 / count); - - // Adjust timestep for these quantities - BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); - for (int i = 0; i != count; ++i) { - dt_data[idx++] = new_timestep; - } - } - - // Field3D quantities evolved together within a cell - for (int jz = 0; jz < mesh->LocalNz; jz++) { - count = 0; - R0 = 0.0; - R1 = 0.0; - for (const auto& f : f3d) { - if (!f.evolve_bndry) { - continue; // Not evolving boundary => Skip - } - R0 += SQ(previous_residual[idx + count]); - R1 += SQ(current_residual[idx + count]); - ++count; - } - if (count > 0) { - R0 = sqrt(R0 / count); - R1 = sqrt(R1 / count); - - BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); - - auto i3d = mesh->ind2Dto3D(i2d, jz); - pseudo_residual[i3d] = R1; - pseudo_timestep[i3d] = new_timestep; - - for (int i = 0; i != count; ++i) { - dt_data[idx++] = new_timestep; - } - } - } - } - - // Bulk of domain. - // These loops don't check the boundary flags - for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { - // Field2D quantities evolved together - if (f2d.size() > 0) { - BoutReal R0 = 0.0; - BoutReal R1 = 0.0; - for (std::size_t i = 0; i != f2d.size(); ++i) { - R0 += SQ(previous_residual[idx + i]); - R1 += SQ(current_residual[idx + i]); - } - R0 = sqrt(R0 / f2d.size()); - R1 = sqrt(R1 / f2d.size()); - - // Adjust timestep for these quantities - BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); - for (std::size_t i = 0; i != f2d.size(); ++i) { - dt_data[idx++] = new_timestep; - } - } - - // Field3D quantities evolved together within a cell - if (f3d.size() > 0) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { - BoutReal R0 = 0.0; - BoutReal R1 = 0.0; - for (std::size_t i = 0; i != f3d.size(); ++i) { - R0 += SQ(previous_residual[idx + i]); - R1 += SQ(current_residual[idx + i]); - } - R0 = sqrt(R0 / f3d.size()); - R1 = sqrt(R1 / f3d.size()); - BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], R0, R1); - - auto i3d = mesh->ind2Dto3D(i2d, jz); - pseudo_residual[i3d] = R1; - - // Compare to neighbors - BoutReal min_neighboring_dt = max_timestep; - BoutReal mean_neighboring_dt = 0.0; - int neighbor_count = 0; - if (i3d.x() != 0) { - const BoutReal val = pseudo_timestep[i3d.xm()]; - min_neighboring_dt = std::min(min_neighboring_dt, val); - mean_neighboring_dt += val; - ++neighbor_count; - } - if (i3d.x() != mesh->LocalNx - 1) { - const BoutReal val = pseudo_timestep[i3d.xp()]; - min_neighboring_dt = std::min(min_neighboring_dt, val); - mean_neighboring_dt += val; - ++neighbor_count; - } - if (i3d.y() != 0) { - const BoutReal val = pseudo_timestep[i3d.ym()]; - min_neighboring_dt = std::min(min_neighboring_dt, val); - mean_neighboring_dt += val; - ++neighbor_count; - } - if (i3d.x() != mesh->LocalNy - 1) { - const BoutReal val = pseudo_timestep[i3d.yp()]; - min_neighboring_dt = std::min(min_neighboring_dt, val); - mean_neighboring_dt += val; - ++neighbor_count; - } - mean_neighboring_dt /= neighbor_count; - - // Smooth - //new_timestep = 0.1 * mean_neighboring_dt + 0.9 * new_timestep; - - // Limit ratio of timestep between neighboring cells - new_timestep = - std::min(new_timestep, pseudo_max_ratio * min_neighboring_dt); - - pseudo_timestep[i3d] = new_timestep; - - for (std::size_t i = 0; i != f3d.size(); ++i) { - dt_data[idx++] = new_timestep; - } - } - } - } - - // Restore Vec data arrays - PetscCall(VecRestoreArrayRead(previous_f, &previous_residual)); - PetscCall(VecRestoreArrayRead(snes_f, ¤t_residual)); - PetscCall(VecRestoreArray(dt_vec, &dt_data)); - - // Need timesteps on neighboring processors - // to limit ratio - mesh->communicate(pseudo_timestep); - // Neumann boundary so timestep isn't pinned at boundaries - pseudo_timestep.applyBoundary("neumann"); + PetscCall(updatePseudoTimestepping(snes_x)); if (pid_controller) { // Adjust pseudo_alpha based on nonlinear iterations @@ -1386,6 +1202,167 @@ int SNESSolver::run() { return 0; } +PetscErrorCode SNESSolver::initPseudoTimestepping() { + // Storage for per-variable timestep + PetscCall(VecDuplicate(snes_x, &dt_vec)); + // Starting timestep + PetscCall(VecSet(dt_vec, timestep)); + + // Diagnostic outputs + pseudo_timestep = timestep; + pseudo_residual = 0.0; + pseudo_residual_2d = 0.0; + + return PETSC_SUCCESS; +} + +PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { + // Call RHS function to get time derivatives + PetscCall(rhs_function(x, snes_f, false)); + + // Use a per-cell timestep so that e.g density and pressure + // evolve in a way consistent with the equation of state. + + // Reading the residual vectors + const BoutReal* current_residual = nullptr; + PetscCall(VecGetArrayRead(snes_f, ¤t_residual)); + // Modifying the dt_vec values + BoutReal* dt_data = nullptr; + PetscCall(VecGetArray(dt_vec, &dt_data)); + + // Note: The ordering of quantities in the PETSc vectors + // depends on the Solver::loop_vars function + Mesh* mesh = bout::globals::mesh; + int idx = 0; // Index into PETSc Vecs + + // Boundary cells + for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { + // Field2D quantities evolved together + int count = 0; + BoutReal residual = 0.0; + + for (const auto& f : f2d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + residual += SQ(current_residual[idx + count]); + ++count; + } + if (count > 0) { + residual = sqrt(residual / count); + + // Adjust timestep for these quantities + BoutReal new_timestep = + updatePseudoTimestep(dt_data[idx], pseudo_residual_2d[i2d], residual); + for (int i = 0; i != count; ++i) { + dt_data[idx++] = new_timestep; + } + pseudo_residual_2d[i2d] = residual; + } + + // Field3D quantities evolved together within a cell + for (int jz = 0; jz < mesh->LocalNz; jz++) { + count = 0; + residual = 0.0; + for (const auto& f : f3d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + residual += SQ(current_residual[idx + count]); + ++count; + } + if (count > 0) { + residual = sqrt(residual / count); + + auto i3d = mesh->ind2Dto3D(i2d, jz); + BoutReal new_timestep = + updatePseudoTimestep(dt_data[idx], pseudo_residual[i3d], residual); + + pseudo_residual[i3d] = residual; + pseudo_timestep[i3d] = new_timestep; + + for (int i = 0; i != count; ++i) { + dt_data[idx++] = new_timestep; + } + } + } + } + + // Bulk of domain. + // These loops don't check the boundary flags + for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { + // Field2D quantities evolved together + if (f2d.size() > 0) { + BoutReal residual = 0.0; + for (std::size_t i = 0; i != f2d.size(); ++i) { + residual += SQ(current_residual[idx + i]); + } + residual = sqrt(residual / f2d.size()); + + // Adjust timestep for these quantities + BoutReal new_timestep = + updatePseudoTimestep(dt_data[idx], pseudo_residual_2d[i2d], residual); + for (std::size_t i = 0; i != f2d.size(); ++i) { + dt_data[idx++] = new_timestep; + } + pseudo_residual_2d[i2d] = residual; + } + + // Field3D quantities evolved together within a cell + if (f3d.size() > 0) { + for (int jz = 0; jz < mesh->LocalNz; jz++) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + + BoutReal residual = 0.0; + for (std::size_t i = 0; i != f3d.size(); ++i) { + residual += SQ(current_residual[idx + i]); + } + residual = sqrt(residual / f3d.size()); + BoutReal new_timestep = + updatePseudoTimestep(dt_data[idx], pseudo_residual[i3d], residual); + + pseudo_residual[i3d] = residual; + + // Compare to neighbors + BoutReal min_neighboring_dt = max_timestep; + if (i3d.x() != 0) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.xm()]); + } + if (i3d.x() != mesh->LocalNx - 1) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.xp()]); + } + if (i3d.y() != 0) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.ym()]); + } + if (i3d.x() != mesh->LocalNy - 1) { + min_neighboring_dt = std::min(min_neighboring_dt, pseudo_timestep[i3d.yp()]); + } + + // Limit ratio of timestep between neighboring cells + new_timestep = std::min(new_timestep, pseudo_max_ratio * min_neighboring_dt); + + pseudo_timestep[i3d] = new_timestep; + + for (std::size_t i = 0; i != f3d.size(); ++i) { + dt_data[idx++] = new_timestep; + } + } + } + } + + // Restore Vec data arrays + PetscCall(VecRestoreArrayRead(snes_f, ¤t_residual)); + PetscCall(VecRestoreArray(dt_vec, &dt_data)); + + // Need timesteps on neighboring processors + // to limit ratio + mesh->communicate(pseudo_timestep); + // Neumann boundary so timestep isn't pinned at boundaries + pseudo_timestep.applyBoundary("neumann"); + + return PETSC_SUCCESS; +} + /// Timestep inversely proportional to the residual, clipped to avoid /// rapid changes in timestep. BoutReal SNESSolver::updatePseudoTimestep_inverse_residual(BoutReal previous_timestep, diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index a7ed032a8f..9086106ecd 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -136,8 +136,12 @@ private: BoutReal pseudo_reduction_factor; ///< Timestep decrease 0.5 BoutReal pseudo_max_ratio; ///< Maximum timestep ratio between neighboring cells Vec dt_vec; ///< Each quantity can have its own timestep - Vec previous_f; ///< Previous residual - /// Decide the next pseudo-timestep + + /// Initialize the Pseudo-Transient Continuation method + PetscErrorCode initPseudoTimestepping(); + /// Update dt_vec based on new solution x + PetscErrorCode updatePseudoTimestepping(Vec x); + /// Decide the next pseudo-timestep. Called by updatePseudoTimestepping BoutReal updatePseudoTimestep(BoutReal previous_timestep, BoutReal previous_residual, BoutReal current_residual); BoutReal updatePseudoTimestep_inverse_residual(BoutReal previous_timestep, @@ -146,6 +150,7 @@ private: BoutReal previous_residual, BoutReal current_residual); Field3D pseudo_residual; ///< Diagnostic output + Field2D pseudo_residual_2d; Field3D pseudo_timestep; ///< PID controller parameters From dcbdc5d13dcb4e44801a55d5ff6b27ce23679bf0 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 13 Oct 2025 19:45:56 -0700 Subject: [PATCH 050/461] snes: Refactoring Jacobian coloring and pruning Moving Jacobian calculations into separate functions. --- src/solver/impls/snes/snes.cxx | 728 +++++++++++++++++---------------- src/solver/impls/snes/snes.hxx | 11 + 2 files changed, 380 insertions(+), 359 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 1840dd21c1..838da5fa5a 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -18,6 +18,7 @@ #include +#include "petscerror.h" #include "petscsnes.h" #include "petscsys.h" #include "petscvec.h" @@ -100,6 +101,371 @@ PetscErrorCode snesPCapply(PC pc, Vec x, Vec y) { } } // namespace +PetscErrorCode SNESSolver::FDJinitialise() { + if (use_coloring) { + // Use matrix coloring. + // This greatly reduces the number of times the rhs() function + // needs to be evaluated when calculating the Jacobian. + + // Use global mesh for now + Mesh* mesh = bout::globals::mesh; + + ////////////////////////////////////////////////// + // Get the local indices by starting at 0 + Field3D index = globalIndex(0); + + ////////////////////////////////////////////////// + // Pre-allocate PETSc storage + + output_progress.write("Setting Jacobian matrix sizes\n"); + + const int n2d = f2d.size(); + const int n3d = f3d.size(); + + // Set size of Matrix on each processor to nlocal x nlocal + MatCreate(BoutComm::get(), &Jfd); + MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE); + MatSetSizes(Jfd, nlocal, nlocal, PETSC_DETERMINE, PETSC_DETERMINE); + MatSetFromOptions(Jfd); + // Determine which row/columns of the matrix are locally owned + int Istart, Iend; + MatGetOwnershipRange(Jfd, &Istart, &Iend); + // Convert local into global indices + // Note: Not in the boundary cells, to keep -1 values + for (const auto& i : mesh->getRegion3D("RGN_NOBNDRY")) { + index[i] += Istart; + } + // Now communicate to fill guard cells + mesh->communicate(index); + + // Non-zero elements on this processor + std::vector d_nnz; + std::vector o_nnz; + auto n_square = (*options)["stencil:square"] + .doc("Extent of stencil (square)") + .withDefault(0); + auto n_cross = + (*options)["stencil:cross"].doc("Extent of stencil (cross)").withDefault(0); + // Set n_taxi 2 if nothing else is set + auto n_taxi = (*options)["stencil:taxi"] + .doc("Extent of stencil (taxi-cab norm)") + .withDefault((n_square == 0 && n_cross == 0) ? 2 : 0); + + auto const xy_offsets = ColoringStencil::getOffsets(n_square, n_taxi, n_cross); + { + // This is ugly but can't think of a better and robust way to + // count the non-zeros for some arbitrary stencil + // effectively the same loop as the one that sets the non-zeros below + std::vector> d_nnz_map2d(nlocal); + std::vector> o_nnz_map2d(nlocal); + std::vector> d_nnz_map3d(nlocal); + std::vector> o_nnz_map3d(nlocal); + // Loop over every element in 2D to count the *unique* non-zeros + for (int x = mesh->xstart; x <= mesh->xend; x++) { + for (int y = mesh->ystart; y <= mesh->yend; y++) { + + const int ind0 = ROUND(index(x, y, 0)) - Istart; + + // 2D fields + for (int i = 0; i < n2d; i++) { + const PetscInt row = ind0 + i; + // Loop through each point in the stencil + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) + || (yi >= mesh->LocalNy)) { + continue; + } + + const int ind2 = ROUND(index(xi, yi, 0)); + if (ind2 < 0) { + continue; // A boundary point + } + + // Depends on all variables on this cell + for (int j = 0; j < n2d; j++) { + const PetscInt col = ind2 + j; + if (col >= Istart && col < Iend) { + d_nnz_map2d[row].insert(col); + } else { + o_nnz_map2d[row].insert(col); + } + } + } + } + // 3D fields + for (int z = 0; z < mesh->LocalNz; z++) { + const int ind = ROUND(index(x, y, z)) - Istart; + + for (int i = 0; i < n3d; i++) { + PetscInt row = ind + i; + if (z == 0) { + row += n2d; + } + + // Depends on 2D fields + for (int j = 0; j < n2d; j++) { + const PetscInt col = ind0 + j; + if (col >= Istart && col < Iend) { + d_nnz_map2d[row].insert(col); + } else { + o_nnz_map2d[row].insert(col); + } + } + + // Star pattern + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) + || (yi >= mesh->LocalNy)) { + continue; + } + + int ind2 = ROUND(index(xi, yi, 0)); + if (ind2 < 0) { + continue; // Boundary point + } + + if (z == 0) { + ind2 += n2d; + } + + // 3D fields on this cell + for (int j = 0; j < n3d; j++) { + const PetscInt col = ind2 + j; + if (col >= Istart && col < Iend) { + d_nnz_map3d[row].insert(col); + } else { + o_nnz_map3d[row].insert(col); + } + } + } + } + } + } + } + + d_nnz.reserve(nlocal); + d_nnz.reserve(nlocal); + + for (int i = 0; i < nlocal; ++i) { + // Assume all elements in the z direction are potentially coupled + d_nnz.emplace_back(d_nnz_map3d[i].size() * mesh->LocalNz + d_nnz_map2d[i].size()); + o_nnz.emplace_back(o_nnz_map3d[i].size() * mesh->LocalNz + o_nnz_map2d[i].size()); + } + } + + output_progress.write("Pre-allocating Jacobian\n"); + // Pre-allocate + MatMPIAIJSetPreallocation(Jfd, 0, d_nnz.data(), 0, o_nnz.data()); + MatSeqAIJSetPreallocation(Jfd, 0, d_nnz.data()); + MatSetUp(Jfd); + MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE); + + ////////////////////////////////////////////////// + // Mark non-zero entries + + output_progress.write("Marking non-zero Jacobian entries\n"); + PetscScalar val = 1.0; + for (int x = mesh->xstart; x <= mesh->xend; x++) { + for (int y = mesh->ystart; y <= mesh->yend; y++) { + + const int ind0 = ROUND(index(x, y, 0)); + + // 2D fields + for (int i = 0; i < n2d; i++) { + const PetscInt row = ind0 + i; + + // Loop through each point in the stencil + for (const auto& [x_off, y_off] : xy_offsets) { + const int xi = x + x_off; + const int yi = y + y_off; + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) || (yi >= mesh->LocalNy)) { + continue; + } + + int ind2 = ROUND(index(xi, yi, 0)); + if (ind2 < 0) { + continue; // A boundary point + } + + // Depends on all variables on this cell + for (int j = 0; j < n2d; j++) { + PetscInt col = ind2 + j; + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); + } + } + } + // 3D fields + for (int z = 0; z < mesh->LocalNz; z++) { + int ind = ROUND(index(x, y, z)); + + for (int i = 0; i < n3d; i++) { + PetscInt row = ind + i; + if (z == 0) { + row += n2d; + } + + // Depends on 2D fields + for (int j = 0; j < n2d; j++) { + PetscInt col = ind0 + j; + PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); + } + + // Star pattern + for (const auto& [x_off, y_off] : xy_offsets) { + int xi = x + x_off; + int yi = y + y_off; + + if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) + || (yi >= mesh->LocalNy)) { + continue; + } + for (int zi = 0; zi < mesh->LocalNz; ++zi) { + int ind2 = ROUND(index(xi, yi, zi)); + if (ind2 < 0) { + continue; // Boundary point + } + + if (z == 0) { + ind2 += n2d; + } + + // 3D fields on this cell + for (int j = 0; j < n3d; j++) { + PetscInt col = ind2 + j; + PetscErrorCode ierr = + MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); + + if (ierr != PETSC_SUCCESS) { + output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", row, + x, y, xi, yi, ind2, ind2 + n3d - 1); + } + CHKERRQ(ierr); + } + } + } + } + } + } + } + + // Finished marking non-zero entries + + output_progress.write("Assembling Jacobian matrix\n"); + + // Assemble Matrix + MatAssemblyBegin(Jfd, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(Jfd, MAT_FINAL_ASSEMBLY); + + { + // Test if the matrix is symmetric + // Values are 0 or 1 so tolerance (1e-5) shouldn't matter + PetscBool symmetric; + PetscCall(MatIsSymmetric(Jfd, 1e-5, &symmetric)); + if (!symmetric) { + output_warn.write("Jacobian pattern is not symmetric\n"); + } + } + + // The above can miss entries around the X-point branch cut: + // The diagonal terms are complicated because moving in X then Y + // is different from moving in Y then X at the X-point. + // Making sure the colouring matrix is symmetric does not + // necessarily give the correct stencil but may help. + if ((*options)["force_symmetric_coloring"] + .doc("Modifies coloring matrix to force it to be symmetric") + .withDefault(false)) { + Mat Jfd_T; + MatCreateTranspose(Jfd, &Jfd_T); + MatAXPY(Jfd, 1, Jfd_T, DIFFERENT_NONZERO_PATTERN); + } + + output_progress.write("Creating Jacobian coloring\n"); + updateColoring(); + + if (prune_jacobian) { + // Will remove small elements from the Jacobian. + // Save a copy to recover from over-pruning + PetscCall(MatDuplicate(Jfd, MAT_SHARE_NONZERO_PATTERN, &Jfd_original)); + } + } else { + // Brute force calculation + // There is usually no reason to use this, except as a check of + // the coloring calculation. + + MatCreateAIJ( + BoutComm::get(), nlocal, nlocal, // Local sizes + PETSC_DETERMINE, PETSC_DETERMINE, // Global sizes + 3, // Number of nonzero entries in diagonal portion of local submatrix + nullptr, + 0, // Number of nonzeros per row in off-diagonal portion of local submatrix + nullptr, &Jfd); + + if (matrix_free_operator) { + SNESSetJacobian(snes, Jmf, Jfd, SNESComputeJacobianDefault, this); + } else { + SNESSetJacobian(snes, Jfd, Jfd, SNESComputeJacobianDefault, this); + } + + MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); + } +} + +PetscErrorCode SNESSolver::FDJpruneJacobian() { +#if PETSC_VERSION_GE(3, 20, 0) + + // Remove small elements from the Jacobian and recompute the coloring + // Only do this if there are a significant number of small elements. + int small_elements = 0; + int total_elements = 0; + + // Get index of rows owned by this processor + int rstart, rend; + MatGetOwnershipRange(Jfd, &rstart, &rend); + + PetscInt ncols; + const PetscScalar* vals; + for (int row = rstart; row < rend; row++) { + MatGetRow(Jfd, row, &ncols, nullptr, &vals); + for (int col = 0; col < ncols; col++) { + if (std::abs(vals[col]) < prune_abstol) { + ++small_elements; + } + ++total_elements; + } + MatRestoreRow(Jfd, row, &ncols, nullptr, &vals); + } + + if (small_elements > prune_fraction * total_elements) { + if (diagnose) { + output.write("\nPruning Jacobian elements: {} / {}\n", small_elements, + total_elements); + } + + // Prune Jacobian, keeping diagonal elements + PetscCall(MatFilter(Jfd, prune_abstol, PETSC_TRUE, PETSC_TRUE)); + + // Update the coloring from Jfd matrix + updateColoring(); + + // Mark the Jacobian as pruned. This is so that it is only restored if pruned. + jacobian_pruned = true; + } +#endif // PETSC_VERSION_GE(3,20,0) +} + +PetscErrorCode SNESSolver::FDJrestoreFromPruning() { + // Restore pruned non-zero elements + PetscCall(MatCopy(Jfd_original, Jfd, DIFFERENT_NONZERO_PATTERN)); + // The non-zero pattern has changed, so update coloring + updateColoring(); + jacobian_pruned = false; // Reset flag. Will be set after pruning. +} + SNESSolver::SNESSolver(Options* opts) : Solver(opts), timestep( @@ -338,321 +704,7 @@ int SNESSolver::init() { // because that would require updating the preconditioner. PetscCall(VecDuplicate(snes_x, &output_x)); - if (use_coloring) { - // Use matrix coloring. - // This greatly reduces the number of times the rhs() function - // needs to be evaluated when calculating the Jacobian. - - // Use global mesh for now - Mesh* mesh = bout::globals::mesh; - - ////////////////////////////////////////////////// - // Get the local indices by starting at 0 - Field3D index = globalIndex(0); - - ////////////////////////////////////////////////// - // Pre-allocate PETSc storage - - output_progress.write("Setting Jacobian matrix sizes\n"); - - const int n2d = f2d.size(); - const int n3d = f3d.size(); - - // Set size of Matrix on each processor to nlocal x nlocal - MatCreate(BoutComm::get(), &Jfd); - MatSetOption(Jfd, MAT_KEEP_NONZERO_PATTERN, PETSC_TRUE); - MatSetSizes(Jfd, nlocal, nlocal, PETSC_DETERMINE, PETSC_DETERMINE); - MatSetFromOptions(Jfd); - // Determine which row/columns of the matrix are locally owned - int Istart, Iend; - MatGetOwnershipRange(Jfd, &Istart, &Iend); - // Convert local into global indices - // Note: Not in the boundary cells, to keep -1 values - for (const auto& i : mesh->getRegion3D("RGN_NOBNDRY")) { - index[i] += Istart; - } - // Now communicate to fill guard cells - mesh->communicate(index); - - // Non-zero elements on this processor - std::vector d_nnz; - std::vector o_nnz; - auto n_square = (*options)["stencil:square"] - .doc("Extent of stencil (square)") - .withDefault(0); - auto n_cross = (*options)["stencil:cross"] - .doc("Extent of stencil (cross)") - .withDefault(0); - // Set n_taxi 2 if nothing else is set - auto n_taxi = (*options)["stencil:taxi"] - .doc("Extent of stencil (taxi-cab norm)") - .withDefault((n_square == 0 && n_cross == 0) ? 2 : 0); - - auto const xy_offsets = ColoringStencil::getOffsets(n_square, n_taxi, n_cross); - { - // This is ugly but can't think of a better and robust way to - // count the non-zeros for some arbitrary stencil - // effectively the same loop as the one that sets the non-zeros below - std::vector> d_nnz_map2d(nlocal); - std::vector> o_nnz_map2d(nlocal); - std::vector> d_nnz_map3d(nlocal); - std::vector> o_nnz_map3d(nlocal); - // Loop over every element in 2D to count the *unique* non-zeros - for (int x = mesh->xstart; x <= mesh->xend; x++) { - for (int y = mesh->ystart; y <= mesh->yend; y++) { - - const int ind0 = ROUND(index(x, y, 0)) - Istart; - - // 2D fields - for (int i = 0; i < n2d; i++) { - const PetscInt row = ind0 + i; - // Loop through each point in the stencil - for (const auto& [x_off, y_off] : xy_offsets) { - const int xi = x + x_off; - const int yi = y + y_off; - if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) - || (yi >= mesh->LocalNy)) { - continue; - } - - const int ind2 = ROUND(index(xi, yi, 0)); - if (ind2 < 0) { - continue; // A boundary point - } - - // Depends on all variables on this cell - for (int j = 0; j < n2d; j++) { - const PetscInt col = ind2 + j; - if (col >= Istart && col < Iend) { - d_nnz_map2d[row].insert(col); - } else { - o_nnz_map2d[row].insert(col); - } - } - } - } - // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { - const int ind = ROUND(index(x, y, z)) - Istart; - - for (int i = 0; i < n3d; i++) { - PetscInt row = ind + i; - if (z == 0) { - row += n2d; - } - - // Depends on 2D fields - for (int j = 0; j < n2d; j++) { - const PetscInt col = ind0 + j; - if (col >= Istart && col < Iend) { - d_nnz_map2d[row].insert(col); - } else { - o_nnz_map2d[row].insert(col); - } - } - - // Star pattern - for (const auto& [x_off, y_off] : xy_offsets) { - const int xi = x + x_off; - const int yi = y + y_off; - - if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) - || (yi >= mesh->LocalNy)) { - continue; - } - - int ind2 = ROUND(index(xi, yi, 0)); - if (ind2 < 0) { - continue; // Boundary point - } - - if (z == 0) { - ind2 += n2d; - } - - // 3D fields on this cell - for (int j = 0; j < n3d; j++) { - const PetscInt col = ind2 + j; - if (col >= Istart && col < Iend) { - d_nnz_map3d[row].insert(col); - } else { - o_nnz_map3d[row].insert(col); - } - } - } - } - } - } - } - - d_nnz.reserve(nlocal); - d_nnz.reserve(nlocal); - - for (int i = 0; i < nlocal; ++i) { - // Assume all elements in the z direction are potentially coupled - d_nnz.emplace_back(d_nnz_map3d[i].size() * mesh->LocalNz - + d_nnz_map2d[i].size()); - o_nnz.emplace_back(o_nnz_map3d[i].size() * mesh->LocalNz - + o_nnz_map2d[i].size()); - } - } - - output_progress.write("Pre-allocating Jacobian\n"); - // Pre-allocate - MatMPIAIJSetPreallocation(Jfd, 0, d_nnz.data(), 0, o_nnz.data()); - MatSeqAIJSetPreallocation(Jfd, 0, d_nnz.data()); - MatSetUp(Jfd); - MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_TRUE); - - ////////////////////////////////////////////////// - // Mark non-zero entries - - output_progress.write("Marking non-zero Jacobian entries\n"); - PetscScalar val = 1.0; - for (int x = mesh->xstart; x <= mesh->xend; x++) { - for (int y = mesh->ystart; y <= mesh->yend; y++) { - - const int ind0 = ROUND(index(x, y, 0)); - - // 2D fields - for (int i = 0; i < n2d; i++) { - const PetscInt row = ind0 + i; - - // Loop through each point in the stencil - for (const auto& [x_off, y_off] : xy_offsets) { - const int xi = x + x_off; - const int yi = y + y_off; - if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) - || (yi >= mesh->LocalNy)) { - continue; - } - - int ind2 = ROUND(index(xi, yi, 0)); - if (ind2 < 0) { - continue; // A boundary point - } - - // Depends on all variables on this cell - for (int j = 0; j < n2d; j++) { - PetscInt col = ind2 + j; - PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); - } - } - } - // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { - int ind = ROUND(index(x, y, z)); - - for (int i = 0; i < n3d; i++) { - PetscInt row = ind + i; - if (z == 0) { - row += n2d; - } - - // Depends on 2D fields - for (int j = 0; j < n2d; j++) { - PetscInt col = ind0 + j; - PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); - } - - // Star pattern - for (const auto& [x_off, y_off] : xy_offsets) { - int xi = x + x_off; - int yi = y + y_off; - - if ((xi < 0) || (yi < 0) || (xi >= mesh->LocalNx) - || (yi >= mesh->LocalNy)) { - continue; - } - for (int zi = 0; zi < mesh->LocalNz; ++zi) { - int ind2 = ROUND(index(xi, yi, zi)); - if (ind2 < 0) { - continue; // Boundary point - } - - if (z == 0) { - ind2 += n2d; - } - - // 3D fields on this cell - for (int j = 0; j < n3d; j++) { - PetscInt col = ind2 + j; - PetscErrorCode ierr = - MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); - - if (ierr != PETSC_SUCCESS) { - output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", - row, x, y, xi, yi, ind2, ind2 + n3d - 1); - } - CHKERRQ(ierr); - } - } - } - } - } - } - } - - // Finished marking non-zero entries - - output_progress.write("Assembling Jacobian matrix\n"); - - // Assemble Matrix - MatAssemblyBegin(Jfd, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(Jfd, MAT_FINAL_ASSEMBLY); - - { - // Test if the matrix is symmetric - // Values are 0 or 1 so tolerance (1e-5) shouldn't matter - PetscBool symmetric; - PetscCall(MatIsSymmetric(Jfd, 1e-5, &symmetric)); - if (!symmetric) { - output_warn.write("Jacobian pattern is not symmetric\n"); - } - } - - // The above can miss entries around the X-point branch cut: - // The diagonal terms are complicated because moving in X then Y - // is different from moving in Y then X at the X-point. - // Making sure the colouring matrix is symmetric does not - // necessarily give the correct stencil but may help. - if ((*options)["force_symmetric_coloring"] - .doc("Modifies coloring matrix to force it to be symmetric") - .withDefault(false)) { - Mat Jfd_T; - MatCreateTranspose(Jfd, &Jfd_T); - MatAXPY(Jfd, 1, Jfd_T, DIFFERENT_NONZERO_PATTERN); - } - - output_progress.write("Creating Jacobian coloring\n"); - updateColoring(); - - if (prune_jacobian) { - // Will remove small elements from the Jacobian. - // Save a copy to recover from over-pruning - PetscCall(MatDuplicate(Jfd, MAT_SHARE_NONZERO_PATTERN, &Jfd_original)); - } - } else { - // Brute force calculation - // There is usually no reason to use this, except as a check of - // the coloring calculation. - - MatCreateAIJ( - BoutComm::get(), nlocal, nlocal, // Local sizes - PETSC_DETERMINE, PETSC_DETERMINE, // Global sizes - 3, // Number of nonzero entries in diagonal portion of local submatrix - nullptr, - 0, // Number of nonzeros per row in off-diagonal portion of local submatrix - nullptr, &Jfd); - - if (matrix_free_operator) { - SNESSetJacobian(snes, Jmf, Jfd, SNESComputeJacobianDefault, this); - } else { - SNESSetJacobian(snes, Jfd, Jfd, SNESComputeJacobianDefault, this); - } - - MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); - } + FDJinitialise(); // Re-use Jacobian // Note: If the 'Amat' Jacobian is matrix free, SNESComputeJacobian @@ -954,14 +1006,10 @@ int SNESSolver::run() { if (jacobian_pruned and (snes_failures > 2) and (4 * lin_its > 3 * maxl)) { // Taking 3/4 of maximum linear iterations on average per linear step // May indicate a preconditioner problem. - // Restore pruned non-zero elements if (diagnose) { output.write("\nRestoring Jacobian\n"); } - PetscCall(MatCopy(Jfd_original, Jfd, DIFFERENT_NONZERO_PATTERN)); - // The non-zero pattern has changed, so update coloring - updateColoring(); - jacobian_pruned = false; // Reset flag. Will be set after pruning. + PetscCall(FDJrestoreFromPruning()); } if (saved_jacobian_lag == 0) { @@ -1045,50 +1093,12 @@ int SNESSolver::run() { output.write("\n"); } -#if PETSC_VERSION_GE(3, 20, 0) // MatFilter and MatEliminateZeros(Mat, bool) require PETSc >= 3.20 if (jacobian_recalculated and prune_jacobian) { jacobian_recalculated = false; // Reset flag - // Remove small elements from the Jacobian and recompute the coloring - // Only do this if there are a significant number of small elements. - int small_elements = 0; - int total_elements = 0; - - // Get index of rows owned by this processor - int rstart, rend; - MatGetOwnershipRange(Jfd, &rstart, &rend); - - PetscInt ncols; - const PetscScalar* vals; - for (int row = rstart; row < rend; row++) { - MatGetRow(Jfd, row, &ncols, nullptr, &vals); - for (int col = 0; col < ncols; col++) { - if (std::abs(vals[col]) < prune_abstol) { - ++small_elements; - } - ++total_elements; - } - MatRestoreRow(Jfd, row, &ncols, nullptr, &vals); - } - - if (small_elements > prune_fraction * total_elements) { - if (diagnose) { - output.write("\nPruning Jacobian elements: {} / {}\n", small_elements, - total_elements); - } - - // Prune Jacobian, keeping diagonal elements - PetscCall(MatFilter(Jfd, prune_abstol, PETSC_TRUE, PETSC_TRUE)); - - // Update the coloring from Jfd matrix - updateColoring(); - - // Mark the Jacobian as pruned. This is so that it is only restored if pruned. - jacobian_pruned = true; - } + FDJpruneJacobian(); } -#endif // PETSC_VERSION_GE(3,20,0) if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Adjust local timesteps diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 9086106ecd..98e96d3e4e 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -58,6 +58,13 @@ BOUT_ENUM_CLASS(BoutPTCStrategy, history_based, ///< Grow/shrink dt based on residual decrease/increase hybrid); ///< Combine inverse_residual and history_based strategies +// class FiniteDifferenceJacobian { +// public: +// PetscError initialise(); +// private: + +// }; + /// Uses PETSc's SNES interface to find a steady state solution to a /// nonlinear ODE by integrating in time with Backward Euler class SNESSolver : public Solver { @@ -100,6 +107,10 @@ public: void outputVars(Options& output_options, bool save_repeat = true) override; private: + PetscErrorCode FDJinitialise(); + PetscErrorCode FDJpruneJacobian(); + PetscErrorCode FDJrestoreFromPruning(); + /// Call the physics model RHS function /// /// @param[in] x The state vector. Will be scaled if scale_vars=true From 48c0b180f677183aaeb8ffa42e5a063b1a72c0b3 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Tue, 14 Oct 2025 11:03:06 -0700 Subject: [PATCH 051/461] snes: Expand manual and minor tidying Add manual sections on adaptive timestepping and PTC method. Minor tidying to address some Clang-Tidy comments. --- manual/sphinx/user_docs/time_integration.rst | 230 +++++++++++++++++-- src/solver/impls/snes/snes.cxx | 7 +- src/solver/impls/snes/snes.hxx | 8 +- 3 files changed, 214 insertions(+), 31 deletions(-) diff --git a/manual/sphinx/user_docs/time_integration.rst b/manual/sphinx/user_docs/time_integration.rst index 373b5cdbe6..7658602bc8 100644 --- a/manual/sphinx/user_docs/time_integration.rst +++ b/manual/sphinx/user_docs/time_integration.rst @@ -390,7 +390,7 @@ The SNES solver is configured through the ``[solver]`` section of the input file pc_type = ilu # Preconditioner: ilu, bjacobi, hypre, etc. Timestepping Modes ------------------- +~~~~~~~~~~~~~~~~~~ The solver supports several timestepping strategies controlled by ``equation_form``: @@ -425,6 +425,183 @@ The solver supports several timestepping strategies controlled by ``equation_for This uses the same form as rearranged_backward_euler, but the time step can be different for each cell. +Adaptive Timestepping +~~~~~~~~~~~~~~~~~~~~~ + +When ``equation_form = rearranged_backward_euler`` (default), the +solver uses global timestepping with adaptive timestep control based +on nonlinear iteration count. + +.. code-block:: ini + + [solver] + type = snes + equation_form = rearranged_backward_euler + + # Initial and maximum timesteps + timestep = 1.0 # Initial timestep + max_timestep = 1e10 # Upper limit on timestep + dt_min_reset = 1e-6 # Reset the solver when timestep < this + + # Timestep adaptation + lower_its = 3 # Increase dt if iterations < this + upper_its = 10 # Decrease dt if iterations > this + timestep_factor_on_lower_its = 1.4 # Growth factor + timestep_factor_on_upper_its = 0.9 # Reduction factor + timestep_factor_on_failure = 0.5 # Reduction on convergence failure + +PID Controller +^^^^^^^^^^^^^^ + +An alternative adaptive strategy using a PID controller: + +.. code-block:: ini + + [solver] + pid_controller = true + target_its = 7 # Target number of nonlinear iterations + kP = 0.7 # Proportional gain + kI = 0.3 # Integral gain + kD = 0.2 # Derivative gain + +The PID controller adjusts the timestep to maintain approximately ``target_its`` +nonlinear iterations per solve, providing smoother adaptation than threshold-based +methods. + +Pseudo-Transient Continuation and Switched Evolution Relaxation +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +When ``equation_form = pseudo_transient`` the solver uses +Pseudo-Transient Continuation (PTC). This is a robust numerical +technique for solving steady-state problems that are too nonlinear for +direct Newton iteration. Instead of solving the steady-state system +**F(u) = 0** directly, PTC solves a modified time-dependent problem: + +.. math:: + + M(u) \frac{\partial u}{\partial \tau} + F(u) = 0 + +where :math:`\tau` is a pseudo-time variable (not physical time) and :math:`M(u)` +is a preconditioning matrix. As :math:`\tau \to \infty`, the solution converges +to the steady state **F(u) = 0**. + +The key advantage of PTC is that it transforms a difficult root-finding problem +into a sequence of easier initial value problems. Poor initial guesses that would +cause Newton's method to diverge can still reach the solution via a stable +pseudo-transient path. + +The Switched Evolution Relaxation (SER) method is a spatially adaptive +variant of PTC that allows each cell to use a different +pseudo-timestep :math:`\Delta\tau_i`. The timestep in each cell adapts +based on the local residual, allowing the algorithm to take large +timesteps in well-behaved regions (fast convergence), while taking +small timesteps in difficult regions (stable advancement). The the +same :math:`\Delta\tau_i` is used for all equations (density, +momentum, energy etc.) within each cell. This maintains coupling +between temperature, pressure, and composition through the equation of +state. + +**Key parameters:** + +``pseudo_max_ratio`` (default: 2.0) + Maximum allowed ratio of timesteps between neighboring cells. This prevents + sharp spatial gradients in convergence rate. + +**Example PTC configuration:** + +.. code-block:: ini + + [solver] + type = snes + equation_form = pseudo_transient + + timestep = 1.0 # Initial timestep + + # SER parameters + pid_controller = true # Scale timesteps based on iterations + pseudo_max_ratio = 2.0 # Limit neighbor timestep ratio + + # Tolerances + atol = 1e-7 + rtol = 1e-6 + stol = 1e-12 + +SER timestep strategy +^^^^^^^^^^^^^^^^^^^^^ + +After each nonlinear solve the timesteps in each cell are adjusted. +The strategy used depends on the ``pseudo_strategy`` option: + +**inverse_residual** (default) + +If ``pseudo_strategy = inverse_residual`` then the timestep is inversely +proportional to the RMS residual in each cell. +``pseudo_alpha`` (default: 100 × atol × timestep) +Controls the relationship between residual and timestep. The local timestep +is computed as: + +.. math:: + + \Delta\tau_i = \frac{\alpha}{||R_i||} + +Larger values allow more aggressive timestepping. The default is to use +a fixed ``pseudo_alpha`` but a better strategy is to enable the PID controller +that adjusts this parameter based on the nonlinear solver convergence. + +The timestep is limited to be between ``dt_min_reset`` and +``max_timestep``. In addition the timestep is limited between 0.67 × +previous timestep and 1.5 × previous timestep, to limit sudden changes +in timestep. + +In practice this strategy seems to work well, though problems could +arise when residuals become very small. + +**history_based** + +When ``pseudo_strategy = history_based`` the history of residuals +within each cell is used to adjust the timestep. The key parameters +are: + +``pseudo_growth_factor`` (default: 1.1) + Factor by which timestep increases when residual decreases successfully. + +``pseudo_reduction_factor`` (default: 0.5) + Factor by which timestep decreases when residual increases (step rejected). + +This method may be less susceptible to fluctuations when residuals +become small, but tends to be slower to converge when residuals are +large. + +**hybrid** + +When ``pseudo_strategy = hybrid`` the ``inverse_residual`` and +``history_based`` strategies are combined: When the residuals are +large the ``inverse_residual`` method is used, and when residuals +become small the method switches to ``history_based``. + +PID Controller +^^^^^^^^^^^^^^ + +When using the PTC method the PID controller can be used to dynamically +adjust ``pseudo_alpha`` depending on the nonlinearity of the system: + +.. code-block:: ini + + [solver] + pid_controller = true + target_its = 7 # Target number of nonlinear iterations + kP = 0.7 # Proportional gain + kI = 0.3 # Integral gain + kD = 0.2 # Derivative gain + +The PID controller adjusts ``pseudo_alpha``, scaling all cell +timesteps together, to maintain approximately ``target_its`` nonlinear +iterations per solve. + +With this enabled the solver uses the number of nonlinear iterations +to scale timesteps globally, and residuals to scale timesteps locally. +Note that the PID controller has no effect on the ``history_based`` +strategy because that strategy does not use ``pseudo_alpha``. Jacobian Finite Difference with Coloring ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -435,7 +612,7 @@ The default and recommended approach for most problems: [solver] use_coloring = true # Enable (default) - lag_jacobian = 50 # Reuse Jacobian for this many iterations + lag_jacobian = 5 # Reuse Jacobian for this many iterations # Stencil shape (determines Jacobian sparsity pattern) stencil:taxi = 2 # Taxi-cab distance (default) @@ -449,7 +626,17 @@ Jacobian coloring stencil ^^^^^^^^^^^^^^^^^^^^^^^^^ The stencil used to create the Jacobian colouring can be varied, -depending on which numerical operators are in use. +depending on which numerical operators are in use. It is important to +note that the coloring won't work for every problem: It assumes that +each evolving quantity is coupled to all other evolving quantities on +the same grid cell, and on all the neighbouring grid cells. If the RHS +function includes Fourier transforms, or matrix inversions +(e.g. potential solves) then these will introduce longer-range +coupling and the Jacobian calculation will give spurious +results. Generally the method will then fail to converge. Two +solutions are to a) switch to matrix-free (``matrix_free=true``), +or b) solve the matrix inversion as a constraint. + ``solver:stencil:cross = N`` e.g. for N == 2 @@ -489,8 +676,26 @@ Setting ``solver:force_symmetric_coloring = true``, will make sure that the jacobian colouring matrix is symmetric. This will often include a few extra non-zeros that the stencil will miss otherwise +Diagnostics and Monitoring +--------------------------- + +.. code-block:: ini + + [solver] + diagnose = true # Print iteration info to screen + diagnose_failures = true # Detailed diagnostics on failures + +When ``equation_form = pseudo_transient``, the solver saves additional diagnostic fields: + +- ``snes_pseudo_residual``: Local residual in each cell +- ``snes_pseudo_timestep``: Local pseudo-timestep in each cell +- ``snes_pseudo_alpha``: Global timestep scaling +These can be visualized to understand convergence behavior and identify +problematic regions. +Summary of solver options +~~~~~~~~~~~~~~~~~~~~~~~~~ +---------------------------+---------------+----------------------------------------------------+ | Option | Default |Description | @@ -552,22 +757,11 @@ include a few extra non-zeros that the stencil will miss otherwise The predictor is linear extrapolation from the last two timesteps. It seems to be effective, but can be disabled by setting ``predictor = false``. - - The default `newtonls` SNES type can be very effective if combined with Jacobian coloring: The coloring enables the Jacobian to be calculated relatively efficiently; once a Jacobian matrix has been calculated, effective preconditioners can be used to speed up -convergence. It is important to note that the coloring assumes a star -stencil and so won't work for every problem: It assumes that each -evolving quantity is coupled to all other evolving quantities on the -same grid cell, and on all the neighbouring grid cells. If the RHS -function includes Fourier transforms, or matrix inversions -(e.g. potential solves) then these will introduce longer-range -coupling and the Jacobian calculation will give spurious -results. Generally the method will then fail to converge. Two -solutions are to a) switch to matrix-free (``matrix_free=true``), or b) -solve the matrix inversion as a constraint. +convergence. The `SNES type `_ @@ -587,12 +781,6 @@ Preconditioner types: Enable with command-line args ``-pc_type hypre -pc_hypre_type euclid -pc_hypre_euclid_levels k`` where ``k`` is the level (1-8 typically). -Pseudo-Transient Continuation (PTC) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - - - -Saves diagnostic ``Field3D`` output variables ``snes_pseudo_residual`` and ``snes_pseudo_timestep``. ODE integration diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 838da5fa5a..1207fcee1b 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -19,6 +19,7 @@ #include #include "petscerror.h" +#include "petscpc.h" #include "petscsnes.h" #include "petscsys.h" #include "petscvec.h" @@ -1435,11 +1436,11 @@ BoutReal SNESSolver::updatePseudoTimestep(BoutReal previous_timestep, // and then the method transitions to being history-based if (current_residual > 1000. * atol) { return updatePseudoTimestep_inverse_residual(previous_timestep, current_residual); - } else { - return updatePseudoTimestep_history_based(previous_timestep, previous_residual, - current_residual); } + return updatePseudoTimestep_history_based(previous_timestep, previous_residual, + current_residual); }; + throw BoutException("SNESSolver::updatePseudoTimestep invalid BoutPTCStrategy"); } PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 98e96d3e4e..c37cc0b37a 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -39,6 +39,7 @@ class SNESSolver; #include #include +#include #include #include @@ -58,13 +59,6 @@ BOUT_ENUM_CLASS(BoutPTCStrategy, history_based, ///< Grow/shrink dt based on residual decrease/increase hybrid); ///< Combine inverse_residual and history_based strategies -// class FiniteDifferenceJacobian { -// public: -// PetscError initialise(); -// private: - -// }; - /// Uses PETSc's SNES interface to find a steady state solution to a /// nonlinear ODE by integrating in time with Backward Euler class SNESSolver : public Solver { From ca04bc81b374cbdd5a91f2e0a7843b9322a2a1f6 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Tue, 14 Oct 2025 15:17:58 -0700 Subject: [PATCH 052/461] snes: Add missing returns, clang tidying --- src/solver/impls/snes/snes.cxx | 32 ++++++++++++++++++++------------ src/solver/impls/snes/snes.hxx | 6 +++--- 2 files changed, 23 insertions(+), 15 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 1207fcee1b..d349193ee4 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -14,14 +14,18 @@ #include #include +#include +#include #include #include #include "petscerror.h" +#include "petscmat.h" #include "petscpc.h" #include "petscsnes.h" #include "petscsys.h" +#include "petscsystypes.h" #include "petscvec.h" class ColoringStencil { @@ -270,7 +274,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { // Mark non-zero entries output_progress.write("Marking non-zero Jacobian entries\n"); - PetscScalar val = 1.0; + const PetscScalar val = 1.0; for (int x = mesh->xstart; x <= mesh->xend; x++) { for (int y = mesh->ystart; y <= mesh->yend; y++) { @@ -288,14 +292,14 @@ PetscErrorCode SNESSolver::FDJinitialise() { continue; } - int ind2 = ROUND(index(xi, yi, 0)); + const int ind2 = ROUND(index(xi, yi, 0)); if (ind2 < 0) { continue; // A boundary point } // Depends on all variables on this cell for (int j = 0; j < n2d; j++) { - PetscInt col = ind2 + j; + const PetscInt col = ind2 + j; PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); } } @@ -312,7 +316,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { // Depends on 2D fields for (int j = 0; j < n2d; j++) { - PetscInt col = ind0 + j; + const PetscInt col = ind0 + j; PetscCall(MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES)); } @@ -337,7 +341,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { // 3D fields on this cell for (int j = 0; j < n3d; j++) { - PetscInt col = ind2 + j; + const PetscInt col = ind2 + j; PetscErrorCode ierr = MatSetValues(Jfd, 1, &row, 1, &col, &val, INSERT_VALUES); @@ -414,6 +418,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { MatSetOption(Jfd, MAT_NEW_NONZERO_ALLOCATION_ERR, PETSC_FALSE); } + return PETSC_SUCCESS; } PetscErrorCode SNESSolver::FDJpruneJacobian() { @@ -457,6 +462,7 @@ PetscErrorCode SNESSolver::FDJpruneJacobian() { jacobian_pruned = true; } #endif // PETSC_VERSION_GE(3,20,0) + return PETSC_SUCCESS; } PetscErrorCode SNESSolver::FDJrestoreFromPruning() { @@ -465,6 +471,7 @@ PetscErrorCode SNESSolver::FDJrestoreFromPruning() { // The non-zero pattern has changed, so update coloring updateColoring(); jacobian_pruned = false; // Reset flag. Will be set after pruning. + return PETSC_SUCCESS; } SNESSolver::SNESSolver(Options* opts) @@ -705,7 +712,8 @@ int SNESSolver::init() { // because that would require updating the preconditioner. PetscCall(VecDuplicate(snes_x, &output_x)); - FDJinitialise(); + // Initialize the Finite Difference Jacobian + PetscCall(FDJinitialise()); // Re-use Jacobian // Note: If the 'Amat' Jacobian is matrix free, SNESComputeJacobian @@ -1286,7 +1294,7 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { residual = sqrt(residual / count); auto i3d = mesh->ind2Dto3D(i2d, jz); - BoutReal new_timestep = + const BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], pseudo_residual[i3d], residual); pseudo_residual[i3d] = residual; @@ -1303,7 +1311,7 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { // These loops don't check the boundary flags for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { // Field2D quantities evolved together - if (f2d.size() > 0) { + if (!f2d.empty()) { BoutReal residual = 0.0; for (std::size_t i = 0; i != f2d.size(); ++i) { residual += SQ(current_residual[idx + i]); @@ -1311,7 +1319,7 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { residual = sqrt(residual / f2d.size()); // Adjust timestep for these quantities - BoutReal new_timestep = + const BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], pseudo_residual_2d[i2d], residual); for (std::size_t i = 0; i != f2d.size(); ++i) { dt_data[idx++] = new_timestep; @@ -1320,7 +1328,7 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { } // Field3D quantities evolved together within a cell - if (f3d.size() > 0) { + if (!f3d.empty()) { for (int jz = 0; jz < mesh->LocalNz; jz++) { auto i3d = mesh->ind2Dto3D(i2d, jz); @@ -1530,7 +1538,7 @@ PetscErrorCode SNESSolver::snes_function(Vec x, Vec f, bool linear) { PetscCall(VecPointwiseMult(f, f, rhs_scaling_factors)); } - return 0; + return PETSC_SUCCESS; } /* @@ -1572,7 +1580,7 @@ PetscErrorCode SNESSolver::scaleJacobian(Mat Jac_new) { jacobian_recalculated = true; if (!scale_rhs) { - return 0; // Not scaling the RHS values + return PETSC_SUCCESS; // Not scaling the RHS values } // Get index of rows owned by this processor diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index c37cc0b37a..c988f2cc3f 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -101,9 +101,9 @@ public: void outputVars(Options& output_options, bool save_repeat = true) override; private: - PetscErrorCode FDJinitialise(); - PetscErrorCode FDJpruneJacobian(); - PetscErrorCode FDJrestoreFromPruning(); + PetscErrorCode FDJinitialise(); ///< Finite Difference Jacobian initialise + PetscErrorCode FDJpruneJacobian(); ///< Remove small elements from the Jacobian + PetscErrorCode FDJrestoreFromPruning(); ///< Restore Jacobian to original pattern /// Call the physics model RHS function /// From 9b1eb95f3d2dfb0f2a325fc938949ce33edc5362 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 15 Oct 2025 15:39:48 -0700 Subject: [PATCH 053/461] snes: pid_consider_failures and asinh_vars Experimental features, off by default 1. pid_consider_failures (bool, default: false) If enabled, this reduces the magnitude of timestep increases when recent solves have frequently failed. Makes the solver more cautious so may reduce failures but also may not increase timesteps as quickly. Net effect on performance is likely problem dependent. 2. asinh_vars (bool, default: false) If enabled, evolves asinh() of all variables. This might be a good idea when variable magnitudes vary widely, either due to normalisation or strong variations across the mesh. - Behaves like log() for large values - Close to linear for small values, so the solver doesn't over-resolve e.g low density regions - Retains sign, unlike log: asinh(-x) = -asinh(x) --- src/solver/impls/snes/snes.cxx | 121 ++++++++++++++++++++++++--------- src/solver/impls/snes/snes.hxx | 6 ++ 2 files changed, 96 insertions(+), 31 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index d349193ee4..4325a11250 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -540,6 +540,10 @@ SNESSolver::SNESSolver(Options* opts) kP((*options)["kP"].doc("Proportional PID parameter").withDefault(0.7)), kI((*options)["kI"].doc("Integral PID parameter").withDefault(0.3)), kD((*options)["kD"].doc("Derivative PID parameter").withDefault(0.2)), + pid_consider_failures( + (*options)["pid_consider_failures"] + .doc("Reduce timestep increases if recent solves have failed") + .withDefault(false)), diagnose( (*options)["diagnose"].doc("Print additional diagnostics").withDefault(false)), diagnose_failures((*options)["diagnose_failures"] @@ -594,6 +598,9 @@ SNESSolver::SNESSolver(Options* opts) .withDefault(false)), scale_vars((*options)["scale_vars"] .doc("Scale variables (Jacobian column scaling)?") + .withDefault(false)), + asinh_vars((*options)["asinh_vars"] + .doc("Apply asinh() to all variables?") .withDefault(false)) {} int SNESSolver::init() { @@ -655,6 +662,8 @@ int SNESSolver::init() { PetscCall(VecSet(var_scaling_factors, 1.0)); // Storage for scaled 'x' state vectors PetscCall(VecDuplicate(snes_x, &scaled_x)); + } else if (asinh_vars) { + PetscCall(VecDuplicate(snes_x, &scaled_x)); } if (equation_form == BoutSnesEquationForm::pseudo_transient) { @@ -824,18 +833,17 @@ int SNESSolver::run() { BoutReal* xdata = nullptr; PetscCall(VecGetArray(snes_x, &xdata)); save_vars(xdata); - PetscCall(VecRestoreArray(snes_x, &xdata)); - } - if (equation_form == BoutSnesEquationForm::pseudo_transient) { - // Calculate the initial residual in snes_f - run_rhs(simtime); - { - BoutReal* fdata = nullptr; - PetscCall(VecGetArray(snes_f, &fdata)); - save_derivs(fdata); - PetscCall(VecRestoreArray(snes_f, &fdata)); + if (asinh_vars) { + // Evolving asinh(vars) + PetscInt size; + PetscCall(VecGetLocalSize(snes_x, &size)); + for (PetscInt i = 0; i != size; ++i) { + xdata[i] = std::asinh(xdata[i] / asinh_scale); + } } + + PetscCall(VecRestoreArray(snes_x, &xdata)); } BoutReal target = simtime; @@ -847,6 +855,7 @@ int SNESSolver::run() { int steps_since_snes_failure = 0; int saved_jacobian_lag = 0; int loop_count = 0; + recent_failure_rate = 0.0; do { if (simtime >= target) break; // Could happen if step over multiple outputs @@ -973,9 +982,14 @@ int SNESSolver::run() { int lin_its; SNESGetLinearSolveIterations(snes, &lin_its); + // Rolling average of recent failures + recent_failure_rate *= 1. - inv_failure_window; + if ((ierr != PETSC_SUCCESS) or (reason < 0)) { // Diverged or SNES failed + recent_failure_rate += inv_failure_window; + if (diagnose_failures) { // Print diagnostics to help identify source of the problem @@ -1200,17 +1214,29 @@ int SNESSolver::run() { if (scale_vars) { // scaled_x <- output_x * var_scaling_factors PetscCall(VecPointwiseMult(scaled_x, output_x, var_scaling_factors)); - - const BoutReal* xdata = nullptr; - PetscCall(VecGetArrayRead(scaled_x, &xdata)); - load_vars(const_cast(xdata)); - PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + } else if (asinh_vars) { + PetscCall(VecCopy(output_x, scaled_x)); } else { - const BoutReal* xdata = nullptr; - PetscCall(VecGetArrayRead(output_x, &xdata)); - load_vars(const_cast(xdata)); - PetscCall(VecRestoreArrayRead(output_x, &xdata)); + scaled_x = output_x; + } + + if (asinh_vars) { + PetscInt size; + PetscCall(VecGetLocalSize(scaled_x, &size)); + + BoutReal* scaled_data = nullptr; + PetscCall(VecGetArray(scaled_x, &scaled_data)); + for (PetscInt i = 0; i != size; ++i) { + scaled_data[i] = asinh_scale * std::sinh(scaled_data[i]); + } + PetscCall(VecRestoreArray(scaled_x, &scaled_data)); } + + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &xdata)); + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + run_rhs(target); // Run RHS to calculate auxilliary variables if (call_monitors(target, s, getNumberOutputSteps()) != 0) { @@ -1456,20 +1482,30 @@ PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { if (scale_vars) { // scaled_x <- x * var_scaling_factors PetscCall(VecPointwiseMult(scaled_x, x, var_scaling_factors)); - - const BoutReal* xdata = nullptr; - PetscCall(VecGetArrayRead(scaled_x, &xdata)); - // const_cast needed due to load_vars API. Not writing to xdata. - load_vars(const_cast(xdata)); - PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + } else if (asinh_vars) { + PetscCall(VecCopy(x, scaled_x)); } else { - const BoutReal* xdata = nullptr; - PetscCall(VecGetArrayRead(x, &xdata)); - // const_cast needed due to load_vars API. Not writing to xdata. - load_vars(const_cast(xdata)); - PetscCall(VecRestoreArrayRead(x, &xdata)); + scaled_x = x; + } + + if (asinh_vars) { + PetscInt size; + PetscCall(VecGetLocalSize(scaled_x, &size)); + + BoutReal* scaled_data = nullptr; + PetscCall(VecGetArray(scaled_x, &scaled_data)); + for (PetscInt i = 0; i != size; ++i) { + scaled_data[i] = asinh_scale * std::sinh(scaled_data[i]); + } + PetscCall(VecRestoreArray(scaled_x, &scaled_data)); } + const BoutReal* xdata = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &xdata)); + // const_cast needed due to load_vars API. Not writing to xdata. + load_vars(const_cast(xdata)); + PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); + try { // Call RHS function run_rhs(simtime + dt, linear); @@ -1484,6 +1520,24 @@ PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { BoutReal* fdata = nullptr; PetscCall(VecGetArray(f, &fdata)); save_derivs(fdata); + + if (asinh_vars) { + // Modify time-derivatives for asinh(var) using chain rule + // Evolving u = asinh(var / scale) + // + // du/dt = dvar/dt * du/dvar + // + // du/var = 1 / sqrt(var^2 + scale^2) + PetscInt size; + PetscCall(VecGetLocalSize(f, &size)); + const BoutReal* scaled_data = nullptr; + PetscCall(VecGetArrayRead(scaled_x, &scaled_data)); + for (PetscInt i = 0; i != size; ++i) { + fdata[i] /= std::sqrt(SQ(scaled_data[i]) + SQ(asinh_scale)); + } + PetscCall(VecRestoreArrayRead(scaled_x, &scaled_data)); + } + PetscCall(VecRestoreArray(f, &fdata)); return PETSC_SUCCESS; } @@ -1713,7 +1767,12 @@ BoutReal SNESSolver::pid(BoutReal timestep, int nl_its, BoutReal max_dt) { kD); // clamp growth factor to avoid huge changes - const BoutReal fac = std::clamp(facP * facI * facD, 0.2, 5.0); + BoutReal fac = std::clamp(facP * facI * facD, 0.2, 5.0); + + if (pid_consider_failures && (fac > 1.0)) { + // Reduce aggressiveness if recent steps have failed often + fac = pow(fac, std::max(0.3, 1.0 - 2.0 * recent_failure_rate)); + } /* ---------- update timestep and history ---------- */ const BoutReal dt_new = std::min(timestep * fac, max_dt); diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index c988f2cc3f..7039954418 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -165,6 +165,9 @@ private: BoutReal kP; ///< (0.6 - 0.8) Proportional parameter (main response to current step) BoutReal kI; ///< (0.2 - 0.4) Integral parameter (smooths history of changes) BoutReal kD; ///< (0.1 - 0.3) Derivative (dampens oscillation - optional) + bool pid_consider_failures; ///< Reduce timestep increases if recent solves have failed + BoutReal recent_failure_rate; ///< Rolling average of recent failure rate + const BoutReal inv_failure_window = 0.1; ///< 1 / number of recent solves int nl_its_prev; int nl_its_prev2; @@ -222,6 +225,9 @@ private: bool scale_vars; ///< Scale individual variables? Vec var_scaling_factors; ///< Factors to multiply variables when passing to user Vec scaled_x; ///< The values passed to the user RHS + + bool asinh_vars; ///< Evolve asinh(vars) to compress magnitudes while preserving signs + const BoutReal asinh_scale = 1e-5; // Scale below which asinh response becomes ~linear }; #else From cab2e9535f8592bd8e1dc96276918070305ff2df Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 24 Oct 2025 09:57:11 +0200 Subject: [PATCH 054/461] set more env vars for container --- .docker/fedora/Dockerfile | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index 56445f8cea..e4a10a263c 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -22,7 +22,7 @@ RUN git clone $URL \ && git checkout $COMMIT \ && git submodule update --init --recursive - +ENV HOME=/home/boutuser WORKDIR /home/boutuser/BOUT-dev RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/opt/bout++/ \ @@ -36,4 +36,11 @@ RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/opt/bout++/ \ RUN make -C build -j 2 RUN sudo make -C build install +ENV PATH=/opt/bout++/bin:$PATH \ + LD_LIBRARY_PATH=/opt/bout++/lib:/opt/petsc/lib/:$LD_LIBRARY_PATH \ + PYTHONPATH=/opt/bout++/lib/python3.13/site-packages/:$PYTHONPATH + +# Debug RUN find /opt/bout++ +# smoke test +RUN python3 -c 'import boutpp' From 6d856001e0c34ee19cfd2451ff0afd1e7c294dae Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 24 Oct 2025 09:57:37 +0200 Subject: [PATCH 055/461] CI: Build container for test branch --- .github/workflows/docker.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1380d0ea8e..39c9891338 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -6,7 +6,7 @@ on: - master - next # Add your branch here if you want containers for it - - db-WIP + - fix3121 - docker-ci env: From 802f8a09b8cb9cc7c6dfacc0688e72eb26b87489 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 24 Oct 2025 10:08:32 +0200 Subject: [PATCH 056/461] Build in one step --- .docker/fedora/Dockerfile | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index e4a10a263c..e152103255 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -25,22 +25,19 @@ RUN git clone $URL \ ENV HOME=/home/boutuser WORKDIR /home/boutuser/BOUT-dev -RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/opt/bout++/ \ +RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ -DBOUT_GENERATE_FIELDOPS=OFF \ - -DBOUT_USE_PETSC=ON -DPETSc_ROOT=/opt/petsc \ + -DBOUT_USE_PETSC=ON -DPETSc_ROOT=/usr/local \ -DBOUT_ENABLE_PYTHON=ON \ -DBOUT_USE_SUNDIALS=ON -DSUNDIALS_ROOT=/usr/lib64/$MPI/ -DSUNDIALS_INCLUDE_DIR=/usr/include/$MPI-x86_64/sundials/ \ - $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1) + $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1) \ + make -C build -j 2 \ + sudo make -C build install \ + rm -rf build +ENV PATH=/usr/local/bin:$PATH \ + LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH \ + PYTHONPATH=/usr/local/lib/python3.13/site-packages/:$PYTHONPATH -RUN make -C build -j 2 -RUN sudo make -C build install - -ENV PATH=/opt/bout++/bin:$PATH \ - LD_LIBRARY_PATH=/opt/bout++/lib:/opt/petsc/lib/:$LD_LIBRARY_PATH \ - PYTHONPATH=/opt/bout++/lib/python3.13/site-packages/:$PYTHONPATH - -# Debug -RUN find /opt/bout++ # smoke test RUN python3 -c 'import boutpp' From 9542428ddd0ba6130262a145e01337f95598b339 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 24 Oct 2025 10:13:01 +0200 Subject: [PATCH 057/461] Fixup: add ; at end of line --- .docker/fedora/Dockerfile | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index e152103255..2b6800ce21 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -30,9 +30,9 @@ RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ -DBOUT_USE_PETSC=ON -DPETSc_ROOT=/usr/local \ -DBOUT_ENABLE_PYTHON=ON \ -DBOUT_USE_SUNDIALS=ON -DSUNDIALS_ROOT=/usr/lib64/$MPI/ -DSUNDIALS_INCLUDE_DIR=/usr/include/$MPI-x86_64/sundials/ \ - $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1) \ - make -C build -j 2 \ - sudo make -C build install \ + $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1); \ + make -C build -j 2; \ + sudo make -C build install; \ rm -rf build ENV PATH=/usr/local/bin:$PATH \ From 9d63267793d31a12677d34f5d9384801734a17c6 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 31 Oct 2025 23:48:34 -0700 Subject: [PATCH 058/461] snes: Add max_snes_failures option Abort if too many consecutive failures. Prevents getting stuck in an infinite loop. --- src/solver/impls/snes/snes.cxx | 32 ++++++++++++++++++++++++-------- src/solver/impls/snes/snes.hxx | 2 ++ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 6a06bf21f5..b8bf8c3e58 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -508,6 +508,9 @@ SNESSolver::SNESSolver(Options* opts) upper_its((*options)["upper_its"] .doc("Iterations above which the next timestep is reduced") .withDefault(static_cast(maxits * 0.8))), + max_snes_failures((*options)["max_snes_failures"] + .doc("Abort after this number of consecutive failures") + .withDefault(10)), timestep_factor_on_failure((*options)["timestep_factor_on_failure"] .doc("Multiply timestep on convergence failure") .withDefault(0.5)), @@ -990,7 +993,10 @@ int SNESSolver::run() { recent_failure_rate += inv_failure_window; - if (diagnose_failures) { + ++snes_failures; + steps_since_snes_failure = 0; + + if (diagnose_failures or (snes_failures == max_snes_failures)) { // Print diagnostics to help identify source of the problem output.write("\n======== SNES failed =========\n"); @@ -1009,15 +1015,25 @@ int SNESSolver::run() { } } - ++snes_failures; - steps_since_snes_failure = 0; + if (snes_failures == max_snes_failures) { + output.write("Too many SNES failures ({}). Aborting."); + return 1; + } if (equation_form == BoutSnesEquationForm::pseudo_transient) { - // Global scaling of timesteps - // Note: A better strategy might be to reduce timesteps - // in problematic cells. - PetscCall(VecScale(dt_vec, timestep_factor_on_failure)); - + if (snes_failures == max_snes_failures - 1) { + // Last chance. Set to uniform smallest timestep + PetscCall(VecSet(dt_vec, dt_min_reset)); + + } else if (snes_failures == 5) { + // Set uniform timestep + PetscCall(VecSet(dt_vec, timestep)); + } else { + // Global scaling of timesteps + // Note: A better strategy might be to reduce timesteps + // in problematic cells. + PetscCall(VecScale(dt_vec, timestep_factor_on_failure)); + } } else { // Try a smaller timestep timestep *= timestep_factor_on_failure; diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 7039954418..5d007f8ad7 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -129,6 +129,8 @@ private: int maxits; ///< Maximum nonlinear iterations int lower_its, upper_its; ///< Limits on iterations for timestep adjustment + int max_snes_failures; ///< Maximum number of consecutive SNES failures before abort. + BoutReal timestep_factor_on_failure; BoutReal timestep_factor_on_upper_its; BoutReal timestep_factor_on_lower_its; From bb08c43619cdd81f022e78a28a4cd3ed1b22b057 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 17 Nov 2025 19:14:11 +0000 Subject: [PATCH 059/461] Bump externalpackages/googletest from `244cec8` to `1b96fa1` Bumps [externalpackages/googletest](https://github.com/google/googletest) from `244cec8` to `1b96fa1`. - [Release notes](https://github.com/google/googletest/releases) - [Commits](https://github.com/google/googletest/compare/244cec869d12e53378fa0efb610cd4c32a454ec8...1b96fa13f549387b7549cc89e1a785cf143a1a50) --- updated-dependencies: - dependency-name: externalpackages/googletest dependency-version: 1b96fa13f549387b7549cc89e1a785cf143a1a50 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- externalpackages/googletest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externalpackages/googletest b/externalpackages/googletest index 244cec869d..1b96fa13f5 160000 --- a/externalpackages/googletest +++ b/externalpackages/googletest @@ -1 +1 @@ -Subproject commit 244cec869d12e53378fa0efb610cd4c32a454ec8 +Subproject commit 1b96fa13f549387b7549cc89e1a785cf143a1a50 From d1c137f01f0f53cdefcdbb9789c43ef384da30ab Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 2 Jul 2024 15:38:01 +0200 Subject: [PATCH 060/461] Add option to disable tracking --- include/bout/field3d.hxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 8866f17b49..ac2baf56ba 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -302,6 +302,13 @@ public: return *this; } + /// Disable tracking + Field3D& disableTracking() { + tracking = nullptr; + tracking_state = 0; + return *this; + } + ///////////////////////////////////////////////////////// // Data access From ee8a9d6b5ac711a78507c292757f14e57c69b0ef Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Tue, 18 Nov 2025 15:01:45 +0000 Subject: [PATCH 061/461] Apply clang-format changes --- include/bout/invertable_operator.hxx | 4 ++-- src/bout++.cxx | 2 +- src/field/field3d.cxx | 4 ++-- src/solver/impls/arkode/arkode.cxx | 6 ++---- src/solver/impls/snes/snes.hxx | 14 +++++++------- 5 files changed, 14 insertions(+), 16 deletions(-) diff --git a/include/bout/invertable_operator.hxx b/include/bout/invertable_operator.hxx index fe139986be..99f7d9b675 100644 --- a/include/bout/invertable_operator.hxx +++ b/include/bout/invertable_operator.hxx @@ -575,7 +575,7 @@ public: }; #endif // PETSC -}; // namespace inversion -}; // namespace bout +}; // namespace inversion +}; // namespace bout #endif // HEADER GUARD diff --git a/src/bout++.cxx b/src/bout++.cxx index 7f23cf5f91..4e9e37fe8d 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -84,7 +84,7 @@ const char DEFAULT_DIR[] = "data"; // Define S_ISDIR if not defined by system headers (that is, MSVC) // Taken from https://github.com/curl/curl/blob/e59540139a398dc70fde6aec487b19c5085105af/lib/curl_setup.h#L748-L751 #if !defined(S_ISDIR) && defined(S_IFMT) && defined(S_IFDIR) -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #ifdef _MSC_VER diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 1eeb0dd344..17571ea4c1 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -869,9 +869,9 @@ void Field3D::_track(const T& change, std::string operation) { const std::string changename = change.name; #endif (*locked)[outname].setAttributes({ - {"operation", operation}, + {"operation", operation}, #if BOUT_USE_TRACK - {"rhs.name", changename}, + {"rhs.name", changename}, #endif }); } diff --git a/src/solver/impls/arkode/arkode.cxx b/src/solver/impls/arkode/arkode.cxx index 23883cc043..02f2cb7b81 100644 --- a/src/solver/impls/arkode/arkode.cxx +++ b/src/solver/impls/arkode/arkode.cxx @@ -229,8 +229,7 @@ int ArkodeSolver::init() { throw BoutException("ARKodeSetUserData failed\n"); } - if (ARKodeSetLinear(arkode_mem, static_cast(set_linear)) - != ARK_SUCCESS) { + if (ARKodeSetLinear(arkode_mem, static_cast(set_linear)) != ARK_SUCCESS) { throw BoutException("ARKodeSetLinear failed\n"); } @@ -415,8 +414,7 @@ int ArkodeSolver::init() { if (hasPreconditioner()) { output.write("\tUsing user-supplied preconditioner\n"); - if (ARKodeSetPreconditioner(arkode_mem, nullptr, arkode_pre) - != ARKLS_SUCCESS) { + if (ARKodeSetPreconditioner(arkode_mem, nullptr, arkode_pre) != ARKLS_SUCCESS) { throw BoutException("ARKodeSetPreconditioner failed\n"); } } else { diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 17050ad775..f695ba3718 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -121,9 +121,9 @@ private: Vec x1; ///< Previous solution BoutReal time1{-1.0}; ///< Time of previous solution - SNES snes; ///< SNES context - Mat Jmf; ///< Matrix Free Jacobian - Mat Jfd; ///< Finite Difference Jacobian + SNES snes; ///< SNES context + Mat Jmf; ///< Matrix Free Jacobian + Mat Jfd; ///< Finite Difference Jacobian MatFDColoring fdcoloring{nullptr}; ///< Matrix coloring context ///< Jacobian evaluation @@ -135,10 +135,10 @@ private: std::string pc_hypre_type; ///< Hypre preconditioner type std::string line_search_type; ///< Line search type - bool matrix_free; ///< Use matrix free Jacobian - bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? - int lag_jacobian; ///< Re-use Jacobian - bool use_coloring; ///< Use matrix coloring + bool matrix_free; ///< Use matrix free Jacobian + bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? + int lag_jacobian; ///< Re-use Jacobian + bool use_coloring; ///< Use matrix coloring bool jacobian_recalculated; ///< Flag set when Jacobian is recalculated bool prune_jacobian; ///< Remove small elements in the Jacobian? From e684304083bc5594f85bea2fed5a03ab42987a53 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 20 Nov 2025 14:19:39 +0100 Subject: [PATCH 062/461] Forward f to avoid copy --- include/bout/field.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index fb74dcb93d..9e576f938f 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -690,7 +690,7 @@ inline T setName(T&& f, const std::string& name, Types... args) { #if BOUT_USE_TRACK f.name = fmt::format(name, args...); #endif - return f; + return std::forward(f); } #endif /* FIELD_H */ From b75d020bc10ca6b6dc86418118a575a8d70a2b5f Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 24 Nov 2025 10:58:50 +0100 Subject: [PATCH 063/461] Add missing header from clang-tidy --- include/bout/field.hxx | 1 + include/bout/field3d.hxx | 4 ++++ src/field/field3d.cxx | 4 ++++ src/field/gen_fieldops.py | 15 +++++++++------ src/field/generated_fieldops.cxx | 15 +++++++++------ src/mesh/coordinates.cxx | 1 + src/solver/impls/euler/euler.cxx | 6 ++++++ src/solver/impls/pvode/pvode.cxx | 5 +++++ 8 files changed, 39 insertions(+), 12 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 9e576f938f..2d813bb275 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -36,6 +36,7 @@ class Field; #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/field_data.hxx" #include "bout/msg_stack.hxx" #include "bout/region.hxx" diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 039c085cfd..4dff626d7b 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -33,11 +33,15 @@ class Field3D; #include "bout/field2d.hxx" #include "bout/fieldperp.hxx" #include "bout/region.hxx" +#include "bout/traits.hxx" +#include #include +#include #include class Mesh; +class Options; /// Class for 3D X-Y-Z scalar fields /*! diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index cf7c339e39..3b4765e063 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -31,6 +31,8 @@ #include #include +#include +#include #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" @@ -47,6 +49,8 @@ #include #include +#include "fmt/format.h" + /// Constructor Field3D::Field3D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in) : Field(localmesh, location_in, directions_in) { diff --git a/src/field/gen_fieldops.py b/src/field/gen_fieldops.py index 29631ff7aa..b6f99549d2 100755 --- a/src/field/gen_fieldops.py +++ b/src/field/gen_fieldops.py @@ -65,12 +65,15 @@ def smart_open(filename, mode="r"): ) header = """// This file is autogenerated - see gen_fieldops.py -#include -#include -#include -#include -#include -#include +#include "bout/build_defines.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/globals.hxx" +#include "bout/interpolation.hxx" +#include "bout/mesh.hxx" +#include "bout/region.hxx" + +#include "fmt/format.h" """ diff --git a/src/field/generated_fieldops.cxx b/src/field/generated_fieldops.cxx index 3495d87dbc..f37a870c74 100644 --- a/src/field/generated_fieldops.cxx +++ b/src/field/generated_fieldops.cxx @@ -1,10 +1,13 @@ // This file is autogenerated - see gen_fieldops.py -#include -#include -#include -#include -#include -#include +#include "bout/build_defines.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/globals.hxx" +#include "bout/interpolation.hxx" +#include "bout/mesh.hxx" +#include "bout/region.hxx" + +#include "fmt/format.h" // Provide the C++ wrapper for multiplication of Field3D and Field3D Field3D operator*(const Field3D& lhs, const Field3D& rhs) { diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 3f67a5dc72..e805df00f2 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 110331eeb1..7d81b979a2 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -4,11 +4,17 @@ #include "bout/version.hxx" #include #include +#include "bout/field.hxx" +#include #include #include +#include #include +#include + #include +#include #include diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 4815f0153b..339508c385 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -31,6 +31,7 @@ #include #include +#include #include #include #include @@ -42,7 +43,11 @@ #include // contains the enum for types of preconditioning #include // band preconditioner function prototypes +#include "fmt/format.h" + #include +#include +#include using namespace pvode; From b824cfc15e4312d2eb10edee56144b0492e6b45f Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 24 Nov 2025 10:59:47 +0100 Subject: [PATCH 064/461] Apply more clang-tidy suggestions --- include/bout/field3d.hxx | 2 +- src/field/field3d.cxx | 2 +- src/solver/impls/pvode/pvode.cxx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 4dff626d7b..537613eda8 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -540,7 +540,7 @@ private: // non-changing copy that is used for the variable names in the dump files std::string selfname; template - inline void track(const T& change, const std::string& operation) { + void track(const T& change, const std::string& operation) { if (tracking_state != 0) { _track(change, operation); } diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 3b4765e063..7ece06a454 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -875,7 +875,7 @@ void Field3D::setRegion(const std::string& region_name) { Field3D& Field3D::enableTracking(const std::string& name, std::weak_ptr _tracking) { - tracking = _tracking; + tracking = std::move(_tracking); tracking_state = 1; selfname = name; return *this; diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 339508c385..a7b5f2e242 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -364,7 +364,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { if (flag != SUCCESS) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); if (debug_on_failure) { - CVodeMemRec* cv_mem = (CVodeMem)cvode_mem; + CVodeMemRec* cv_mem = static_cast(cvode_mem); if (!(f2d.empty() and v2d.empty() and v3d.empty())) { output_warn.write("debug_on_failure is currently only supported for Field3Ds"); return -1.0; From 90815fa149704ae97124880df0f1b15e840619c6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 24 Nov 2025 11:04:14 +0100 Subject: [PATCH 065/461] Add more headers explicitly --- src/solver/impls/pvode/pvode.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index a7b5f2e242..601cfc2448 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,7 @@ #include #include +#include #include // use CVSPGMR linear solver each internal step #include // contains the enum for types of preconditioning #include // band preconditioner function prototypes @@ -47,6 +49,7 @@ #include #include +#include #include using namespace pvode; From 69457b855d00aadec2463b77853cc8f42eef17a6 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:08:34 +0000 Subject: [PATCH 066/461] Apply clang-format changes --- src/solver/impls/euler/euler.cxx | 2 +- src/solver/impls/pvode/pvode.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 7d81b979a2..a4804590dc 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -1,10 +1,10 @@ #include "euler.hxx" +#include "bout/field.hxx" #include "bout/version.hxx" #include #include -#include "bout/field.hxx" #include #include #include diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 601cfc2448..f25c5bd9f2 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -47,10 +47,10 @@ #include "fmt/format.h" -#include +#include #include +#include #include -#include using namespace pvode; From b265c907dcae25dd0f326e82fcd805328328674e Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 24 Nov 2025 11:12:30 +0100 Subject: [PATCH 067/461] Remove duplicate definition --- include/bout/field3d.hxx | 7 ------- 1 file changed, 7 deletions(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 537613eda8..fad4815b92 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -306,13 +306,6 @@ public: return *this; } - /// Disable tracking - Field3D& disableTracking() { - tracking = nullptr; - tracking_state = 0; - return *this; - } - ///////////////////////////////////////////////////////// // Data access From 9c47bb8ae02c23d7027c210979cbb16ed3ae76a5 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 24 Nov 2025 11:16:24 +0100 Subject: [PATCH 068/461] Add more headers explicitly --- src/solver/impls/pvode/pvode.cxx | 1 + src/sys/options.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index f25c5bd9f2..6bd218e86b 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -36,6 +36,7 @@ #include #include #include +#include #include #include #include diff --git a/src/sys/options.cxx b/src/sys/options.cxx index b3238bf758..ada63bd526 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -22,6 +22,7 @@ #include #include +#include #include #include #include From 02cc7d17e806a0617069987c426f9bbdc2f4f9fd Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Mon, 24 Nov 2025 10:17:09 +0000 Subject: [PATCH 069/461] Apply clang-format changes --- src/solver/impls/pvode/pvode.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 6bd218e86b..2b83d4f155 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -35,8 +35,8 @@ #include #include #include -#include #include +#include #include #include #include From de2316741425c60fb0eefea1137a636ffe80410e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:28:45 +0000 Subject: [PATCH 070/461] Bump ZedThree/clang-tidy-review from 0.21.0 to 0.22.2 Bumps [ZedThree/clang-tidy-review](https://github.com/zedthree/clang-tidy-review) from 0.21.0 to 0.22.2. - [Release notes](https://github.com/zedthree/clang-tidy-review/releases) - [Changelog](https://github.com/ZedThree/clang-tidy-review/blob/master/CHANGELOG.md) - [Commits](https://github.com/zedthree/clang-tidy-review/compare/v0.21.0...v0.22.2) --- updated-dependencies: - dependency-name: ZedThree/clang-tidy-review dependency-version: 0.22.2 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/clang-tidy-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 3f20db608f..3661be9627 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -21,7 +21,7 @@ jobs: submodules: true - name: Run clang-tidy - uses: ZedThree/clang-tidy-review@v0.21.0 + uses: ZedThree/clang-tidy-review@v0.22.2 id: review with: build_dir: build @@ -46,4 +46,4 @@ jobs: -DBOUT_UPDATE_GIT_SUBMODULE=OFF - name: Upload clang-tidy fixes - uses: ZedThree/clang-tidy-review/upload@v0.21.0 + uses: ZedThree/clang-tidy-review/upload@v0.22.2 From 85a0367eef674eb8492d8b7ac52e5ac255ebee95 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 24 Nov 2025 19:28:52 +0000 Subject: [PATCH 071/461] Bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/black-fix.yml | 2 +- .github/workflows/clang-format.yml | 2 +- .github/workflows/clang-tidy-review.yml | 2 +- .github/workflows/docker.yml | 2 +- .github/workflows/python-package.yml | 6 +++--- .github/workflows/tests.yml | 6 +++--- 6 files changed, 10 insertions(+), 10 deletions(-) diff --git a/.github/workflows/black-fix.yml b/.github/workflows/black-fix.yml index 5cddbbfac6..4306ee8b43 100644 --- a/.github/workflows/black-fix.yml +++ b/.github/workflows/black-fix.yml @@ -20,7 +20,7 @@ jobs: runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: ref: ${{ github.head_ref }} diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml index 4c1307d7c9..678960ba3e 100644 --- a/.github/workflows/clang-format.yml +++ b/.github/workflows/clang-format.yml @@ -13,7 +13,7 @@ jobs: runs-on: ubuntu-latest steps: # Checkout the pull request branch, also include all history - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: ref: ${{ github.head_ref }} fetch-depth: 0 diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 3f20db608f..f0f3c73dac 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -16,7 +16,7 @@ jobs: if: ${{ !endsWith(github.head_ref, '-rc') }} runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: true diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 1380d0ea8e..0b510c48e8 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -52,7 +52,7 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Log in to the Container registry uses: docker/login-action@master diff --git a/.github/workflows/python-package.yml b/.github/workflows/python-package.yml index fc5e84662c..915265e850 100644 --- a/.github/workflows/python-package.yml +++ b/.github/workflows/python-package.yml @@ -17,7 +17,7 @@ jobs: if: always() steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 submodules: true @@ -57,7 +57,7 @@ jobs: if: always() steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 submodules: true @@ -106,7 +106,7 @@ jobs: if: always() steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: fetch-depth: 0 submodules: true diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 314ee196ae..f2c3618fab 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -168,7 +168,7 @@ jobs: libparpack2-dev libhypre-dev - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: true @@ -205,7 +205,7 @@ jobs: timeout-minutes: 120 runs-on: ubuntu-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: true - name: Build Fedora @@ -219,7 +219,7 @@ jobs: container: ghcr.io/ggeorgakoudis/boutdev-cuda:latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: submodules: true - name: Build minimal CUDA 12.2 @ GCC9.4.0 @ Ubuntu 20.04 From 6703d0ee10aa820cb0bd2f77c18557596033d282 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 1 Dec 2025 14:21:42 +0000 Subject: [PATCH 072/461] Remove explicit use of `MPI_COMM_WORLD` Fixes #3215 --- src/invert/laplace/impls/cyclic/cyclic_laplace.cxx | 14 ++++++++------ src/invert/laplace/impls/pcr/pcr.cxx | 8 ++++---- src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx | 8 ++++---- src/mesh/interpolation/hermite_spline_xz.cxx | 10 ++-------- 4 files changed, 18 insertions(+), 22 deletions(-) diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index 5ce4e540b7..837ac3755f 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -38,8 +38,10 @@ #if not BOUT_USE_METRIC_3D #include "cyclic_laplace.hxx" -#include "bout/assert.hxx" -#include "bout/bout_types.hxx" + +#include +#include +#include #include #include #include @@ -591,21 +593,21 @@ void LaplaceCyclic ::verify_solution(const Matrix& a_ver, } if (xproc > 0) { - MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, MPI_COMM_WORLD, + MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, BoutComm::get(), &request[1]); for (int kz = 0; kz < nsys; kz++) { sbufdown[kz] = x_ver(kz, 1); } - MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, MPI_COMM_WORLD, + MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, BoutComm::get(), &request[0]); } if (xproc < nprocs - 1) { - MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, MPI_COMM_WORLD, + MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, BoutComm::get(), &request[3]); for (int kz = 0; kz < nsys; kz++) { sbufup[kz] = x_ver(kz, nx); } - MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, MPI_COMM_WORLD, + MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, BoutComm::get(), &request[2]); } diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index 48bbdbac4b..b619d80100 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -1041,21 +1041,21 @@ void LaplacePCR ::verify_solution(const Matrix& a_ver, } if (xproc > 0) { - MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, MPI_COMM_WORLD, + MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, BoutComm::get(), &request[1]); for (int kz = 0; kz < nsys; kz++) { sbufdown[kz] = x_ver(kz, 1); } - MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, MPI_COMM_WORLD, + MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, BoutComm::get(), &request[0]); } if (xproc < nprocs - 1) { - MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, MPI_COMM_WORLD, + MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, BoutComm::get(), &request[3]); for (int kz = 0; kz < nsys; kz++) { sbufup[kz] = x_ver(kz, nx); } - MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, MPI_COMM_WORLD, + MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, BoutComm::get(), &request[2]); } diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index 61c8f58694..b48b20f246 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -1140,21 +1140,21 @@ void LaplacePCR_THOMAS ::verify_solution(const Matrix& a_ver, } if (xproc > 0) { - MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, MPI_COMM_WORLD, + MPI_Irecv(&rbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 901, BoutComm::get(), &request[1]); for (int kz = 0; kz < nsys; kz++) { sbufdown[kz] = x_ver(kz, 1); } - MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, MPI_COMM_WORLD, + MPI_Isend(&sbufdown[0], nsys, MPI_DOUBLE_COMPLEX, myrank - 1, 900, BoutComm::get(), &request[0]); } if (xproc < nprocs - 1) { - MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, MPI_COMM_WORLD, + MPI_Irecv(&rbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 900, BoutComm::get(), &request[3]); for (int kz = 0; kz < nsys; kz++) { sbufup[kz] = x_ver(kz, nx); } - MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, MPI_COMM_WORLD, + MPI_Isend(&sbufup[0], nsys, MPI_DOUBLE_COMPLEX, myrank + 1, 901, BoutComm::get(), &request[2]); } diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index c0040d096e..5064c7d4a1 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -21,6 +21,7 @@ **************************************************************************/ #include "../impls/bout/boutmesh.hxx" +#include "bout/bout.hxx" #include "bout/globals.hxx" #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" @@ -133,16 +134,9 @@ XZHermiteSpline::XZHermiteSpline(int y_offset, Mesh* mesh) #ifdef HS_USE_PETSC petsclib = new PetscLib( &Options::root()["mesh:paralleltransform:xzinterpolation:hermitespline"]); - // MatCreate(MPI_Comm comm,Mat *A) - // MatCreate(MPI_COMM_WORLD, &petscWeights); - // MatSetSizes(petscWeights, m, m, M, M); - // PetscErrorCode MatCreateAIJ(MPI_Comm comm, PetscInt m, PetscInt n, PetscInt M, - // PetscInt N, PetscInt d_nz, const PetscInt d_nnz[], - // PetscInt o_nz, const PetscInt o_nnz[], Mat *A) - // MatSetSizes(Mat A,PetscInt m,PetscInt n,PetscInt M,PetscInt N) const int m = localmesh->LocalNx * localmesh->LocalNy * localmesh->LocalNz; const int M = m * localmesh->getNXPE() * localmesh->getNYPE(); - MatCreateAIJ(MPI_COMM_WORLD, m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); + MatCreateAIJ(BoutComm::get(), m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); #endif #endif #ifndef HS_USE_PETSC From 305e1773088daa8e5954db18a176ebc3b3a0c537 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 2 Dec 2025 14:05:54 +0100 Subject: [PATCH 073/461] Add unversioned python path --- .docker/fedora/Dockerfile | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index 2b6800ce21..5d74886b50 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -35,9 +35,12 @@ RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ sudo make -C build install; \ rm -rf build +# Add unversioned path for python +RUN sudo ln -s /usr/local/lib/python3.* /usr/local/lib/python3.x/ + ENV PATH=/usr/local/bin:$PATH \ LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH \ - PYTHONPATH=/usr/local/lib/python3.13/site-packages/:$PYTHONPATH + PYTHONPATH=/usr/local/lib/python3.x/site-packages/:$PYTHONPATH # smoke test RUN python3 -c 'import boutpp' From 938ca44bc62aa91f0d655652f9548521c36174c1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 2 Dec 2025 14:52:51 +0100 Subject: [PATCH 074/461] Fix ln command --- .docker/fedora/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index 5d74886b50..da4d2d9fac 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -36,7 +36,7 @@ RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ rm -rf build # Add unversioned path for python -RUN sudo ln -s /usr/local/lib/python3.* /usr/local/lib/python3.x/ +RUN sudo ln -s /usr/local/lib/python3.* /usr/local/lib/python3.x ENV PATH=/usr/local/bin:$PATH \ LD_LIBRARY_PATH=/usr/local/lib:$LD_LIBRARY_PATH \ From 74b736d0b630637805217a2b678b6cc508647099 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 09:56:07 +0100 Subject: [PATCH 075/461] tests: Ruff fixes for FCI runtest --- tests/MMS/spatial/fci/runtest | 238 +++++++++++++++------------------- 1 file changed, 105 insertions(+), 133 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 7a9d6e655e..1f0b297ebc 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -6,19 +6,28 @@ # Cores: 2 # requires: zoidberg -from boututils.run_wrapper import build_and_log, launch_safe -from boutdata.collect import collect +import pathlib +import pickle +import sys + import boutconfig as conf +import zoidberg as zb +from boutdata.collect import collect +from boututils.run_wrapper import build_and_log, launch_safe +from numpy import arange, array, linspace, log, polyfit +from scipy.interpolate import RectBivariateSpline as RBS -from numpy import array, log, polyfit, linspace, arange -import pickle +def myRBS(a, b, c): + mx, _ = c.shape + kx = max(mx - 1, 1) + kx = min(kx, 3) + return RBS(a, b, c, kx=kx) -from sys import stdout -import zoidberg as zb +zb.poloidal_grid.RectBivariateSpline = myRBS -nx = 4 # Not changed for these tests +nx = 3 # Not changed for these tests # Resolution in y and z nlist = [8, 16, 32, 64, 128] @@ -31,7 +40,6 @@ directory = "data" nproc = 2 mthread = 2 - success = True error_2 = {} @@ -46,115 +54,88 @@ failures = [] build_and_log("FCI MMS test") for nslice in nslices: - for method in [ - "hermitespline", - "lagrange4pt", - "bilinear", - # "monotonichermitespline", - ]: - error_2[nslice] = [] - error_inf[nslice] = [] - - # Which central difference scheme to use and its expected order - order = nslice * 2 - method_orders[nslice] = {"name": "C{}".format(order), "order": order} - - for n in nlist: - # Define the magnetic field using new poloidal gridding method - # Note that the Bz and Bzprime parameters here must be the same as in mms.py - field = zb.field.Slab(Bz=0.05, Bzprime=0.1) - # Create rectangular poloidal grids - poloidal_grid = zb.poloidal_grid.RectangularPoloidalGrid( - nx, n, 0.1, 1.0, MXG=1 - ) - # Set the ylength and y locations - ylength = 10.0 - - if yperiodic: - ycoords = linspace(0.0, ylength, n, endpoint=False) - else: - # Doesn't include the end points - ycoords = (arange(n) + 0.5) * ylength / float(n) - - # Create the grid - grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=yperiodic) - # Make and write maps - maps = zb.make_maps(grid, field, nslice=nslice, quiet=True, MXG=1) - zb.write_maps( - grid, - field, - maps, - new_names=False, - metric2d=conf.isMetric2D(), - quiet=True, - ) - - args = " MZ={} MYG={} mesh:paralleltransform:y_periodic={} mesh:ddy:first={} NXPE={}".format( - n, - nslice, - yperiodic, - method_orders[nslice]["name"], - 2 if conf.has["petsc"] and method == "hermitespline" else 1, - ) - args += f" mesh:paralleltransform:xzinterpolation:type={method}" - - # Command to run - cmd = "./fci_mms " + args - - print("Running command: " + cmd) - - # Launch using MPI - s, out = launch_safe(cmd, nproc=nproc, mthread=mthread, pipe=True) - - # Save output to log file - with open("run.log." + str(n), "w") as f: - f.write(out) - - if s: - print("Run failed!\nOutput was:\n") - print(out) - exit(s) - - # Collect data - l_2 = collect( - "l_2", - tind=[1, 1], - info=False, - path=directory, - xguards=False, - yguards=False, - ) - l_inf = collect( - "l_inf", - tind=[1, 1], - info=False, - path=directory, - xguards=False, - yguards=False, - ) - - error_2[nslice].append(l_2) - error_inf[nslice].append(l_inf) - - print("Errors : l-2 {:f} l-inf {:f}".format(l_2, l_inf)) - - dx = 1.0 / array(nlist) - - # Calculate convergence order - fit = polyfit(log(dx), log(error_2[nslice]), 1) - order = fit[0] - stdout.write("Convergence order = {:f} (fit)".format(order)) - - order = log(error_2[nslice][-2] / error_2[nslice][-1]) / log(dx[-2] / dx[-1]) - stdout.write(", {:f} (small spacing)".format(order)) - - # Should be close to the expected order - if order > method_orders[nslice]["order"] * 0.95: - print("............ PASS\n") + error_2[nslice] = [] + error_inf[nslice] = [] + + # Which central difference scheme to use and its expected order + order = nslice * 2 + name = f"C{order}" + method_orders[nslice] = {"name": name, "order": order} + + for n in nlist: + # Define the magnetic field using new poloidal gridding method + # Note that the Bz and Bzprime parameters here must be the same as in mms.py + field = zb.field.Slab(Bz=0.05, Bzprime=0.1) + # Create rectangular poloidal grids + poloidal_grid = zb.poloidal_grid.RectangularPoloidalGrid(nx, n, 0.1, 1.0, MXG=1) + # Set the ylength and y locations + ylength = 10.0 + + if yperiodic: + ycoords = linspace(0.0, ylength, n, endpoint=False) else: - print("............ FAIL\n") - success = False - failures.append(method_orders[nslice]["name"]) + # Doesn't include the end points + ycoords = (arange(n) + 0.5) * ylength / float(n) + + # Create the grid + grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=yperiodic) + # Make and write maps + maps = zb.make_maps(grid, field, nslice=nslice, quiet=True, MXG=1) + zb.write_maps( + grid, field, maps, new_names=False, metric2d=conf.isMetric2D(), quiet=True + ) + + # Command to run + args = f" MZ={n} MYG={nslice} mesh:paralleltransform:y_periodic={yperiodic} mesh:ddy:first={name}" + cmd = f"./fci_mms {args}" + + print(f"Running command: {cmd}") + + # Launch using MPI + s, out = launch_safe(cmd, nproc=nproc, mthread=mthread, pipe=True) + + # Save output to log file + pathlib.Path(f"run.log.{n}").write_text(out) + + if s: + print(f"Run failed!\nOutput was:\n{out}") + sys.exit(s) + + # Collect data + l_2 = collect( + "l_2", tind=[1, 1], info=False, path=directory, xguards=False, yguards=False + ) + l_inf = collect( + "l_inf", + tind=[1, 1], + info=False, + path=directory, + xguards=False, + yguards=False, + ) + + error_2[nslice].append(l_2) + error_inf[nslice].append(l_inf) + + print(f"Errors : l-2 {l_2:f} l-inf {l_inf:f}") + + dx = 1.0 / array(nlist) + + # Calculate convergence order + fit = polyfit(log(dx), log(error_2[nslice]), 1) + order = fit[0] + print(f"Convergence order = {order:f} (fit)", end="") + + order = log(error_2[nslice][-2] / error_2[nslice][-1]) / log(dx[-2] / dx[-1]) + print(f", {order:f} (small spacing)") + + # Should be close to the expected order + if order > order * 0.95: + print("............ PASS\n") + else: + print("............ FAIL\n") + success = False + failures.append(name) with open("fci_mms.pkl", "wb") as output: @@ -164,7 +145,7 @@ with open("fci_mms.pkl", "wb") as output: pickle.dump(error_inf[nslice], output) # Do we want to show the plot as well as save it to file. -showPlot = True +show_plot = True if False: try: @@ -174,18 +155,9 @@ if False: fig, ax = plt.subplots(1, 1) for nslice in nslices: - ax.plot( - dx, - error_2[nslice], - "-", - label="{} $l_2$".format(method_orders[nslice]["name"]), - ) - ax.plot( - dx, - error_inf[nslice], - "--", - label="{} $l_\\inf$".format(method_orders[nslice]["name"]), - ) + name = method_orders[nslice]["name"] + ax.plot(dx, error_2[nslice], "-", label=f"{name} $l_2$") + ax.plot(dx, error_inf[nslice], "--", label=f"{name} $l_\\inf$") ax.legend(loc="upper left") ax.grid() ax.set_yscale("log") @@ -198,7 +170,7 @@ if False: print("Plot saved to fci_mms.pdf") - if showPlot: + if show_plot: plt.show() plt.close() except ImportError: @@ -206,9 +178,9 @@ if False: if success: print("All tests passed") - exit(0) + sys.exit(0) else: print("Some tests failed:") for failure in failures: - print("\t" + failure) - exit(1) + print(f"\t{failure}") + sys.exit(1) From eb589e16948c795bc4137e85e72d77cd452c3142 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 12:05:01 +0100 Subject: [PATCH 076/461] tests: Expand FCI MMS test to `Grad2_par2` --- CMakeLists.txt | 1 + tests/MMS/spatial/fci/data/BOUT.inp | 5 +- tests/MMS/spatial/fci/fci_mms.cxx | 48 ++++++++++----- tests/MMS/spatial/fci/mms.py | 5 +- tests/MMS/spatial/fci/runtest | 96 +++++++++++++++-------------- 5 files changed, 90 insertions(+), 65 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d100e444..eabfc055ee 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -376,6 +376,7 @@ if (zoidberg_FOUND EQUAL 0) else() set(zoidberg_FOUND OFF) endif() +message(STATUS "Found Zoidberg for FCI tests: ${zoidberg_FOUND}") option(BOUT_GENERATE_FIELDOPS "Automatically re-generate the Field arithmetic operators from the Python templates. \ Requires Python3, clang-format, and Jinja2. Turn this OFF to skip generating them if, for example, \ diff --git a/tests/MMS/spatial/fci/data/BOUT.inp b/tests/MMS/spatial/fci/data/BOUT.inp index 5f2001a906..f065989524 100644 --- a/tests/MMS/spatial/fci/data/BOUT.inp +++ b/tests/MMS/spatial/fci/data/BOUT.inp @@ -1,7 +1,6 @@ - input_field = sin(y - 2*z) + sin(y - z) - -solution = (6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y - z)) + 0.628318530717959*cos(y - 2*z) + 0.628318530717959*cos(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) +grad_par_solution = (6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y - z)) + 0.628318530717959*cos(y - 2*z) + 0.628318530717959*cos(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) +grad2_par2_solution = (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0))/sqrt((0.01*x + 0.045)^2 + 1.0) MXG = 1 MYG = 1 diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index 18405a7f88..48b18f04ef 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -1,5 +1,4 @@ #include "bout/bout.hxx" -#include "bout/derivs.hxx" #include "bout/field_factory.hxx" int main(int argc, char** argv) { @@ -8,29 +7,50 @@ int main(int argc, char** argv) { using bout::globals::mesh; Field3D input{FieldFactory::get()->create3D("input_field", Options::getRoot(), mesh)}; - Field3D solution{FieldFactory::get()->create3D("solution", Options::getRoot(), mesh)}; // Communicate to calculate parallel transform mesh->communicate(input); - Field3D result{Grad_par(input)}; - Field3D error{result - solution}; - Options dump; // Add mesh geometry variables mesh->outputVars(dump); - dump["l_2"] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); - dump["l_inf"] = max(abs(error), true, "RGN_NOBNDRY"); + auto* factory = FieldFactory::get(); + { + Field3D solution{factory->create3D("grad_par_solution", Options::getRoot(), mesh)}; + Field3D result{Grad_par(input)}; + Field3D error{result - solution}; + + dump["grad_par_l_2"] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); + dump["grad_par_l_inf"] = max(abs(error), true, "RGN_NOBNDRY"); - dump["result"] = result; - dump["error"] = error; - dump["input"] = input; - dump["solution"] = solution; + dump["grad_par_result"] = result; + dump["grad_par_error"] = error; + dump["grad_par_input"] = input; + dump["grad_par_solution"] = solution; - for (int slice = 1; slice < mesh->ystart; ++slice) { - dump[fmt::format("input.ynext(-{})", slice)] = input.ynext(-slice); - dump[fmt::format("input.ynext({})", slice)] = input.ynext(slice); + for (int slice = 1; slice < mesh->ystart; ++slice) { + dump[fmt::format("grad_par_input.ynext(-{})", slice)] = input.ynext(-slice); + dump[fmt::format("grad_par_input.ynext({})", slice)] = input.ynext(slice); + } + } + { + Field3D solution{factory->create3D("grad2_par2_solution", Options::getRoot(), mesh)}; + Field3D result{Grad2_par2(input)}; + Field3D error{result - solution}; + + dump["grad2_par2_l_2"] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); + dump["grad2_par2_l_inf"] = max(abs(error), true, "RGN_NOBNDRY"); + + dump["grad2_par2_result"] = result; + dump["grad2_par2_error"] = error; + dump["grad2_par2_input"] = input; + dump["grad2_par2_solution"] = solution; + + for (int slice = 1; slice < mesh->ystart; ++slice) { + dump[fmt::format("grad2_par2_input.ynext(-{})", slice)] = input.ynext(-slice); + dump[fmt::format("grad2_par2_input.ynext({})", slice)] = input.ynext(slice); + } } bout::writeDefaultOutputFile(dump); diff --git a/tests/MMS/spatial/fci/mms.py b/tests/MMS/spatial/fci/mms.py index 1e71135c90..ae48ef8f06 100755 --- a/tests/MMS/spatial/fci/mms.py +++ b/tests/MMS/spatial/fci/mms.py @@ -30,5 +30,6 @@ def FCI_ddy(f): ############################################ # Equations solved -print("input = " + exprToStr(f)) -print("solution = " + exprToStr(FCI_ddy(f))) +print(f"input_field = {exprToStr(f)}") +print(f"grad_par_solution = {exprToStr(FCI_ddy(f))}") +print(f"grad2_par2_solution = {exprToStr(FCI_ddy(FCI_ddy(f)))}") diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 1f0b297ebc..27b18baafd 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -9,6 +9,7 @@ import pathlib import pickle import sys +from collections import defaultdict import boutconfig as conf import zoidberg as zb @@ -19,6 +20,7 @@ from scipy.interpolate import RectBivariateSpline as RBS def myRBS(a, b, c): + """RectBivariateSpline, but automatically tune spline degree for small arrays""" mx, _ = c.shape kx = max(mx - 1, 1) kx = min(kx, 3) @@ -27,6 +29,13 @@ def myRBS(a, b, c): zb.poloidal_grid.RectBivariateSpline = myRBS + +def quiet_collect(name: str): + return collect( + name, tind=[1, 1], info=False, path=directory, xguards=False, yguards=False, + ) + + nx = 3 # Not changed for these tests # Resolution in y and z @@ -50,12 +59,29 @@ method_orders = {} yperiodic = True failures = [] +operators = ("grad_par", "grad2_par2") build_and_log("FCI MMS test") + +def assert_convergence(error, dx, name, order) -> bool: + fit = polyfit(log(dx), log(error), 1) + order = fit[0] + print(f"{name} convergence order = {order:f} (fit)", end="") + + order = log(error[-2] / error[-1]) / log(dx[-2] / dx[-1]) + print(f", {order:f} (small spacing)", end="") + + # Should be close to the expected order + success = order > order * 0.95 + print(f" ............ {'PASS' if success else 'FAIL'}") + + return success + + for nslice in nslices: - error_2[nslice] = [] - error_inf[nslice] = [] + error_2[nslice] = defaultdict(list) + error_inf[nslice] = defaultdict(list) # Which central difference scheme to use and its expected order order = nslice * 2 @@ -79,70 +105,48 @@ for nslice in nslices: # Create the grid grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=yperiodic) - # Make and write maps maps = zb.make_maps(grid, field, nslice=nslice, quiet=True, MXG=1) zb.write_maps( - grid, field, maps, new_names=False, metric2d=conf.isMetric2D(), quiet=True + grid, field, maps, new_names=False, metric2d=conf.isMetric2D(), quiet=True, ) # Command to run args = f" MZ={n} MYG={nslice} mesh:paralleltransform:y_periodic={yperiodic} mesh:ddy:first={name}" cmd = f"./fci_mms {args}" - print(f"Running command: {cmd}") # Launch using MPI - s, out = launch_safe(cmd, nproc=nproc, mthread=mthread, pipe=True) + status, out = launch_safe(cmd, nproc=nproc, mthread=mthread, pipe=True) # Save output to log file pathlib.Path(f"run.log.{n}").write_text(out) - if s: + if status: print(f"Run failed!\nOutput was:\n{out}") - sys.exit(s) + sys.exit(status) # Collect data - l_2 = collect( - "l_2", tind=[1, 1], info=False, path=directory, xguards=False, yguards=False - ) - l_inf = collect( - "l_inf", - tind=[1, 1], - info=False, - path=directory, - xguards=False, - yguards=False, - ) + for operator in operators: + l_2 = quiet_collect(f"{operator}_l_2") + l_inf = quiet_collect(f"{operator}_l_inf") - error_2[nslice].append(l_2) - error_inf[nslice].append(l_inf) + error_2[nslice][operator].append(l_2) + error_inf[nslice][operator].append(l_inf) - print(f"Errors : l-2 {l_2:f} l-inf {l_inf:f}") + print(f"{operator} errors: l-2 {l_2:f} l-inf {l_inf:f}") dx = 1.0 / array(nlist) + for operator in operators: + test_name = f"{operator} {name}" + success &= assert_convergence(error_2[nslice][operator], dx, test_name, order) + if not success: + failures.append(test_name) - # Calculate convergence order - fit = polyfit(log(dx), log(error_2[nslice]), 1) - order = fit[0] - print(f"Convergence order = {order:f} (fit)", end="") - - order = log(error_2[nslice][-2] / error_2[nslice][-1]) / log(dx[-2] / dx[-1]) - print(f", {order:f} (small spacing)") - - # Should be close to the expected order - if order > order * 0.95: - print("............ PASS\n") - else: - print("............ FAIL\n") - success = False - failures.append(name) - -with open("fci_mms.pkl", "wb") as output: +with pathlib.Path("fci_mms.pkl").open("wb") as output: pickle.dump(nlist, output) - for nslice in nslices: - pickle.dump(error_2[nslice], output) - pickle.dump(error_inf[nslice], output) + pickle.dump(error_2, output) + pickle.dump(error_inf, output) # Do we want to show the plot as well as save it to file. show_plot = True @@ -177,10 +181,10 @@ if False: print("No matplotlib") if success: - print("All tests passed") - sys.exit(0) + print("\nAll tests passed") else: - print("Some tests failed:") + print("\nSome tests failed:") for failure in failures: print(f"\t{failure}") - sys.exit(1) + +sys.exit(0 if success else 1) From c0cc84b78ffe127fea61414db92d92c23743906b Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 15:10:32 +0100 Subject: [PATCH 077/461] tests: Generalise FCI MMS test to allow for more cases --- tests/MMS/spatial/fci/runtest | 302 +++++++++++++++++++++------------- 1 file changed, 188 insertions(+), 114 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 27b18baafd..c575f1afbc 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -6,10 +6,12 @@ # Cores: 2 # requires: zoidberg +import argparse +import json import pathlib -import pickle import sys -from collections import defaultdict +from time import time +from typing import Any import boutconfig as conf import zoidberg as zb @@ -18,6 +20,16 @@ from boututils.run_wrapper import build_and_log, launch_safe from numpy import arange, array, linspace, log, polyfit from scipy.interpolate import RectBivariateSpline as RBS +# Global parameters +DIRECTORY = "data" +NPROC = 2 +MTHREAD = 2 +OPERATORS = ("grad_par", "grad2_par2") +NX = 3 +# Resolution in y and z +NLIST = [8, 16, 32, 64, 128] +dx = 1.0 / array(NLIST) + def myRBS(a, b, c): """RectBivariateSpline, but automatically tune spline degree for small arrays""" @@ -30,38 +42,16 @@ def myRBS(a, b, c): zb.poloidal_grid.RectBivariateSpline = myRBS -def quiet_collect(name: str): +def quiet_collect(name: str) -> float: + # Index to return a plain (numpy) float rather than `BoutArray` return collect( - name, tind=[1, 1], info=False, path=directory, xguards=False, yguards=False, - ) - - -nx = 3 # Not changed for these tests - -# Resolution in y and z -nlist = [8, 16, 32, 64, 128] - -# Number of parallel slices (in each direction) -nslices = [1] - -directory = "data" - -nproc = 2 -mthread = 2 - -success = True - -error_2 = {} -error_inf = {} -method_orders = {} - -# Run with periodic Y? -yperiodic = True - -failures = [] -operators = ("grad_par", "grad2_par2") - -build_and_log("FCI MMS test") + name, + tind=[1, 1], + info=False, + path=DIRECTORY, + xguards=False, + yguards=False, + )[()] def assert_convergence(error, dx, name, order) -> bool: @@ -74,117 +64,201 @@ def assert_convergence(error, dx, name, order) -> bool: # Should be close to the expected order success = order > order * 0.95 - print(f" ............ {'PASS' if success else 'FAIL'}") + print(f"\t............ {'PASS' if success else 'FAIL'}") return success -for nslice in nslices: - error_2[nslice] = defaultdict(list) - error_inf[nslice] = defaultdict(list) +def run_fci_operators( + nslice: int, nz: int, yperiodic: bool, name: str +) -> dict[str, float]: + # Define the magnetic field using new poloidal gridding method + # Note that the Bz and Bzprime parameters here must be the same as in mms.py + field = zb.field.Slab(Bz=0.05, Bzprime=0.1) + # Create rectangular poloidal grids + poloidal_grid = zb.poloidal_grid.RectangularPoloidalGrid(NX, nz, 0.1, 1.0, MXG=1) + # Set the ylength and y locations + ylength = 10.0 + + if yperiodic: + ycoords = linspace(0.0, ylength, nz, endpoint=False) + else: + # Doesn't include the end points + ycoords = (arange(nz) + 0.5) * ylength / float(nz) + + # Create the grid + grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=yperiodic) + maps = zb.make_maps(grid, field, nslice=nslice, quiet=True, MXG=1) + zb.write_maps( + grid, + field, + maps, + new_names=False, + metric2d=conf.isMetric2D(), + quiet=True, + ) - # Which central difference scheme to use and its expected order - order = nslice * 2 - name = f"C{order}" - method_orders[nslice] = {"name": name, "order": order} - - for n in nlist: - # Define the magnetic field using new poloidal gridding method - # Note that the Bz and Bzprime parameters here must be the same as in mms.py - field = zb.field.Slab(Bz=0.05, Bzprime=0.1) - # Create rectangular poloidal grids - poloidal_grid = zb.poloidal_grid.RectangularPoloidalGrid(nx, n, 0.1, 1.0, MXG=1) - # Set the ylength and y locations - ylength = 10.0 - - if yperiodic: - ycoords = linspace(0.0, ylength, n, endpoint=False) - else: - # Doesn't include the end points - ycoords = (arange(n) + 0.5) * ylength / float(n) - - # Create the grid - grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=yperiodic) - maps = zb.make_maps(grid, field, nslice=nslice, quiet=True, MXG=1) - zb.write_maps( - grid, field, maps, new_names=False, metric2d=conf.isMetric2D(), quiet=True, - ) + # Command to run + args = f"MZ={nz} MYG={nslice} mesh:paralleltransform:y_periodic={yperiodic} mesh:ddy:first={name}" + cmd = f"./fci_mms {args}" + print(f"Running command: {cmd}", end="") + + # Launch using MPI + start = time() + status, out = launch_safe(cmd, nproc=NPROC, mthread=MTHREAD, pipe=True) + print(f" ... done in {time() - start:.3}s") + + # Save output to log file + pathlib.Path(f"run.log.{nz}").write_text(out) + + if status: + print(f"Run failed!\nOutput was:\n{out}") + sys.exit(status) + + return { + operator: { + "l_2": quiet_collect(f"{operator}_l_2"), + "l_inf": quiet_collect(f"{operator}_l_inf"), + } + for operator in OPERATORS + } - # Command to run - args = f" MZ={n} MYG={nslice} mesh:paralleltransform:y_periodic={yperiodic} mesh:ddy:first={name}" - cmd = f"./fci_mms {args}" - print(f"Running command: {cmd}") - # Launch using MPI - status, out = launch_safe(cmd, nproc=nproc, mthread=mthread, pipe=True) +def transpose( + errors: list[dict[str, dict[str, float]]], +) -> dict[str, dict[str, list[float]]]: + """Turn a list of {operator: error} into a dict of {operator: [errors]}""" - # Save output to log file - pathlib.Path(f"run.log.{n}").write_text(out) + kinds = ("l_2", "l_inf") + result = {operator: {kind: [] for kind in kinds} for operator in OPERATORS} + for error in errors: + for k, v in error.items(): + for kind in kinds: + result[k][kind].append(v[kind]) + return result - if status: - print(f"Run failed!\nOutput was:\n{out}") - sys.exit(status) - # Collect data - for operator in operators: - l_2 = quiet_collect(f"{operator}_l_2") - l_inf = quiet_collect(f"{operator}_l_inf") +def check_fci_operators(case: dict) -> bool: + failures = [] - error_2[nslice][operator].append(l_2) - error_inf[nslice][operator].append(l_inf) + nslice = case["nslice"] + yperiodic = case["yperiodic"] + name = case["name"] + order = case["order"] + + all_errors = [] + + for n in NLIST: + errors = run_fci_operators(nslice, n, yperiodic, name) + all_errors.append(errors) + + for operator in OPERATORS: + l_2 = errors[operator]["l_2"] + l_inf = errors[operator]["l_inf"] print(f"{operator} errors: l-2 {l_2:f} l-inf {l_inf:f}") - dx = 1.0 / array(nlist) - for operator in operators: + final_errors = transpose(all_errors) + for operator in OPERATORS: test_name = f"{operator} {name}" - success &= assert_convergence(error_2[nslice][operator], dx, test_name, order) + success = assert_convergence( + final_errors[operator]["l_2"], dx, test_name, order + ) if not success: failures.append(test_name) + return final_errors, failures -with pathlib.Path("fci_mms.pkl").open("wb") as output: - pickle.dump(nlist, output) - pickle.dump(error_2, output) - pickle.dump(error_inf, output) - -# Do we want to show the plot as well as save it to file. -show_plot = True -if False: +def make_plots(cases): try: - # Plot using matplotlib if available import matplotlib.pyplot as plt + except ImportError: + print("No matplotlib") + return - fig, ax = plt.subplots(1, 1) + fig, axes = plt.subplots(1, len(OPERATORS), figsize=(9, 4)) - for nslice in nslices: - name = method_orders[nslice]["name"] - ax.plot(dx, error_2[nslice], "-", label=f"{name} $l_2$") - ax.plot(dx, error_inf[nslice], "--", label=f"{name} $l_\\inf$") + for ax, operator in zip(axes, OPERATORS): + for name, case in cases.items(): + ax.loglog(dx, case[operator]["l_2"], "-", label=f"{name} $l_2$") + ax.loglog(dx, case[operator]["l_inf"], "--", label=f"{name} $l_\\inf$") ax.legend(loc="upper left") ax.grid() - ax.set_yscale("log") - ax.set_xscale("log") - ax.set_title("error scaling") + ax.set_title(f"Error scaling for {operator}") ax.set_xlabel(r"Mesh spacing $\delta x$") ax.set_ylabel("Error norm") - plt.savefig("fci_mms.pdf") + fig.tight_layout() + fig.savefig("fci_mms.pdf") + print("Plot saved to fci_mms.pdf") - print("Plot saved to fci_mms.pdf") + if args.show_plots: + plt.show() + plt.close() - if show_plot: - plt.show() - plt.close() - except ImportError: - print("No matplotlib") -if success: - print("\nAll tests passed") -else: - print("\nSome tests failed:") - for failure in failures: - print(f"\t{failure}") +def make_case(nslice: int, yperiodic: bool) -> dict[str, Any]: + """ + nslice: + Number of parallel slices (in each direction) + yperiodic: + Run with periodic Y + """ + order = nslice * 2 + return { + "nslice": nslice, + # Which central difference scheme to use and its expected order + "order": order, + "name": f"C{order}", + "yperiodic": yperiodic, + } + + +if __name__ == "__main__": + build_and_log("FCI MMS test") + + parser = argparse.ArgumentParser("Error scaling test for FCI operators") + parser.add_argument( + "--make-plots", action="store_true", help="Create plots of error scaling" + ) + parser.add_argument( + "--show-plots", + action="store_true", + help="Stop and show plots, implies --make-plots", + ) + parser.add_argument( + "--dump-errors", + type=str, + help="Output file to dump errors as JSON", + default="fci_operator_errors.json", + ) + + args = parser.parse_args() + + success = True + failures = [] + cases = { + "nslice=1": make_case(nslice=1, yperiodic=True), + } + + for case in cases.values(): + error2, failures_ = check_fci_operators(case) + case.update(error2) + failures.extend(failures_) + success &= len(failures) == 0 + + if args.dump_errors: + pathlib.Path(args.dump_errors).write_text(json.dumps(cases)) + + if args.make_plots or args.show_plots: + make_plots(cases) + + if success: + print("\nAll tests passed") + else: + print("\nSome tests failed:") + for failure in failures: + print(f"\t{failure}") -sys.exit(0 if success else 1) + sys.exit(0 if success else 1) From 2eda48cf04bb41abaa8fce6d61336fc5b5dddf2d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 15:30:58 +0100 Subject: [PATCH 078/461] tests: Add test for FCI `Div_par` --- tests/MMS/spatial/fci/data/BOUT.inp | 1 + tests/MMS/spatial/fci/fci_mms.cxx | 67 +++++++++++++---------------- tests/MMS/spatial/fci/mms.py | 4 ++ tests/MMS/spatial/fci/runtest | 2 +- 4 files changed, 36 insertions(+), 38 deletions(-) diff --git a/tests/MMS/spatial/fci/data/BOUT.inp b/tests/MMS/spatial/fci/data/BOUT.inp index f065989524..3103b37e2d 100644 --- a/tests/MMS/spatial/fci/data/BOUT.inp +++ b/tests/MMS/spatial/fci/data/BOUT.inp @@ -1,6 +1,7 @@ input_field = sin(y - 2*z) + sin(y - z) grad_par_solution = (6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y - z)) + 0.628318530717959*cos(y - 2*z) + 0.628318530717959*cos(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) grad2_par2_solution = (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0))/sqrt((0.01*x + 0.045)^2 + 1.0) +div_par_solution = (0.01*x + 0.045)*(-12.5663706143592*cos(y - 2*z) - 6.28318530717959*cos(y - z) + 0.628318530717959*(cos(y - 2*z) + cos(y - z))/(0.01*x + 0.045))/sqrt((0.01*x + 0.045)^2 + 1.0) MXG = 1 MYG = 1 diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index 48b18f04ef..bc995424c6 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -1,6 +1,33 @@ #include "bout/bout.hxx" +#include "bout/difops.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include + +namespace { +auto fci_op_test(const std::string& name, Options& dump, const Field3D& input, + const Field3D& result) { + auto* mesh = input.getMesh(); + Field3D solution{FieldFactory::get()->create3D(fmt::format("{}_solution", name), + Options::getRoot(), mesh)}; + Field3D error{result - solution}; + + dump[fmt::format("{}_l_2", name)] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); + dump[fmt::format("{}_l_inf", name)] = max(abs(error), true, "RGN_NOBNDRY"); + + dump[fmt::format("{}_result", name)] = result; + dump[fmt::format("{}_error", name)] = error; + dump[fmt::format("{}_input", name)] = input; + dump[fmt::format("{}_solution", name)] = solution; + + for (int slice = 1; slice < mesh->ystart; ++slice) { + dump[fmt::format("{}_input.ynext(-{})", name, slice)] = input.ynext(-slice); + dump[fmt::format("{}_input.ynext({})", name, slice)] = input.ynext(slice); + } +} +} // namespace + int main(int argc, char** argv) { BoutInitialise(argc, argv); @@ -15,43 +42,9 @@ int main(int argc, char** argv) { // Add mesh geometry variables mesh->outputVars(dump); - auto* factory = FieldFactory::get(); - { - Field3D solution{factory->create3D("grad_par_solution", Options::getRoot(), mesh)}; - Field3D result{Grad_par(input)}; - Field3D error{result - solution}; - - dump["grad_par_l_2"] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); - dump["grad_par_l_inf"] = max(abs(error), true, "RGN_NOBNDRY"); - - dump["grad_par_result"] = result; - dump["grad_par_error"] = error; - dump["grad_par_input"] = input; - dump["grad_par_solution"] = solution; - - for (int slice = 1; slice < mesh->ystart; ++slice) { - dump[fmt::format("grad_par_input.ynext(-{})", slice)] = input.ynext(-slice); - dump[fmt::format("grad_par_input.ynext({})", slice)] = input.ynext(slice); - } - } - { - Field3D solution{factory->create3D("grad2_par2_solution", Options::getRoot(), mesh)}; - Field3D result{Grad2_par2(input)}; - Field3D error{result - solution}; - - dump["grad2_par2_l_2"] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); - dump["grad2_par2_l_inf"] = max(abs(error), true, "RGN_NOBNDRY"); - - dump["grad2_par2_result"] = result; - dump["grad2_par2_error"] = error; - dump["grad2_par2_input"] = input; - dump["grad2_par2_solution"] = solution; - - for (int slice = 1; slice < mesh->ystart; ++slice) { - dump[fmt::format("grad2_par2_input.ynext(-{})", slice)] = input.ynext(-slice); - dump[fmt::format("grad2_par2_input.ynext({})", slice)] = input.ynext(slice); - } - } + fci_op_test("grad_par", dump, input, Grad_par(input)); + fci_op_test("grad2_par2", dump, input, Grad2_par2(input)); + fci_op_test("div_par", dump, input, Div_par(input)); bout::writeDefaultOutputFile(dump); diff --git a/tests/MMS/spatial/fci/mms.py b/tests/MMS/spatial/fci/mms.py index ae48ef8f06..5f5d48bee2 100755 --- a/tests/MMS/spatial/fci/mms.py +++ b/tests/MMS/spatial/fci/mms.py @@ -26,6 +26,9 @@ def FCI_ddy(f): return (Bt * diff(f, y) * 2.0 * pi / Ly + Bpx * diff(f, z) * 2.0 * pi / Lz) / B +def FCI_div_par(f): + return Bpx * FCI_ddy(f / Bpx) + ############################################ # Equations solved @@ -33,3 +36,4 @@ def FCI_ddy(f): print(f"input_field = {exprToStr(f)}") print(f"grad_par_solution = {exprToStr(FCI_ddy(f))}") print(f"grad2_par2_solution = {exprToStr(FCI_ddy(FCI_ddy(f)))}") +print(f"div_par_solution = {exprToStr(FCI_div_par(f))}") diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index c575f1afbc..c98d924e7b 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -24,7 +24,7 @@ from scipy.interpolate import RectBivariateSpline as RBS DIRECTORY = "data" NPROC = 2 MTHREAD = 2 -OPERATORS = ("grad_par", "grad2_par2") +OPERATORS = ("grad_par", "grad2_par2", "div_par") NX = 3 # Resolution in y and z NLIST = [8, 16, 32, 64, 128] From 636bf288f616f67e2d84552e569a24d64d8f5485 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 15:47:01 +0100 Subject: [PATCH 079/461] tests: Make MMS script update input in-place --- tests/MMS/spatial/fci/data/BOUT.inp | 7 +++--- tests/MMS/spatial/fci/mms.py | 36 +++++++++++++++++++++-------- 2 files changed, 30 insertions(+), 13 deletions(-) diff --git a/tests/MMS/spatial/fci/data/BOUT.inp b/tests/MMS/spatial/fci/data/BOUT.inp index 3103b37e2d..f44773667c 100644 --- a/tests/MMS/spatial/fci/data/BOUT.inp +++ b/tests/MMS/spatial/fci/data/BOUT.inp @@ -3,13 +3,12 @@ grad_par_solution = (6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y grad2_par2_solution = (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0))/sqrt((0.01*x + 0.045)^2 + 1.0) div_par_solution = (0.01*x + 0.045)*(-12.5663706143592*cos(y - 2*z) - 6.28318530717959*cos(y - z) + 0.628318530717959*(cos(y - 2*z) + cos(y - z))/(0.01*x + 0.045))/sqrt((0.01*x + 0.045)^2 + 1.0) -MXG = 1 -MYG = 1 -NXPE = 1 - [mesh] symmetricglobalx = true file = fci.grid.nc +MXG = 1 +MYG = 1 +NXPE = 1 [mesh:ddy] first = C2 diff --git a/tests/MMS/spatial/fci/mms.py b/tests/MMS/spatial/fci/mms.py index 5f5d48bee2..e434ea3ecf 100755 --- a/tests/MMS/spatial/fci/mms.py +++ b/tests/MMS/spatial/fci/mms.py @@ -3,11 +3,15 @@ # Generate manufactured solution and sources for FCI test # -from boutdata.mms import * +from math import pi +import warnings -from sympy import sin, cos, sqrt +from boututils.boutwarnings import AlwaysWarning +from boutdata.data import BoutOptionsFile +from boutdata.mms import diff, exprToStr, x, y, z +from sympy import sin, sqrt, Expr -from math import pi +warnings.simplefilter("ignore", AlwaysWarning) f = sin(y - z) + sin(y - 2 * z) @@ -23,17 +27,31 @@ B = sqrt(Bpx**2 + Bt**2) -def FCI_ddy(f): +def FCI_ddy(f: Expr) -> Expr: return (Bt * diff(f, y) * 2.0 * pi / Ly + Bpx * diff(f, z) * 2.0 * pi / Lz) / B -def FCI_div_par(f): + +def FCI_div_par(f: Expr) -> Expr: return Bpx * FCI_ddy(f / Bpx) ############################################ # Equations solved -print(f"input_field = {exprToStr(f)}") -print(f"grad_par_solution = {exprToStr(FCI_ddy(f))}") -print(f"grad2_par2_solution = {exprToStr(FCI_ddy(FCI_ddy(f)))}") -print(f"div_par_solution = {exprToStr(FCI_div_par(f))}") +input_field = exprToStr(f) +grad_par_solution = exprToStr(FCI_ddy(f)) +grad2_par2_solution = exprToStr(FCI_ddy(FCI_ddy(f))) +div_par_solution = exprToStr(FCI_div_par(f)) + +print(f"input_field = {input_field}") +print(f"grad_par_solution = {grad_par_solution}") +print(f"grad2_par2_solution = {grad2_par2_solution}") +print(f"div_par_solution = {div_par_solution}") +print(f"div_par_K_grad_par_solution = {div_par_K_grad_par_solution}") + +options = BoutOptionsFile("data/BOUT.inp") +options["input_field"] = input_field +options["grad_par_solution"] = grad_par_solution +options["grad2_par2_solution"] = grad2_par2_solution +options["div_par_solution"] = div_par_solution +options.write("data/BOUT.inp", overwrite=True) From 16318eccf35685ca4caf7fe9263a06c4d884abe4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 16:34:44 +0100 Subject: [PATCH 080/461] tests: Add test for FCI `Div_par_K_Grad_par` --- tests/MMS/spatial/fci/data/BOUT.inp | 2 ++ tests/MMS/spatial/fci/fci_mms.cxx | 5 ++++- tests/MMS/spatial/fci/mms.py | 23 ++++++++++++++++++----- tests/MMS/spatial/fci/runtest | 5 +++-- 4 files changed, 27 insertions(+), 8 deletions(-) diff --git a/tests/MMS/spatial/fci/data/BOUT.inp b/tests/MMS/spatial/fci/data/BOUT.inp index f44773667c..e31d84cb35 100644 --- a/tests/MMS/spatial/fci/data/BOUT.inp +++ b/tests/MMS/spatial/fci/data/BOUT.inp @@ -2,6 +2,8 @@ input_field = sin(y - 2*z) + sin(y - z) grad_par_solution = (6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y - z)) + 0.628318530717959*cos(y - 2*z) + 0.628318530717959*cos(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) grad2_par2_solution = (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0))/sqrt((0.01*x + 0.045)^2 + 1.0) div_par_solution = (0.01*x + 0.045)*(-12.5663706143592*cos(y - 2*z) - 6.28318530717959*cos(y - z) + 0.628318530717959*(cos(y - 2*z) + cos(y - z))/(0.01*x + 0.045))/sqrt((0.01*x + 0.045)^2 + 1.0) +div_par_K_grad_par_solution = (0.01*x + 0.045)*(6.28318530717959*sin(y - z) - 0.628318530717959*sin(y - z)/(0.01*x + 0.045))*(6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y - z)) + 0.628318530717959*cos(y - 2*z) + 0.628318530717959*cos(y - z))/((0.01*x + 0.045)^2 + 1.0) + (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0))*cos(y - z)/sqrt((0.01*x + 0.045)^2 + 1.0) +K = cos(y - z) [mesh] symmetricglobalx = true diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index bc995424c6..a0b70e9da6 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -2,6 +2,7 @@ #include "bout/difops.hxx" #include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/options.hxx" #include @@ -34,9 +35,10 @@ int main(int argc, char** argv) { using bout::globals::mesh; Field3D input{FieldFactory::get()->create3D("input_field", Options::getRoot(), mesh)}; + Field3D K{FieldFactory::get()->create3D("K", Options::getRoot(), mesh)}; // Communicate to calculate parallel transform - mesh->communicate(input); + mesh->communicate(input, K); Options dump; // Add mesh geometry variables @@ -45,6 +47,7 @@ int main(int argc, char** argv) { fci_op_test("grad_par", dump, input, Grad_par(input)); fci_op_test("grad2_par2", dump, input, Grad2_par2(input)); fci_op_test("div_par", dump, input, Div_par(input)); + fci_op_test("div_par_K_grad_par", dump, input, Div_par_K_Grad_par(K, input)); bout::writeDefaultOutputFile(dump); diff --git a/tests/MMS/spatial/fci/mms.py b/tests/MMS/spatial/fci/mms.py index e434ea3ecf..79ec661507 100755 --- a/tests/MMS/spatial/fci/mms.py +++ b/tests/MMS/spatial/fci/mms.py @@ -9,11 +9,12 @@ from boututils.boutwarnings import AlwaysWarning from boutdata.data import BoutOptionsFile from boutdata.mms import diff, exprToStr, x, y, z -from sympy import sin, sqrt, Expr +from sympy import sin, cos, sqrt, Expr warnings.simplefilter("ignore", AlwaysWarning) f = sin(y - z) + sin(y - 2 * z) +K = cos(z - y) Lx = 0.1 Ly = 10.0 @@ -27,23 +28,33 @@ B = sqrt(Bpx**2 + Bt**2) -def FCI_ddy(f: Expr) -> Expr: +def FCI_grad_par(f: Expr) -> Expr: return (Bt * diff(f, y) * 2.0 * pi / Ly + Bpx * diff(f, z) * 2.0 * pi / Lz) / B +def FCI_grad2_par2(f: Expr) -> Expr: + return FCI_grad_par(FCI_grad_par(f)) + + def FCI_div_par(f: Expr) -> Expr: - return Bpx * FCI_ddy(f / Bpx) + return Bpx * FCI_grad_par(f / Bpx) + + +def FCI_div_par_K_grad_par(f: Expr, K: Expr) -> Expr: + return (K * FCI_grad2_par2(f)) + (FCI_div_par(K) * FCI_grad_par(f)) ############################################ # Equations solved input_field = exprToStr(f) -grad_par_solution = exprToStr(FCI_ddy(f)) -grad2_par2_solution = exprToStr(FCI_ddy(FCI_ddy(f))) +grad_par_solution = exprToStr(FCI_grad_par(f)) +grad2_par2_solution = exprToStr(FCI_grad2_par2(f)) div_par_solution = exprToStr(FCI_div_par(f)) +div_par_K_grad_par_solution = exprToStr(FCI_div_par_K_grad_par(f, K)) print(f"input_field = {input_field}") +print(f"K = {K}") print(f"grad_par_solution = {grad_par_solution}") print(f"grad2_par2_solution = {grad2_par2_solution}") print(f"div_par_solution = {div_par_solution}") @@ -51,7 +62,9 @@ def FCI_div_par(f: Expr) -> Expr: options = BoutOptionsFile("data/BOUT.inp") options["input_field"] = input_field +options["K"] = K options["grad_par_solution"] = grad_par_solution options["grad2_par2_solution"] = grad2_par2_solution options["div_par_solution"] = div_par_solution +options["div_par_K_grad_par_solution"] = div_par_K_grad_par_solution options.write("data/BOUT.inp", overwrite=True) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index c98d924e7b..5a19877a45 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -24,7 +24,7 @@ from scipy.interpolate import RectBivariateSpline as RBS DIRECTORY = "data" NPROC = 2 MTHREAD = 2 -OPERATORS = ("grad_par", "grad2_par2", "div_par") +OPERATORS = ("grad_par", "grad2_par2", "div_par", "div_par_K_grad_par") NX = 3 # Resolution in y and z NLIST = [8, 16, 32, 64, 128] @@ -177,7 +177,8 @@ def make_plots(cases): print("No matplotlib") return - fig, axes = plt.subplots(1, len(OPERATORS), figsize=(9, 4)) + num_operators = len(OPERATORS) + fig, axes = plt.subplots(1, num_operators, figsize=(num_operators * 4, 4)) for ax, operator in zip(axes, OPERATORS): for name, case in cases.items(): From 14b4b11a097320b6c936a27fe81b24161cc924ff Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 7 Oct 2025 17:30:48 +0100 Subject: [PATCH 081/461] Many small fixes for FCI - Include all relevant headers, remove unused ones, add some forward declarations - Make data `private` instead of `protected` - Add getter instead of `const` member - Change member reference to pointer - Move ctor to .cxx file - Use `std::array` instead of C array - Bunch of other small clang-tidy fixes --- src/mesh/parallel/fci.cxx | 178 ++++++++++++++++++++++++++------------ src/mesh/parallel/fci.hxx | 83 ++++++------------ 2 files changed, 150 insertions(+), 111 deletions(-) diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index e8d3af1cdb..e44a74966e 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -37,79 +37,99 @@ **************************************************************************/ #include "fci.hxx" + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/field_data.hxx" +#include "bout/mesh.hxx" +#include "bout/msg_stack.hxx" +#include "bout/options.hxx" #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" -#include -#include -#include -#include -#include +#include "bout/paralleltransform.hxx" +#include "bout/region.hxx" + +#include +#include +#include +#include +#include +#include #include +#include + +using namespace std::string_view_literals; -FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& options, - int offset_, const std::shared_ptr& inner_boundary, +FCIMap::FCIMap(Mesh& mesh, [[maybe_unused]] const Coordinates::FieldMetric& dy, + Options& options, int offset, + const std::shared_ptr& inner_boundary, const std::shared_ptr& outer_boundary, bool zperiodic) - : map_mesh(mesh), offset(offset_), - region_no_boundary(map_mesh.getRegion("RGN_NOBNDRY")), + : map_mesh(&mesh), offset_(offset), + region_no_boundary(map_mesh->getRegion("RGN_NOBNDRY")), corner_boundary_mask(map_mesh) { - TRACE("Creating FCIMAP for direction {:d}", offset); + TRACE("Creating FCIMAP for direction {:d}", offset_); - if (offset == 0) { + if (offset_ == 0) { throw BoutException( "FCIMap called with offset = 0; You probably didn't mean to do that"); } auto& interpolation_options = options["xzinterpolation"]; - interp = - XZInterpolationFactory::getInstance().create(&interpolation_options, &map_mesh); - interp->setYOffset(offset); + interp = XZInterpolationFactory::getInstance().create(&interpolation_options, map_mesh); + interp->setYOffset(offset_); interp_corner = - XZInterpolationFactory::getInstance().create(&interpolation_options, &map_mesh); - interp_corner->setYOffset(offset); + XZInterpolationFactory::getInstance().create(&interpolation_options, map_mesh); + interp_corner->setYOffset(offset_); // Index-space coordinates of forward/backward points - Field3D xt_prime{&map_mesh}, zt_prime{&map_mesh}; + Field3D xt_prime{map_mesh}; + Field3D zt_prime{map_mesh}; // Real-space coordinates of grid points - Field3D R{&map_mesh}, Z{&map_mesh}; + Field3D R{map_mesh}; + Field3D Z{map_mesh}; // Real-space coordinates of forward/backward points - Field3D R_prime{&map_mesh}, Z_prime{&map_mesh}; + Field3D R_prime{map_mesh}; + Field3D Z_prime{map_mesh}; - map_mesh.get(R, "R", 0.0, false); - map_mesh.get(Z, "Z", 0.0, false); + map_mesh->get(R, "R", 0.0, false); + map_mesh->get(Z, "Z", 0.0, false); // Get a unique name for a field based on the sign/magnitude of the offset - const auto parallel_slice_field_name = [&](std::string field) -> std::string { - const std::string direction = (offset > 0) ? "forward" : "backward"; + const auto parallel_slice_field_name = [&](std::string_view field) -> std::string { + const auto direction = (offset_ > 0) ? "forward"sv : "backward"sv; // We only have a suffix for parallel slices beyond the first // This is for backwards compatibility - const std::string slice_suffix = - (std::abs(offset) > 1) ? "_" + std::to_string(std::abs(offset)) : ""; - return direction + "_" + field + slice_suffix; + const auto slice_suffix = + (std::abs(offset_) > 1) ? fmt::format("_{}", std::abs(offset_)) : ""; + return fmt::format("{}_{}{}", direction, field, slice_suffix); }; // If we can't read in any of these fields, things will silently not // work, so best throw - if (map_mesh.get(xt_prime, parallel_slice_field_name("xt_prime"), 0.0, false) != 0) { + if (map_mesh->get(xt_prime, parallel_slice_field_name("xt_prime"), 0.0, false) != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", parallel_slice_field_name("xt_prime")); } - if (map_mesh.get(zt_prime, parallel_slice_field_name("zt_prime"), 0.0, false) != 0) { + if (map_mesh->get(zt_prime, parallel_slice_field_name("zt_prime"), 0.0, false) != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", parallel_slice_field_name("zt_prime")); } - if (map_mesh.get(R_prime, parallel_slice_field_name("R"), 0.0, false) != 0) { + if (map_mesh->get(R_prime, parallel_slice_field_name("R"), 0.0, false) != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", parallel_slice_field_name("R")); } - if (map_mesh.get(Z_prime, parallel_slice_field_name("Z"), 0.0, false) != 0) { + if (map_mesh->get(Z_prime, parallel_slice_field_name("Z"), 0.0, false) != 0) { throw BoutException("Could not read {:s} from grid file!\n" " Either add it to the grid file, or reduce MYG", parallel_slice_field_name("Z")); @@ -157,7 +177,7 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& interp->calcWeights(xt_prime, zt_prime); } - const int ncz = map_mesh.LocalNz; + const int ncz = map_mesh->LocalNz; BoutMask to_remove(map_mesh); const int xend = @@ -167,15 +187,16 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& BOUT_FOR_SERIAL(i, xt_prime.getRegion("RGN_NOBNDRY")) { // z is periodic, so make sure the z-index wraps around if (zperiodic) { - zt_prime[i] = zt_prime[i] - - ncz * (static_cast(zt_prime[i] / static_cast(ncz))); + zt_prime[i] = + zt_prime[i] + - (ncz * (static_cast(zt_prime[i] / static_cast(ncz)))); if (zt_prime[i] < 0.0) { zt_prime[i] += ncz; } } - if ((xt_prime[i] >= map_mesh.xstart) and (xt_prime[i] <= xend)) { + if ((xt_prime[i] >= map_mesh->xstart) and (xt_prime[i] <= xend)) { // Not a boundary continue; } @@ -215,7 +236,7 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& const BoutReal dR_dz = 0.5 * (R[i_zp] - R[i_zm]); const BoutReal dZ_dz = 0.5 * (Z[i_zp] - Z[i_zm]); - const BoutReal det = dR_dx * dZ_dz - dR_dz * dZ_dx; // Determinant of 2x2 matrix + const BoutReal det = (dR_dx * dZ_dz) - (dR_dz * dZ_dx); // Determinant of 2x2 matrix const BoutReal dR = R_prime[i] - R[i]; const BoutReal dZ = Z_prime[i] - Z[i]; @@ -228,9 +249,9 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& // outer boundary. However, if any of the surrounding points are negative, // that also means inner. So to differentiate between inner and outer we // need at least 2 points in the domain. - ASSERT2(map_mesh.xend - map_mesh.xstart >= 2); - auto boundary = (xt_prime[i] < map_mesh.xstart) ? inner_boundary : outer_boundary; - boundary->add_point(x, y, z, x + dx, y + 0.5 * offset, + ASSERT2(map_mesh->xend - map_mesh->xstart >= 2); + auto boundary = (xt_prime[i] < map_mesh->xstart) ? inner_boundary : outer_boundary; + boundary->add_point(x, y, z, x + dx, y + (0.5 * offset_), z + dz, // Intersection point in local index space 0.5, // Distance to intersection 1 // Default to that there is a point in the other direction @@ -240,13 +261,14 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& interp->setRegion(region_no_boundary); - const auto region = fmt::format("RGN_YPAR_{:+d}", offset); - if (not map_mesh.hasRegion3D(region)) { + const auto region = fmt::format("RGN_YPAR_{:+d}", offset_); + if (not map_mesh->hasRegion3D(region)) { // The valid region for this slice - map_mesh.addRegion3D( - region, Region(map_mesh.xstart, map_mesh.xend, map_mesh.ystart + offset, - map_mesh.yend + offset, 0, map_mesh.LocalNz - 1, - map_mesh.LocalNy, map_mesh.LocalNz)); + map_mesh->addRegion3D(region, Region(map_mesh->xstart, map_mesh->xend, + map_mesh->ystart + offset_, + map_mesh->yend + offset_, 0, + map_mesh->LocalNz - 1, map_mesh->LocalNy, + map_mesh->LocalNz)); } } @@ -254,7 +276,7 @@ Field3D FCIMap::integrate(Field3D& f) const { TRACE("FCIMap::integrate"); ASSERT1(f.getDirectionY() == YDirectionType::Standard); - ASSERT1(&map_mesh == f.getMesh()); + ASSERT1(map_mesh == f.getMesh()); // Cell centre values Field3D centre = interp->interpolate(f); @@ -269,7 +291,7 @@ Field3D FCIMap::integrate(Field3D& f) const { #endif BOUT_FOR(i, region_no_boundary) { - const auto inext = i.yp(offset); + const auto inext = i.yp(offset_); const BoutReal f_c = centre[inext]; const auto izm = i.zm(); const int x = i.x(); @@ -278,7 +300,7 @@ Field3D FCIMap::integrate(Field3D& f) const { const int zm = izm.z(); if (corner_boundary_mask(x, y, z) || corner_boundary_mask(x - 1, y, z) || corner_boundary_mask(x, y, zm) || corner_boundary_mask(x - 1, y, zm) - || (x == map_mesh.xstart)) { + || (x == map_mesh->xstart)) { // One of the corners leaves the domain. // Use the cell centre value, since boundary conditions are not // currently applied to corners. @@ -299,10 +321,60 @@ Field3D FCIMap::integrate(Field3D& f) const { return result; } +FCITransform::FCITransform(Mesh& mesh, const Coordinates::FieldMetric& dy, bool zperiodic, + Options* opt) + : ParallelTransform(mesh, opt), R{&mesh}, Z{&mesh} { + + // check the coordinate system used for the grid data source + FCITransform::checkInputGrid(); + + // Real-space coordinates of grid cells + mesh.get(R, "R", 0.0, false); + mesh.get(Z, "Z", 0.0, false); + + auto forward_boundary_xin = + std::make_shared("FCI_forward", BNDRY_PAR_FWD_XIN, +1, &mesh); + auto backward_boundary_xin = + std::make_shared("FCI_backward", BNDRY_PAR_BKWD_XIN, -1, &mesh); + auto forward_boundary_xout = + std::make_shared("FCI_forward", BNDRY_PAR_FWD_XOUT, +1, &mesh); + auto backward_boundary_xout = + std::make_shared("FCI_backward", BNDRY_PAR_BKWD_XOUT, -1, &mesh); + + // Add the boundary region to the mesh's vector of parallel boundaries + mesh.addBoundaryPar(forward_boundary_xin, BoundaryParType::xin_fwd); + mesh.addBoundaryPar(backward_boundary_xin, BoundaryParType::xin_bwd); + mesh.addBoundaryPar(forward_boundary_xout, BoundaryParType::xout_fwd); + mesh.addBoundaryPar(backward_boundary_xout, BoundaryParType::xout_bwd); + + field_line_maps.reserve(static_cast(mesh.ystart) * 2); + for (int offset = 1; offset < mesh.ystart + 1; ++offset) { + field_line_maps.emplace_back(mesh, dy, options, offset, forward_boundary_xin, + forward_boundary_xout, zperiodic); + field_line_maps.emplace_back(mesh, dy, options, -offset, backward_boundary_xin, + backward_boundary_xout, zperiodic); + } + ASSERT0(mesh.ystart == 1); + const std::array bndries = {forward_boundary_xin, forward_boundary_xout, + backward_boundary_xin, backward_boundary_xout}; + for (const auto& bndry : bndries) { + for (const auto& bndry2 : bndries) { + if (bndry->dir == bndry2->dir) { + continue; + } + for (bndry->first(); !bndry->isDone(); bndry->next()) { + if (bndry2->contains(*bndry)) { + bndry->setValid(0); + } + } + } + } +} + void FCITransform::checkInputGrid() { std::string parallel_transform; if (mesh.isDataSourceGridFile() - && !mesh.get(parallel_transform, "parallel_transform")) { + && (mesh.get(parallel_transform, "parallel_transform") == 0)) { if (parallel_transform != "fci") { throw BoutException( "Incorrect parallel transform type '" + parallel_transform @@ -310,8 +382,8 @@ void FCITransform::checkInputGrid() { "to generate metric components for FCITransform. Should be 'fci'."); } } // else: parallel_transform variable not found in grid input, indicates older input - // file or grid from options so must rely on the user having ensured the type is - // correct + // file or grid from options so must rely on the user having ensured the type is + // correct } void FCITransform::calcParallelSlices(Field3D& f) { @@ -327,8 +399,8 @@ void FCITransform::calcParallelSlices(Field3D& f) { // Interpolate f onto yup and ydown fields for (const auto& map : field_line_maps) { - f.ynext(map.offset) = map.interpolate(f); - f.ynext(map.offset).setRegion(fmt::format("RGN_YPAR_{:+d}", map.offset)); + f.ynext(map.offset()) = map.interpolate(f); + f.ynext(map.offset()).setRegion(fmt::format("RGN_YPAR_{:+d}", map.offset())); } } @@ -345,7 +417,7 @@ void FCITransform::integrateParallelSlices(Field3D& f) { // Integrate f onto yup and ydown fields for (const auto& map : field_line_maps) { - f.ynext(map.offset) = map.integrate(f); + f.ynext(map.offset()) = map.integrate(f); } } diff --git a/src/mesh/parallel/fci.hxx b/src/mesh/parallel/fci.hxx index 1a02f558e1..65529a4c4e 100644 --- a/src/mesh/parallel/fci.hxx +++ b/src/mesh/parallel/fci.hxx @@ -26,6 +26,11 @@ #ifndef BOUT_FCITRANSFORM_H #define BOUT_FCITRANSFORM_H +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/coordinates.hxx" +#include "bout/region.hxx" #include #include #include @@ -33,25 +38,26 @@ #include #include +#include #include +class BoundaryRegionPar; +class FieldPerp; +class Field2D; +class Field3D; +class Options; + /// Field line map - contains the coefficients for interpolation class FCIMap { /// Interpolation objects std::unique_ptr interp; // Cell centre std::unique_ptr interp_corner; // Cell corner at (x+1, z+1) -public: - FCIMap() = delete; - FCIMap(Mesh& mesh, const Coordinates::FieldMetric& dy, Options& options, int offset, - const std::shared_ptr& inner_boundary, - const std::shared_ptr& outer_boundary, bool zperiodic); - // The mesh this map was created on - Mesh& map_mesh; + Mesh* map_mesh; /// Direction of map - const int offset; + int offset_; /// region containing all points where the field line has not left the /// domain @@ -59,8 +65,17 @@ public: /// If any of the integration area has left the domain BoutMask corner_boundary_mask; +public: + FCIMap() = delete; + FCIMap(Mesh& mesh, const Coordinates::FieldMetric& dy, Options& options, int offset, + const std::shared_ptr& inner_boundary, + const std::shared_ptr& outer_boundary, bool zperiodic); + + /// Direction of map + int offset() const { return offset_; } + Field3D interpolate(Field3D& f) const { - ASSERT1(&map_mesh == f.getMesh()); + ASSERT1(map_mesh == f.getMesh()); return interp->interpolate(f); } @@ -72,55 +87,7 @@ class FCITransform : public ParallelTransform { public: FCITransform() = delete; FCITransform(Mesh& mesh, const Coordinates::FieldMetric& dy, bool zperiodic = true, - Options* opt = nullptr) - : ParallelTransform(mesh, opt), R{&mesh}, Z{&mesh} { - - // check the coordinate system used for the grid data source - FCITransform::checkInputGrid(); - - // Real-space coordinates of grid cells - mesh.get(R, "R", 0.0, false); - mesh.get(Z, "Z", 0.0, false); - - auto forward_boundary_xin = - std::make_shared("FCI_forward", BNDRY_PAR_FWD_XIN, +1, &mesh); - auto backward_boundary_xin = std::make_shared( - "FCI_backward", BNDRY_PAR_BKWD_XIN, -1, &mesh); - auto forward_boundary_xout = - std::make_shared("FCI_forward", BNDRY_PAR_FWD_XOUT, +1, &mesh); - auto backward_boundary_xout = std::make_shared( - "FCI_backward", BNDRY_PAR_BKWD_XOUT, -1, &mesh); - - // Add the boundary region to the mesh's vector of parallel boundaries - mesh.addBoundaryPar(forward_boundary_xin, BoundaryParType::xin_fwd); - mesh.addBoundaryPar(backward_boundary_xin, BoundaryParType::xin_bwd); - mesh.addBoundaryPar(forward_boundary_xout, BoundaryParType::xout_fwd); - mesh.addBoundaryPar(backward_boundary_xout, BoundaryParType::xout_bwd); - - field_line_maps.reserve(mesh.ystart * 2); - for (int offset = 1; offset < mesh.ystart + 1; ++offset) { - field_line_maps.emplace_back(mesh, dy, options, offset, forward_boundary_xin, - forward_boundary_xout, zperiodic); - field_line_maps.emplace_back(mesh, dy, options, -offset, backward_boundary_xin, - backward_boundary_xout, zperiodic); - } - ASSERT0(mesh.ystart == 1); - std::shared_ptr bndries[]{ - forward_boundary_xin, forward_boundary_xout, backward_boundary_xin, - backward_boundary_xout}; - for (auto& bndry : bndries) { - for (const auto& bndry2 : bndries) { - if (bndry->dir == bndry2->dir) { - continue; - } - for (bndry->first(); !bndry->isDone(); bndry->next()) { - if (bndry2->contains(*bndry)) { - bndry->setValid(0); - } - } - } - } - } + Options* opt = nullptr); void calcParallelSlices(Field3D& f) override; From da0d4c4a8df2d98f924c87d7f2ac4af290bfb284 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 8 Oct 2025 10:11:22 +0100 Subject: [PATCH 082/461] tests: Fix for 3D metric in FCI test The Div_par operators use parallel slices of B -- with 2D metrics, these are just the field itself, in 3D we need the actual slices. While `Coordinates::geometry` does communicate the fields, it puts off calculating the parallel slices because that needs the fully constructed `Coordinates`. Upcoming changes should fix this and remove the need to explicitly communicate `Coordinates` members. --- src/mesh/coordinates.cxx | 2 +- tests/MMS/spatial/fci/fci_mms.cxx | 9 ++++++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 3dfee6a553..12e465ffb6 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1577,7 +1577,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, // Need Bxy at location of f, which might be different from location of this // Coordinates object - auto Bxy_floc = f.getCoordinates()->Bxy; + const auto& Bxy_floc = f.getCoordinates()->Bxy; if (!f.hasParallelSlices()) { // No yup/ydown fields. The Grad_par operator will diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index a0b70e9da6..b30ec05e9a 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -1,4 +1,5 @@ #include "bout/bout.hxx" +#include "bout/build_config.hxx" #include "bout/difops.hxx" #include "bout/field3d.hxx" #include "bout/field_factory.hxx" @@ -37,7 +38,13 @@ int main(int argc, char** argv) { Field3D input{FieldFactory::get()->create3D("input_field", Options::getRoot(), mesh)}; Field3D K{FieldFactory::get()->create3D("K", Options::getRoot(), mesh)}; - // Communicate to calculate parallel transform + // Communicate to calculate parallel transform. + if constexpr (bout::build::use_metric_3d) { + // Div_par operators require B parallel slices: + // Coordinates::geometry doesn't ensure this (yet) + auto& Bxy = mesh->getCoordinates()->Bxy; + mesh->communicate(Bxy); + } mesh->communicate(input, K); Options dump; From 2f83692721c64579910175148517b63b9cff7677 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 8 Oct 2025 11:18:59 +0100 Subject: [PATCH 083/461] tests: Add test for FCI `Laplace_par` --- tests/MMS/spatial/fci/data/BOUT.inp | 1 + tests/MMS/spatial/fci/fci_mms.cxx | 1 + tests/MMS/spatial/fci/mms.py | 7 +++++++ tests/MMS/spatial/fci/runtest | 2 +- 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/tests/MMS/spatial/fci/data/BOUT.inp b/tests/MMS/spatial/fci/data/BOUT.inp index e31d84cb35..93e2101473 100644 --- a/tests/MMS/spatial/fci/data/BOUT.inp +++ b/tests/MMS/spatial/fci/data/BOUT.inp @@ -4,6 +4,7 @@ grad2_par2_solution = (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01 div_par_solution = (0.01*x + 0.045)*(-12.5663706143592*cos(y - 2*z) - 6.28318530717959*cos(y - z) + 0.628318530717959*(cos(y - 2*z) + cos(y - z))/(0.01*x + 0.045))/sqrt((0.01*x + 0.045)^2 + 1.0) div_par_K_grad_par_solution = (0.01*x + 0.045)*(6.28318530717959*sin(y - z) - 0.628318530717959*sin(y - z)/(0.01*x + 0.045))*(6.28318530717959*(0.01*x + 0.045)*(-2*cos(y - 2*z) - cos(y - z)) + 0.628318530717959*cos(y - 2*z) + 0.628318530717959*cos(y - z))/((0.01*x + 0.045)^2 + 1.0) + (6.28318530717959*(0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0))*cos(y - z)/sqrt((0.01*x + 0.045)^2 + 1.0) K = cos(y - z) +laplace_par_solution = (0.01*x + 0.045)*(6.28318530717959*(6.28318530717959*(0.01*x + 0.045)*(-4*sin(y - 2*z) - sin(y - z)) + 1.25663706143592*sin(y - 2*z) + 0.628318530717959*sin(y - z))/sqrt((0.01*x + 0.045)^2 + 1.0) + 0.628318530717959*(6.28318530717959*(0.01*x + 0.045)*(2*sin(y - 2*z) + sin(y - z)) - 0.628318530717959*sin(y - 2*z) - 0.628318530717959*sin(y - z))/((0.01*x + 0.045)*sqrt((0.01*x + 0.045)^2 + 1.0)))/sqrt((0.01*x + 0.045)^2 + 1.0) [mesh] symmetricglobalx = true diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index b30ec05e9a..b9d335a3c4 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -55,6 +55,7 @@ int main(int argc, char** argv) { fci_op_test("grad2_par2", dump, input, Grad2_par2(input)); fci_op_test("div_par", dump, input, Div_par(input)); fci_op_test("div_par_K_grad_par", dump, input, Div_par_K_Grad_par(K, input)); + fci_op_test("laplace_par", dump, input, Laplace_par(input)); bout::writeDefaultOutputFile(dump); diff --git a/tests/MMS/spatial/fci/mms.py b/tests/MMS/spatial/fci/mms.py index 79ec661507..178c158572 100755 --- a/tests/MMS/spatial/fci/mms.py +++ b/tests/MMS/spatial/fci/mms.py @@ -44,6 +44,10 @@ def FCI_div_par_K_grad_par(f: Expr, K: Expr) -> Expr: return (K * FCI_grad2_par2(f)) + (FCI_div_par(K) * FCI_grad_par(f)) +def FCI_Laplace_par(f: Expr) -> Expr: + return FCI_div_par(FCI_grad_par(f)) + + ############################################ # Equations solved @@ -52,6 +56,7 @@ def FCI_div_par_K_grad_par(f: Expr, K: Expr) -> Expr: grad2_par2_solution = exprToStr(FCI_grad2_par2(f)) div_par_solution = exprToStr(FCI_div_par(f)) div_par_K_grad_par_solution = exprToStr(FCI_div_par_K_grad_par(f, K)) +Laplace_par_solution = exprToStr(FCI_Laplace_par(f)) print(f"input_field = {input_field}") print(f"K = {K}") @@ -59,6 +64,7 @@ def FCI_div_par_K_grad_par(f: Expr, K: Expr) -> Expr: print(f"grad2_par2_solution = {grad2_par2_solution}") print(f"div_par_solution = {div_par_solution}") print(f"div_par_K_grad_par_solution = {div_par_K_grad_par_solution}") +print(f"laplace_par_solution = {Laplace_par_solution}") options = BoutOptionsFile("data/BOUT.inp") options["input_field"] = input_field @@ -67,4 +73,5 @@ def FCI_div_par_K_grad_par(f: Expr, K: Expr) -> Expr: options["grad2_par2_solution"] = grad2_par2_solution options["div_par_solution"] = div_par_solution options["div_par_K_grad_par_solution"] = div_par_K_grad_par_solution +options["laplace_par_solution"] = Laplace_par_solution options.write("data/BOUT.inp", overwrite=True) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 5a19877a45..612486bdf4 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -24,7 +24,7 @@ from scipy.interpolate import RectBivariateSpline as RBS DIRECTORY = "data" NPROC = 2 MTHREAD = 2 -OPERATORS = ("grad_par", "grad2_par2", "div_par", "div_par_K_grad_par") +OPERATORS = ("grad_par", "grad2_par2", "div_par", "div_par_K_grad_par", "laplace_par") NX = 3 # Resolution in y and z NLIST = [8, 16, 32, 64, 128] From 0cde140be713e61b3ab0636aaa3fb5fccc1218e7 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 8 Oct 2025 11:22:57 +0100 Subject: [PATCH 084/461] Reduce duplication in FCI mms script --- tests/MMS/spatial/fci/mms.py | 36 ++++++++++++++---------------------- 1 file changed, 14 insertions(+), 22 deletions(-) diff --git a/tests/MMS/spatial/fci/mms.py b/tests/MMS/spatial/fci/mms.py index 178c158572..b28e337ac0 100755 --- a/tests/MMS/spatial/fci/mms.py +++ b/tests/MMS/spatial/fci/mms.py @@ -51,27 +51,19 @@ def FCI_Laplace_par(f: Expr) -> Expr: ############################################ # Equations solved -input_field = exprToStr(f) -grad_par_solution = exprToStr(FCI_grad_par(f)) -grad2_par2_solution = exprToStr(FCI_grad2_par2(f)) -div_par_solution = exprToStr(FCI_div_par(f)) -div_par_K_grad_par_solution = exprToStr(FCI_div_par_K_grad_par(f, K)) -Laplace_par_solution = exprToStr(FCI_Laplace_par(f)) - -print(f"input_field = {input_field}") -print(f"K = {K}") -print(f"grad_par_solution = {grad_par_solution}") -print(f"grad2_par2_solution = {grad2_par2_solution}") -print(f"div_par_solution = {div_par_solution}") -print(f"div_par_K_grad_par_solution = {div_par_K_grad_par_solution}") -print(f"laplace_par_solution = {Laplace_par_solution}") - options = BoutOptionsFile("data/BOUT.inp") -options["input_field"] = input_field -options["K"] = K -options["grad_par_solution"] = grad_par_solution -options["grad2_par2_solution"] = grad2_par2_solution -options["div_par_solution"] = div_par_solution -options["div_par_K_grad_par_solution"] = div_par_K_grad_par_solution -options["laplace_par_solution"] = Laplace_par_solution + +for name, expr in ( + ("input_field", f), + ("K", K), + ("grad_par_solution", FCI_grad_par(f)), + ("grad2_par2_solution", FCI_grad2_par2(f)), + ("div_par_solution", FCI_div_par(f)), + ("div_par_K_grad_par_solution", FCI_div_par_K_grad_par(f, K)), + ("laplace_par_solution", FCI_Laplace_par(f)), +): + expr_str = exprToStr(expr) + print(f"{name} = {expr_str}") + options[name] = expr_str + options.write("data/BOUT.inp", overwrite=True) From 37dc27341019c5bf962269e4f015a777014535c4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 8 Oct 2025 11:25:56 +0100 Subject: [PATCH 085/461] Remove circular include --- include/bout/difops.hxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index 71053d454a..c2fdac195d 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -40,7 +40,9 @@ #include "bout/field3d.hxx" #include "bout/bout_types.hxx" -#include "bout/solver.hxx" +#include "bout/coordinates.hxx" + +class Solver; /*! * Parallel derivative (central differencing) in Y From 9ac46e845b65adb16da3f6e2240bc6cb177636c4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 8 Oct 2025 16:48:20 +0100 Subject: [PATCH 086/461] tests: Add cases for FCI interpolation methods --- tests/MMS/spatial/fci/runtest | 44 +++++++++++++++-------------------- 1 file changed, 19 insertions(+), 25 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 612486bdf4..6ac0b0bca8 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -99,7 +99,7 @@ def run_fci_operators( ) # Command to run - args = f"MZ={nz} MYG={nslice} mesh:paralleltransform:y_periodic={yperiodic} mesh:ddy:first={name}" + args = f"MZ={nz} MYG={nslice} mesh:paralleltransform:y_periodic={yperiodic} {name}" cmd = f"./fci_mms {args}" print(f"Running command: {cmd}", end="") @@ -138,18 +138,18 @@ def transpose( return result -def check_fci_operators(case: dict) -> bool: +def check_fci_operators(name: str, case: dict) -> bool: failures = [] nslice = case["nslice"] yperiodic = case["yperiodic"] - name = case["name"] order = case["order"] + args = case["args"] all_errors = [] for n in NLIST: - errors = run_fci_operators(nslice, n, yperiodic, name) + errors = run_fci_operators(nslice, n, yperiodic, args) all_errors.append(errors) for operator in OPERATORS: @@ -170,7 +170,7 @@ def check_fci_operators(case: dict) -> bool: return final_errors, failures -def make_plots(cases): +def make_plots(cases: dict[str, dict]): try: import matplotlib.pyplot as plt except ImportError: @@ -199,23 +199,6 @@ def make_plots(cases): plt.close() -def make_case(nslice: int, yperiodic: bool) -> dict[str, Any]: - """ - nslice: - Number of parallel slices (in each direction) - yperiodic: - Run with periodic Y - """ - order = nslice * 2 - return { - "nslice": nslice, - # Which central difference scheme to use and its expected order - "order": order, - "name": f"C{order}", - "yperiodic": yperiodic, - } - - if __name__ == "__main__": build_and_log("FCI MMS test") @@ -240,11 +223,22 @@ if __name__ == "__main__": success = True failures = [] cases = { - "nslice=1": make_case(nslice=1, yperiodic=True), + "nslice=1 hermitespline": { + "nslice": 1, + "order": 2, + "yperiodic": True, + "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=hermitespline", + }, + "nslice=1 lagrange4pt": { + "nslice": 1, + "order": 2, + "yperiodic": True, + "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=lagrange4pt", + }, } - for case in cases.values(): - error2, failures_ = check_fci_operators(case) + for name, case in cases.items(): + error2, failures_ = check_fci_operators(name, case) case.update(error2) failures.extend(failures_) success &= len(failures) == 0 From 5974a6e89d457a8e6cbcdad177837aeda924ad50 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 9 Oct 2025 17:23:45 +0100 Subject: [PATCH 087/461] tests: Increase nx for hermitespline interpolation boundary problem --- src/mesh/interpolation/hermite_spline_xz.cxx | 14 +++++++++++++- tests/MMS/spatial/fci/runtest | 4 +++- 2 files changed, 16 insertions(+), 2 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 5020a5b9a3..27a4f1d614 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -171,7 +171,19 @@ void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z BoutReal t_x = delta_x(x, y, z) - static_cast(i_corn); BoutReal t_z = delta_z(x, y, z) - static_cast(k_corner(x, y, z)); - // NOTE: A (small) hack to avoid one-sided differences + // NOTE: A (small) hack to avoid one-sided differences. We need at + // least 2 interior points due to an awkwardness with the + // boundaries. The splines need derivatives in x, but we don't + // know the value in the boundaries, so _any_ interpolation in the + // last interior cell can't be done. Instead, we fudge the + // interpolation in the last cell to be at the extreme right-hand + // edge of the previous cell (that is, exactly on the last + // interior point). However, this doesn't work with only one + // interior point, because we have to do something similar to the + // _first_ cell, and these two fudges cancel out and we end up + // indexing into the boundary anyway. + // TODO(peter): Can we remove this if we apply (dirichlet?) BCs to + // the X derivatives? Note that we need at least _2_ if (i_corn >= xend) { i_corn = xend - 1; t_x = 1.0; diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 6ac0b0bca8..5393437ff7 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -25,7 +25,9 @@ DIRECTORY = "data" NPROC = 2 MTHREAD = 2 OPERATORS = ("grad_par", "grad2_par2", "div_par", "div_par_K_grad_par", "laplace_par") -NX = 3 +# Note that we need at least _2_ interior points for hermite spline +# interpolation due to an awkwardness with the boundaries +NX = 4 # Resolution in y and z NLIST = [8, 16, 32, 64, 128] dx = 1.0 / array(NLIST) From 6ab945e6783b754acf6e84d4fbefcfcd16d94c6f Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 9 Oct 2025 17:24:28 +0100 Subject: [PATCH 088/461] tests: Add monotonichermitespline, decrease resolution scan --- tests/MMS/spatial/fci/runtest | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 5393437ff7..e3d10d989b 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -11,7 +11,6 @@ import json import pathlib import sys from time import time -from typing import Any import boutconfig as conf import zoidberg as zb @@ -29,7 +28,7 @@ OPERATORS = ("grad_par", "grad2_par2", "div_par", "div_par_K_grad_par", "laplace # interpolation due to an awkwardness with the boundaries NX = 4 # Resolution in y and z -NLIST = [8, 16, 32, 64, 128] +NLIST = [8, 16, 32, 64] dx = 1.0 / array(NLIST) @@ -237,6 +236,12 @@ if __name__ == "__main__": "yperiodic": True, "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=lagrange4pt", }, + "nslice=1 monotonichermitespline": { + "nslice": 1, + "order": 2, + "yperiodic": True, + "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=monotonichermitespline", + }, } for name, case in cases.items(): From 16c3718187378e28c37c5c8304e2d9a5e326d794 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 3 Nov 2025 15:52:25 +0000 Subject: [PATCH 089/461] tests: Small refactor of FCI tests --- .../test-fci-boundary/get_par_bndry.cxx | 22 ++-- tests/integrated/test-fci-boundary/runtest | 36 ++---- tests/integrated/test-fci-mpi/fci_mpi.cxx | 59 +++++----- tests/integrated/test-fci-mpi/runtest | 107 +++++++++++------- 4 files changed, 116 insertions(+), 108 deletions(-) diff --git a/tests/integrated/test-fci-boundary/get_par_bndry.cxx b/tests/integrated/test-fci-boundary/get_par_bndry.cxx index ac0f5de2a6..ba282d8988 100644 --- a/tests/integrated/test-fci-boundary/get_par_bndry.cxx +++ b/tests/integrated/test-fci-boundary/get_par_bndry.cxx @@ -1,5 +1,5 @@ #include "bout/bout.hxx" -#include "bout/derivs.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" #include "bout/parallel_boundary_region.hxx" @@ -8,24 +8,24 @@ int main(int argc, char** argv) { using bout::globals::mesh; - std::vector fields; - fields.resize(static_cast(BoundaryParType::SIZE)); + std::vector fields (static_cast(BoundaryParType::SIZE), Field3D{0.0}); + Options dump; for (int i = 0; i < fields.size(); i++) { - fields[i] = Field3D{0.0}; + fields[i].allocate(); + const auto boundary = static_cast(i); + const auto boundary_name = toString(boundary); mesh->communicate(fields[i]); - for (const auto& bndry_par : - mesh->getBoundariesPar(static_cast(i))) { - output.write("{:s} region\n", toString(static_cast(i))); + for (const auto& bndry_par : mesh->getBoundariesPar(boundary)) { + output.write("{:s} region\n", boundary_name); for (bndry_par->first(); !bndry_par->isDone(); bndry_par->next()) { fields[i][bndry_par->ind()] += 1; - output.write("{:s} increment\n", toString(static_cast(i))); + output.write("{:s} increment\n", boundary_name); } } - output.write("{:s} done\n", toString(static_cast(i))); + output.write("{:s} done\n", boundary_name); - dump[fmt::format("field_{:s}", toString(static_cast(i)))] = - fields[i]; + dump[fmt::format("field_{:s}", boundary_name)] = fields[i]; } bout::writeDefaultOutputFile(dump); diff --git a/tests/integrated/test-fci-boundary/runtest b/tests/integrated/test-fci-boundary/runtest index 1b1460da53..e749055185 100755 --- a/tests/integrated/test-fci-boundary/runtest +++ b/tests/integrated/test-fci-boundary/runtest @@ -1,29 +1,15 @@ #!/usr/bin/env python3 # # Python script to run and analyse MMS test -# -# Cores: 2 -# only working with cmake -# requires: False from boututils.run_wrapper import launch_safe from boututils.datafile import DataFile -from boutdata.collect import collect as _collect +from boutdata.collect import collect import numpy as np -def collect(var): - return _collect( - var, - info=False, - path=directory, - xguards=False, - yguards=False, - ) - - -nprocs = [1] # , 2, 4] +nprocs = [1] mthread = 2 directory = "data" @@ -43,11 +29,6 @@ regions = { } regions = {k: v.astype(int) for k, v in regions.items()} -# for x in "xout", "xin": -# regions[x] = np.logical_or(regions[f"{x}_fwd"], regions[f"{x}_bwd"]) -# for x in "fwd", "bwd": -# regions[x] = np.logical_or(regions[f"xin_{x}"], regions[f"xout_{x}"]) -# regions["all"] = np.logical_or(regions["xin"], regions["xout"]) for x in "xout", "xin": regions[x] = regions[f"{x}_fwd"] + regions[f"{x}_bwd"] for x in "fwd", "bwd": @@ -56,15 +37,18 @@ regions["all"] = regions["xin"] + regions["xout"] for nproc in nprocs: cmd = "./get_par_bndry" - - # Launch using MPI _, out = launch_safe(cmd, nproc=nproc, mthread=mthread, pipe=True) for k, v in regions.items(): - # Collect data - data = collect(f"field_{k}") + data = collect( + f"field_{k}", + info=False, + path=directory, + xguards=False, + yguards=False, + ) assert np.allclose(data, v), ( - k + " does not match", + f"{k} does not match", np.sum(data), np.sum(v), np.max(data), diff --git a/tests/integrated/test-fci-mpi/fci_mpi.cxx b/tests/integrated/test-fci-mpi/fci_mpi.cxx index 94520dd4a6..cc4fba8ffe 100644 --- a/tests/integrated/test-fci-mpi/fci_mpi.cxx +++ b/tests/integrated/test-fci-mpi/fci_mpi.cxx @@ -1,38 +1,37 @@ +#include "fmt/format.h" #include "bout/bout.hxx" -#include "bout/derivs.hxx" #include "bout/field_factory.hxx" +namespace { +auto fci_mpi_test(int num, Options& dump) { + using bout::globals::mesh; + Field3D input{FieldFactory::get()->create3D(fmt::format("input_{:d}:function", num), + Options::getRoot(), mesh)}; + mesh->communicate(input); + + input.applyParallelBoundary("parallel_neumann_o2"); + + for (int slice = -mesh->ystart; slice <= mesh->ystart; ++slice) { + if (slice == 0) { + continue; + } + Field3D tmp{0.}; + BOUT_FOR(i, tmp.getRegion("RGN_NOBNDRY")) { + tmp[i] = input.ynext(slice)[i.yp(slice)]; + } + dump[fmt::format("output_{:d}_{:+d}", num, slice)] = tmp; + } +} +} // namespace + int main(int argc, char** argv) { BoutInitialise(argc, argv); - { - using bout::globals::mesh; - Options* options = Options::getRoot(); - int i = 0; - const std::string default_str{"not_set"}; - Options dump; - while (true) { - std::string temp_str; - options->get(fmt::format("input_{:d}:function", i), temp_str, default_str); - if (temp_str == default_str) { - break; - } - Field3D input{FieldFactory::get()->create3D(fmt::format("input_{:d}:function", i), - Options::getRoot(), mesh)}; - // options->get(fmt::format("input_{:d}:boundary_perp", i), temp_str, s"free_o3"); - mesh->communicate(input); - input.applyParallelBoundary("parallel_neumann_o2"); - for (int slice = -mesh->ystart; slice <= mesh->ystart; ++slice) { - if (slice != 0) { - Field3D tmp{0.}; - BOUT_FOR(i, tmp.getRegion("RGN_NOBNDRY")) { - tmp[i] = input.ynext(slice)[i.yp(slice)]; - } - dump[fmt::format("output_{:d}_{:+d}", i, slice)] = tmp; - } - } - ++i; - } - bout::writeDefaultOutputFile(dump); + Options dump; + + for (auto num : {0, 1, 2, 3}) { + fci_mpi_test(num, dump); } + + bout::writeDefaultOutputFile(dump); BoutFinalise(); } diff --git a/tests/integrated/test-fci-mpi/runtest b/tests/integrated/test-fci-mpi/runtest index 6676f8f7a5..828d8d4a50 100755 --- a/tests/integrated/test-fci-mpi/runtest +++ b/tests/integrated/test-fci-mpi/runtest @@ -1,57 +1,82 @@ #!/usr/bin/env python3 # # Python script to run and analyse MMS test -# - -# Cores: 8 -# requires: metric_3d -from boututils.run_wrapper import build_and_log, launch_safe, shell_safe +from boututils.run_wrapper import build_and_log, launch_safe from boutdata.collect import collect -import boutconfig as conf import itertools +import sys -import numpy as np +import numpy.testing as npt # Resolution in x and y -nlist = [1, 2, 4] +NLIST = [1, 2, 4] +MAXCORES = 8 +NSLICES = [1] -maxcores = 8 +build_and_log("FCI MMS test") -nslices = [1] +COLLECT_KW = dict(info=False, xguards=False, yguards=False, path="data") -success = True -build_and_log("FCI MMS test") +def run_case(nxpe: int, nype: int, mthread: int): + cmd = f"./fci_mpi NXPE={nxpe} NYPE={nype}" + print(f"Running command: {cmd}") + + _, out = launch_safe(cmd, nproc=nxpe * nype, mthread=mthread, pipe=True) + + # Save output to log file + with open(f"run.log.{nxpe}.{nype}.{nslice}.log", "w") as f: + f.write(out) + + +def test_case(nxpe: int, nype: int, mthread: int, ref: dict) -> bool: + run_case(nxpe, nype, mthread) + + failures = [] + + for name, val in ref.items(): + try: + npt.assert_allclose(val, collect(name, **COLLECT_KW)) + except AssertionError as e: + failures.append((nxpe, nype, name, e)) -for nslice in nslices: - for NXPE, NYPE in itertools.product(nlist, nlist): - if NXPE * NYPE > maxcores: + return failures + + +failures = [] + +for nslice in NSLICES: + # reference data! + run_case(1, 1, MAXCORES) + + ref = {} + for i in range(4): + for yp in range(1, nslice + 1): + for y in [-yp, yp]: + name = f"output_{i}_{y:+d}" + ref[name] = collect(name, **COLLECT_KW) + + for nxpe, nype in itertools.product(NLIST, NLIST): + if (nxpe, nype) == (1, 1): + # reference case, done above continue - args = f"NXPE={NXPE} NYPE={NYPE}" - # Command to run - cmd = f"./fci_mpi {args}" - - print(f"Running command: {cmd}") - - mthread = maxcores // (NXPE * NYPE) - # Launch using MPI - _, out = launch_safe(cmd, nproc=NXPE * NYPE, mthread=mthread, pipe=True) - - # Save output to log file - with open(f"run.log.{NXPE}.{NYPE}.{nslice}.log", "w") as f: - f.write(out) - - collect_kw = dict(info=False, xguards=False, yguards=False, path="data") - if NXPE == NYPE == 1: - # reference data! - ref = {} - for i in range(4): - for yp in range(1, nslice + 1): - for y in [-yp, yp]: - name = f"output_{i}_{y:+d}" - ref[name] = collect(name, **collect_kw) - else: - for name, val in ref.items(): - assert np.allclose(val, collect(name, **collect_kw)) + if nxpe * nype > MAXCORES: + continue + + mthread = MAXCORES // (nxpe * nype) + failures_ = test_case(nxpe, nype, mthread, ref) + failures.extend(failures_) + + +success = len(failures) == 0 +if success: + print("\nAll tests passed") +else: + print("\nSome tests failed:") + for (nxpe, nype, name, error) in failures: + print("----------") + print(f"case {nxpe=} {nype=} {name=}\n{error}") + +sys.exit(0 if success else 1) From 3d3ff654b60308e249ae8410a849bfec728f26f1 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Mon, 3 Nov 2025 17:05:43 +0000 Subject: [PATCH 090/461] Apply black changes --- tests/integrated/test-fci-mpi/runtest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrated/test-fci-mpi/runtest b/tests/integrated/test-fci-mpi/runtest index 828d8d4a50..c18ab0391d 100755 --- a/tests/integrated/test-fci-mpi/runtest +++ b/tests/integrated/test-fci-mpi/runtest @@ -75,7 +75,7 @@ if success: print("\nAll tests passed") else: print("\nSome tests failed:") - for (nxpe, nype, name, error) in failures: + for nxpe, nype, name, error in failures: print("----------") print(f"case {nxpe=} {nype=} {name=}\n{error}") From 79f9d0fe70c00bc578404351e8379f1fa79a3dc0 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Tue, 4 Nov 2025 11:58:36 +0000 Subject: [PATCH 091/461] Apply clang-format changes --- src/mesh/parallel/fci.cxx | 4 ++-- tests/integrated/test-fci-boundary/get_par_bndry.cxx | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index e44a74966e..7c7ade1c8d 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -180,8 +180,8 @@ FCIMap::FCIMap(Mesh& mesh, [[maybe_unused]] const Coordinates::FieldMetric& dy, const int ncz = map_mesh->LocalNz; BoutMask to_remove(map_mesh); - const int xend = - map_mesh.xstart + (map_mesh.xend - map_mesh.xstart + 1) * map_mesh.getNXPE() - 1; + const int xend = map_mesh->xstart + + ((map_mesh->xend - map_mesh->xstart + 1) * map_mesh->getNXPE()) - 1; // Serial loop because call to BoundaryRegionPar::addPoint // (probably?) can't be done in parallel BOUT_FOR_SERIAL(i, xt_prime.getRegion("RGN_NOBNDRY")) { diff --git a/tests/integrated/test-fci-boundary/get_par_bndry.cxx b/tests/integrated/test-fci-boundary/get_par_bndry.cxx index ba282d8988..8183d989d1 100644 --- a/tests/integrated/test-fci-boundary/get_par_bndry.cxx +++ b/tests/integrated/test-fci-boundary/get_par_bndry.cxx @@ -8,7 +8,7 @@ int main(int argc, char** argv) { using bout::globals::mesh; - std::vector fields (static_cast(BoundaryParType::SIZE), Field3D{0.0}); + std::vector fields(static_cast(BoundaryParType::SIZE), Field3D{0.0}); Options dump; for (int i = 0; i < fields.size(); i++) { From cee3e445cb7da3d7ce24cbae32cd5ccf53a9509c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 4 Nov 2025 16:13:29 +0000 Subject: [PATCH 092/461] Apply clang-tidy fixes to FCI tests --- tests/MMS/spatial/fci/fci_mms.cxx | 13 ++++++++++--- .../integrated/test-fci-boundary/get_par_bndry.cxx | 8 ++++++++ tests/integrated/test-fci-mpi/fci_mpi.cxx | 6 +++++- 3 files changed, 23 insertions(+), 4 deletions(-) diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index b9d335a3c4..7967452f3d 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -1,19 +1,26 @@ #include "bout/bout.hxx" #include "bout/build_config.hxx" #include "bout/difops.hxx" +#include "bout/field.hxx" #include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" #include "bout/options.hxx" +#include "bout/options_io.hxx" +#include "bout/utils.hxx" +#include + +#include #include namespace { auto fci_op_test(const std::string& name, Options& dump, const Field3D& input, const Field3D& result) { auto* mesh = input.getMesh(); - Field3D solution{FieldFactory::get()->create3D(fmt::format("{}_solution", name), - Options::getRoot(), mesh)}; - Field3D error{result - solution}; + const Field3D solution{FieldFactory::get()->create3D(fmt::format("{}_solution", name), + Options::getRoot(), mesh)}; + const Field3D error{result - solution}; dump[fmt::format("{}_l_2", name)] = sqrt(mean(SQ(error), true, "RGN_NOBNDRY")); dump[fmt::format("{}_l_inf", name)] = max(abs(error), true, "RGN_NOBNDRY"); diff --git a/tests/integrated/test-fci-boundary/get_par_bndry.cxx b/tests/integrated/test-fci-boundary/get_par_bndry.cxx index 8183d989d1..8e3cfac2f7 100644 --- a/tests/integrated/test-fci-boundary/get_par_bndry.cxx +++ b/tests/integrated/test-fci-boundary/get_par_bndry.cxx @@ -1,8 +1,16 @@ #include "bout/bout.hxx" #include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" +#include "bout/options.hxx" +#include "bout/options_io.hxx" +#include "bout/output.hxx" #include "bout/parallel_boundary_region.hxx" +#include + +#include + int main(int argc, char** argv) { BoutInitialise(argc, argv); diff --git a/tests/integrated/test-fci-mpi/fci_mpi.cxx b/tests/integrated/test-fci-mpi/fci_mpi.cxx index cc4fba8ffe..94e8e878ef 100644 --- a/tests/integrated/test-fci-mpi/fci_mpi.cxx +++ b/tests/integrated/test-fci-mpi/fci_mpi.cxx @@ -1,6 +1,11 @@ #include "fmt/format.h" #include "bout/bout.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" +#include "bout/options.hxx" +#include "bout/options_io.hxx" +#include "bout/region.hxx" namespace { auto fci_mpi_test(int num, Options& dump) { @@ -8,7 +13,6 @@ auto fci_mpi_test(int num, Options& dump) { Field3D input{FieldFactory::get()->create3D(fmt::format("input_{:d}:function", num), Options::getRoot(), mesh)}; mesh->communicate(input); - input.applyParallelBoundary("parallel_neumann_o2"); for (int slice = -mesh->ystart; slice <= mesh->ystart; ++slice) { From 88d8db852528f91dffe9d16e93cc3f107baadebb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 4 Nov 2025 16:14:55 +0000 Subject: [PATCH 093/461] Fix comment formatting --- src/mesh/parallel/fci.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index 7c7ade1c8d..055e239725 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -381,7 +381,8 @@ void FCITransform::checkInputGrid() { + "' used " "to generate metric components for FCITransform. Should be 'fci'."); } - } // else: parallel_transform variable not found in grid input, indicates older input + } + // else: parallel_transform variable not found in grid input, indicates older input // file or grid from options so must rely on the user having ensured the type is // correct } From 9d9922c522a1fbe377377e9498ea839b9f7ce82d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 6 Nov 2025 14:27:05 +0000 Subject: [PATCH 094/461] Apply suggestion from @dschwoerer Co-authored-by: David Bold --- src/mesh/parallel/fci.cxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index 055e239725..6243bbb67a 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -107,9 +107,10 @@ FCIMap::FCIMap(Mesh& mesh, [[maybe_unused]] const Coordinates::FieldMetric& dy, const auto direction = (offset_ > 0) ? "forward"sv : "backward"sv; // We only have a suffix for parallel slices beyond the first // This is for backwards compatibility - const auto slice_suffix = - (std::abs(offset_) > 1) ? fmt::format("_{}", std::abs(offset_)) : ""; - return fmt::format("{}_{}{}", direction, field, slice_suffix); + if (std::abs(offset_) == 1) { + return fmt::format("{}_{}", direction, field); + } + return fmt::format("{}_{}_{}", direction, field, std::abs(offset_)); }; // If we can't read in any of these fields, things will silently not From e961e75696aa7fcaf194c6994732725df9bc900c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 6 Nov 2025 17:13:18 +0000 Subject: [PATCH 095/461] tests: Fix typo that made tests always pass --- tests/MMS/spatial/fci/runtest | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index e3d10d989b..b1321c0c5d 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -55,7 +55,7 @@ def quiet_collect(name: str) -> float: )[()] -def assert_convergence(error, dx, name, order) -> bool: +def assert_convergence(error, dx, name, expected) -> bool: fit = polyfit(log(dx), log(error), 1) order = fit[0] print(f"{name} convergence order = {order:f} (fit)", end="") @@ -64,7 +64,7 @@ def assert_convergence(error, dx, name, order) -> bool: print(f", {order:f} (small spacing)", end="") # Should be close to the expected order - success = order > order * 0.95 + success = order > expected * 0.95 print(f"\t............ {'PASS' if success else 'FAIL'}") return success From 2674a58d4e77fda0db5bc213164b245c2854dc8b Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 6 Nov 2025 17:28:58 +0000 Subject: [PATCH 096/461] tests: Remove monotonichermitespline FCI case --- tests/MMS/spatial/fci/runtest | 6 ------ tests/integrated/test-interpolate/runtest | 1 + 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index b1321c0c5d..2bfde26f51 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -236,12 +236,6 @@ if __name__ == "__main__": "yperiodic": True, "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=lagrange4pt", }, - "nslice=1 monotonichermitespline": { - "nslice": 1, - "order": 2, - "yperiodic": True, - "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=monotonichermitespline", - }, } for name, case in cases.items(): diff --git a/tests/integrated/test-interpolate/runtest b/tests/integrated/test-interpolate/runtest index f5460aff2a..da10472610 100755 --- a/tests/integrated/test-interpolate/runtest +++ b/tests/integrated/test-interpolate/runtest @@ -25,6 +25,7 @@ methods = { "hermitespline": 3, "lagrange4pt": 3, "bilinear": 2, + "monotonichermitespline": 2, } From f00db393cf180eba7a44136ada78483e30f965eb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 21 Nov 2025 15:23:30 +0000 Subject: [PATCH 097/461] Add `rtol`, `atol` to `MonotonicHermiteSpline` This allows the user to control the clipping, allowing some overshoot Also restore the FCI MMS test with this interpolator --- include/bout/interpolation_xz.hxx | 40 +++++++++++++++---- .../monotonic_hermite_spline_xz.cxx | 13 +++--- tests/MMS/spatial/fci/runtest | 11 +++++ tests/integrated/test-interpolate/runtest | 1 - 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 6c7419f7e4..d12eecc3d3 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -24,6 +24,7 @@ #ifndef BOUT_INTERP_XZ_H #define BOUT_INTERP_XZ_H +#include "bout/bout_types.hxx" #include "bout/mask.hxx" #define USE_NEW_WEIGHTS 1 @@ -166,7 +167,8 @@ protected: #endif public: - XZHermiteSpline(Mesh* mesh = nullptr) : XZHermiteSpline(0, mesh) {} + XZHermiteSpline(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZHermiteSpline(0, mesh) {} XZHermiteSpline(int y_offset = 0, Mesh* mesh = nullptr); XZHermiteSpline(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) : XZHermiteSpline(y_offset, mesh) { @@ -210,9 +212,29 @@ public: /// but also degrades accuracy near maxima and minima. /// Perhaps should only impose near boundaries, since that is where /// problems most obviously occur. +/// +/// You can control how tight the clipping to the range of the neighbouring cell +/// values through ``rtol`` and ``atol``: +/// +/// diff = (max_of_neighours - min_of_neighours) * rtol + atol +/// +/// and the interpolated value is instead clipped to the range +/// ``[min_of_neighours - diff, max_of_neighours + diff]`` class XZMonotonicHermiteSpline : public XZHermiteSpline { + /// Absolute tolerance for clipping + BoutReal atol = 0.0; + /// Relative tolerance for clipping + BoutReal rtol = 1.0; + public: - XZMonotonicHermiteSpline(Mesh* mesh = nullptr) : XZHermiteSpline(0, mesh) { + XZMonotonicHermiteSpline(Mesh* mesh = nullptr, Options* options = nullptr) + : XZHermiteSpline(0, mesh), + atol{(*options)["atol"] + .doc("Absolute tolerance for clipping overshoot") + .withDefault(0.0)}, + rtol{(*options)["rtol"] + .doc("Relative tolerance for clipping overshoot") + .withDefault(1.0)} { if (localmesh->getNXPE() > 1) { throw BoutException("Do not support MPI splitting in X"); } @@ -248,7 +270,8 @@ class XZLagrange4pt : public XZInterpolation { Field3D t_x, t_z; public: - XZLagrange4pt(Mesh* mesh = nullptr) : XZLagrange4pt(0, mesh) {} + XZLagrange4pt(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZLagrange4pt(0, mesh) {} XZLagrange4pt(int y_offset = 0, Mesh* mesh = nullptr); XZLagrange4pt(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) : XZLagrange4pt(y_offset, mesh) { @@ -284,7 +307,8 @@ class XZBilinear : public XZInterpolation { Field3D w0, w1, w2, w3; public: - XZBilinear(Mesh* mesh = nullptr) : XZBilinear(0, mesh) {} + XZBilinear(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZBilinear(0, mesh) {} XZBilinear(int y_offset = 0, Mesh* mesh = nullptr); XZBilinear(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) : XZBilinear(y_offset, mesh) { @@ -308,7 +332,7 @@ public: }; class XZInterpolationFactory - : public Factory { + : public Factory { public: static constexpr auto type_name = "XZInterpolation"; static constexpr auto section_name = "xzinterpolation"; @@ -316,10 +340,10 @@ public: static constexpr auto default_type = "hermitespline"; ReturnType create(Options* options = nullptr, Mesh* mesh = nullptr) const { - return Factory::create(getType(options), mesh); + return Factory::create(getType(options), mesh, options); } - ReturnType create(const std::string& type, [[maybe_unused]] Options* options) const { - return Factory::create(type, nullptr); + ReturnType create(const std::string& type, Options* options) const { + return Factory::create(type, nullptr, options); } static void ensureRegistered(); diff --git a/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx b/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx index f23bfd499e..f206ed1e0f 100644 --- a/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx +++ b/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx @@ -25,7 +25,7 @@ #include "bout/interpolation_xz.hxx" #include "bout/mesh.hxx" -#include +#include Field3D XZMonotonicHermiteSpline::interpolate(const Field3D& f, const std::string& region) const { @@ -80,7 +80,6 @@ Field3D XZMonotonicHermiteSpline::interpolate(const Field3D& f, // Perhaps should only impose near boundaries, since that is where // problems most obviously occur. const BoutReal localmax = BOUTMAX(f[ic], f[icxp], f[iczp], f[icxpzp]); - const BoutReal localmin = BOUTMIN(f[ic], f[icxp], f[iczp], f[icxpzp]); ASSERT2(std::isfinite(localmax) || i.x() < localmesh->xstart @@ -88,12 +87,10 @@ Field3D XZMonotonicHermiteSpline::interpolate(const Field3D& f, ASSERT2(std::isfinite(localmin) || i.x() < localmesh->xstart || i.x() > localmesh->xend); - if (result > localmax) { - result = localmax; - } - if (result < localmin) { - result = localmin; - } + const auto diff = ((localmax - localmin) * rtol) + atol; + + result = std::min(result, localmax + diff); + result = std::max(result, localmin - diff); f_interp[iyp] = result; } diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 2bfde26f51..34340e53f4 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -236,6 +236,17 @@ if __name__ == "__main__": "yperiodic": True, "args": "mesh:ddy:first=C2 mesh:paralleltransform:xzinterpolation:type=lagrange4pt", }, + "nslice=1 monotonichermitespline": { + "nslice": 1, + "order": 2, + "yperiodic": True, + "args": ( + "mesh:ddy:first=C2 " + "mesh:paralleltransform:xzinterpolation:type=monotonichermitespline " + "mesh:paralleltransform:xzinterpolation:rtol=1e-3 " + "mesh:paralleltransform:xzinterpolation:atol=5e-3" + ), + }, } for name, case in cases.items(): diff --git a/tests/integrated/test-interpolate/runtest b/tests/integrated/test-interpolate/runtest index da10472610..f5460aff2a 100755 --- a/tests/integrated/test-interpolate/runtest +++ b/tests/integrated/test-interpolate/runtest @@ -25,7 +25,6 @@ methods = { "hermitespline": 3, "lagrange4pt": 3, "bilinear": 2, - "monotonichermitespline": 2, } From 0efe4e3d68486f8a7282ce3d6d695876867ca3c9 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 3 Dec 2025 14:34:36 +0000 Subject: [PATCH 098/461] Add missing header --- include/bout/interpolation_xz.hxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index d12eecc3d3..4dd24259fd 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -24,8 +24,9 @@ #ifndef BOUT_INTERP_XZ_H #define BOUT_INTERP_XZ_H -#include "bout/bout_types.hxx" -#include "bout/mask.hxx" +#include +#include +#include #define USE_NEW_WEIGHTS 1 #if BOUT_HAS_PETSC From af3cbd1e6108aea30fdee462ea64c0521ca0ad12 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 4 Dec 2025 16:28:01 +0000 Subject: [PATCH 099/461] Forward arguments to `BoutException` and `Output::write` This enables passing views like `fmt::join` which otherwise fail with: ``` error: static assertion failed: passing views as lvalues is disallowed ``` --- include/bout/boutexception.hxx | 15 ++++++---- include/bout/output.hxx | 43 ++++++++++++++------------- tests/unit/sys/test_boutexception.cxx | 12 +++++++- tests/unit/sys/test_output.cxx | 11 +++++++ 4 files changed, 54 insertions(+), 27 deletions(-) diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index 238c88654c..9c47eed86c 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -19,8 +19,9 @@ public: BoutException(std::string msg); template - BoutException(const S& format, const Args&... args) - : BoutException(fmt::format(format, args...)) {} + BoutException(S&& format, Args&&... args) + : BoutException(fmt::format(std::forward(format), + std::forward(args)...)) {} ~BoutException() override; @@ -46,16 +47,18 @@ class BoutRhsFail : public BoutException { public: BoutRhsFail(std::string message) : BoutException(std::move(message)) {} template - BoutRhsFail(const S& format, const Args&... args) - : BoutRhsFail(fmt::format(format, args...)) {} + BoutRhsFail(S&& format, Args&&... args) + : BoutRhsFail(fmt::format(std::forward(format), + std::forward(args)...)) {} }; class BoutIterationFail : public BoutException { public: BoutIterationFail(std::string message) : BoutException(std::move(message)) {} template - BoutIterationFail(const S& format, const Args&... args) - : BoutIterationFail(fmt::format(format, args...)) {} + BoutIterationFail(S&& format, Args&&... args) + : BoutIterationFail(fmt::format(std::forward(format), + std::forward(args)...)) {} }; #endif diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 2862899067..124f170aea 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -42,6 +42,8 @@ class Output; #include "fmt/core.h" +#include + using std::endl; /// Class for text output to stdout and/or log file @@ -76,7 +78,8 @@ public: } template - Output(const S& format, const Args&... args) : Output(fmt::format(format, args...)) {} + Output(const S& format, Args&&... args) + : Output(fmt::format(format, std::forward(args)...)) {} ~Output() override { close(); } @@ -87,8 +90,8 @@ public: int open(const std::string& filename); template - int open(const S& format, const Args&... args) { - return open(fmt::format(format, args...)); + int open(const S& format, Args&&... args) { + return open(fmt::format(format, std::forward(args)...)); } /// Close the log file @@ -98,15 +101,15 @@ public: virtual void write(const std::string& message); template - void write(const S& format, const Args&... args) { - write(fmt::format(format, args...)); + void write(const S& format, Args&&... args) { + write(fmt::format(format, std::forward(args)...)); } /// Same as write, but only to screen virtual void print(const std::string& message); template - void print(const S& format, const Args&... args) { - print(fmt::format(format, args...)); + void print(const S& format, Args&&... args) { + print(fmt::format(format, std::forward(args)...)); } /// Add an output stream. All output will be sent to all streams @@ -136,14 +139,14 @@ private: class DummyOutput : public Output { public: template - void write([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args){}; + void write([[maybe_unused]] const S& format, [[maybe_unused]] Args&&... args) {}; template - void print([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args){}; - void write([[maybe_unused]] const std::string& message) override{}; - void print([[maybe_unused]] const std::string& message) override{}; - void enable() override{}; - void disable() override{}; - void enable([[maybe_unused]] bool enable){}; + void print([[maybe_unused]] const S& format, [[maybe_unused]] Args&&... args) {}; + void write([[maybe_unused]] const std::string& message) override {}; + void print([[maybe_unused]] const std::string& message) override {}; + void enable() override {}; + void disable() override {}; + void enable([[maybe_unused]] bool enable) {}; bool isEnabled() override { return false; } }; @@ -156,13 +159,13 @@ class ConditionalOutput : public Output { public: /// @param[in] base The Output object which will be written to if enabled /// @param[in] enabled Should this be enabled by default? - ConditionalOutput(Output* base, bool enabled = true) : base(base), enabled(enabled){}; + ConditionalOutput(Output* base, bool enabled = true) : base(base), enabled(enabled) {}; /// Constuctor taking ConditionalOutput. This allows several layers of conditions /// /// @param[in] base A ConditionalOutput which will be written to if enabled /// - ConditionalOutput(ConditionalOutput* base) : base(base), enabled(base->enabled){}; + ConditionalOutput(ConditionalOutput* base) : base(base), enabled(base->enabled) {}; /// If enabled, writes a string using fmt formatting /// by calling base->write @@ -170,10 +173,10 @@ public: void write(const std::string& message) override; template - void write(const S& format, const Args&... args) { + void write(const S& format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->write(fmt::format(format, args...)); + base->write(fmt::format(format, std::forward(args)...)); } } @@ -182,10 +185,10 @@ public: void print(const std::string& message) override; template - void print(const S& format, const Args&... args) { + void print(const S& format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->print(fmt::format(format, args...)); + base->print(fmt::format(format, std::forward(args)...)); } } diff --git a/tests/unit/sys/test_boutexception.cxx b/tests/unit/sys/test_boutexception.cxx index 39f8ee2145..322c7d6e42 100644 --- a/tests/unit/sys/test_boutexception.cxx +++ b/tests/unit/sys/test_boutexception.cxx @@ -4,8 +4,11 @@ #include "bout/boutexception.hxx" #include "gtest/gtest.h" -#include +#include + #include +#include +#include TEST(BoutExceptionTest, ThrowCorrect) { EXPECT_THROW(throw BoutException("test"), BoutException); @@ -45,6 +48,13 @@ TEST(BoutExceptionTest, GetBacktrace) { } } +TEST(BoutExceptionTest, FmtJoin) { + const std::vector things = {1, 2, 3, 4}; + constexpr std::string_view expected = "list: 1, 2, 3, 4"; + const BoutException exception{"list: {}", fmt::join(things, ", ")}; + EXPECT_EQ(exception.what(), expected); +} + TEST(BoutRhsFailTest, ThrowCorrect) { EXPECT_THROW(throw BoutRhsFail("RHS Fail test"), BoutRhsFail); } diff --git a/tests/unit/sys/test_output.cxx b/tests/unit/sys/test_output.cxx index 1338d9ce13..959823ede3 100644 --- a/tests/unit/sys/test_output.cxx +++ b/tests/unit/sys/test_output.cxx @@ -4,7 +4,10 @@ #include "bout/output_bout_types.hxx" #include "gtest/gtest.h" +#include + #include +#include // stdout redirection code from https://stackoverflow.com/a/4043813/2043465 class OutputTest : public ::testing::Test { @@ -385,3 +388,11 @@ TEST_F(OutputTest, FormatIndPerpi) { EXPECT_EQ(buffer.str(), "(15)"); } + +TEST_F(OutputTest, FmtJoin) { + const std::vector things = {1, 2, 3, 4}; + Output local_output; + local_output.write("list: {}", fmt::join(things, ", ")); + + EXPECT_EQ(buffer.str(), "list: 1, 2, 3, 4"); +} From e3f07907e06d555106fbe10d49fb9aa887ae2fc0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 4 Dec 2025 18:08:45 +0000 Subject: [PATCH 100/461] Clang-tidy fixes --- include/bout/boutexception.hxx | 6 +++-- include/bout/output.hxx | 41 ++++++++++++++++++--------------- src/sys/boutexception.cxx | 42 +++++++++++++++++++--------------- src/sys/output.cxx | 14 ++++++++---- 4 files changed, 59 insertions(+), 44 deletions(-) diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index 9c47eed86c..ffd4a03a6c 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -16,6 +16,10 @@ void BoutParallelThrowRhsFail(int status, const char* message); class BoutException : public std::exception { public: + BoutException(const BoutException&) = default; + BoutException(BoutException&&) = delete; + BoutException& operator=(const BoutException&) = default; + BoutException& operator=(BoutException&&) = delete; BoutException(std::string msg); template @@ -39,8 +43,6 @@ private: int trace_size; char** messages; #endif - - void makeBacktrace(); }; class BoutRhsFail : public BoutException { diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 124f170aea..205ca3a874 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -31,13 +31,11 @@ class Output; #include "bout/multiostream.hxx" #include -#include #include #include #include "bout/assert.hxx" -#include "bout/boutexception.hxx" -#include "bout/sys/gettext.hxx" // for gettext _() macro +#include "bout/sys/gettext.hxx" // IWYU pragma: keep for gettext _() macro #include "bout/unused.hxx" #include "fmt/core.h" @@ -63,14 +61,19 @@ using std::endl; class Output : private multioutbuf_init>, public std::basic_ostream> { - using _Tr = std::char_traits; - using multioutbuf_init = ::multioutbuf_init; + using Tr = std::char_traits; + using multioutbuf_init = ::multioutbuf_init; public: - Output() : multioutbuf_init(), std::basic_ostream(multioutbuf_init::buf()) { + Output() : multioutbuf_init(), std::basic_ostream(multioutbuf_init::buf()) { Output::enable(); } + Output(const Output&) = delete; + Output(Output&&) = delete; + Output& operator=(const Output&) = delete; + Output& operator=(Output&&) = delete; + /// Specify a log file to open Output(const std::string& filename) { Output::enable(); @@ -113,12 +116,10 @@ public: } /// Add an output stream. All output will be sent to all streams - void add(std::basic_ostream& str) { multioutbuf_init::buf()->add(str); } + void add(std::basic_ostream& str) { multioutbuf_init::buf()->add(str); } /// Remove an output stream - void remove(std::basic_ostream& str) { - multioutbuf_init::buf()->remove(str); - } + void remove(std::basic_ostream& str) { multioutbuf_init::buf()->remove(str); } static Output* getInstance(); ///< Return pointer to instance @@ -129,7 +130,7 @@ protected: private: std::ofstream file; ///< Log file stream - bool enabled; ///< Whether output to stdout is enabled + bool enabled{}; ///< Whether output to stdout is enabled }; /// Class which behaves like Output, but has no effect. @@ -219,8 +220,6 @@ private: /// The lower-level Output to send output to Output* base; -protected: - friend class WithQuietOutput; /// Does this instance output anything? bool enabled; }; @@ -279,18 +278,23 @@ ConditionalOutput& operator<<(ConditionalOutput& out, const T* t) { /// // output now enabled class WithQuietOutput { public: - explicit WithQuietOutput(ConditionalOutput& output_in) : output(output_in) { - state = output.enabled; - output.disable(); + WithQuietOutput(const WithQuietOutput&) = delete; + WithQuietOutput(WithQuietOutput&&) = delete; + WithQuietOutput& operator=(const WithQuietOutput&) = delete; + WithQuietOutput& operator=(WithQuietOutput&&) = delete; + explicit WithQuietOutput(ConditionalOutput& output_in) + : output(&output_in), state(output->isEnabled()) { + output->disable(); } - ~WithQuietOutput() { output.enable(state); } + ~WithQuietOutput() { output->enable(state); } private: - ConditionalOutput& output; + ConditionalOutput* output; bool state; }; +// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) /// To allow statements like "output.write(...)" or "output << ..." /// Output for debugging #ifdef BOUT_USE_OUTPUT_DEBUG @@ -306,5 +310,6 @@ extern ConditionalOutput output_verbose; ///< less interesting messages /// Generic output, given the same level as output_progress extern ConditionalOutput output; +// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) #endif // BOUT_OUTPUT_H diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index 825fb37e28..e81e395bf8 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -4,16 +4,19 @@ #include #include #include + #include #if BOUT_USE_BACKTRACE #include #include +#include #endif #include #include #include +#include #include @@ -22,18 +25,24 @@ const std::string header{"====== Exception thrown ======\n"}; } void BoutParallelThrowRhsFail(int status, const char* message) { - int allstatus; + int allstatus = 0; MPI_Allreduce(&status, &allstatus, 1, MPI_INT, MPI_LOR, BoutComm::get()); - if (allstatus) { + if (allstatus != 0) { throw BoutRhsFail(message); } } -BoutException::BoutException(std::string msg) : message(std::move(msg)) { - makeBacktrace(); +BoutException::BoutException(std::string msg) + : message(std::move(msg)) +#if BOUT_USE_BACKTRACE + , + trace_size(backtrace(trace.data(), TRACE_MAX)), + messages(backtrace_symbols(trace.data(), trace_size)) +#endif +{ if (std::getenv("BOUT_SHOW_BACKTRACE") != nullptr) { - message = getBacktrace() + "\n" + message; + message = fmt::format("{}\n{}", getBacktrace(), message); } } @@ -54,7 +63,8 @@ std::string BoutException::getBacktrace() const { backtrace_message = "====== Exception path ======\n"; // skip first stack frame (points here) for (int i = trace_size - 1; i > 1; --i) { - backtrace_message += fmt::format(FMT_STRING("[bt] #{:d} {:s}\n"), i - 1, messages[i]); + backtrace_message = fmt::format(FMT_STRING("{}[bt] #{:d} {:s}\n"), backtrace_message, + i - 1, messages[i]); // find first occurence of '(' or ' ' in message[i] and assume // everything before that is the file name. (Don't go beyond 0 though // (string terminator) @@ -65,11 +75,11 @@ std::string BoutException::getBacktrace() const { // If we are compiled as PIE, need to get base pointer of .so and substract Dl_info info; - void* ptr = trace[i]; - if (dladdr(trace[i], &info)) { + const void* ptr = trace[i]; + if (dladdr(ptr, &info) != 0) { // Additionally, check whether this is the default offset for an executable if (info.dli_fbase != reinterpret_cast(0x400000)) { - ptr = reinterpret_cast(reinterpret_cast(trace[i]) + ptr = reinterpret_cast(reinterpret_cast(ptr) - reinterpret_cast(info.dli_fbase)); } } @@ -82,14 +92,14 @@ std::string BoutException::getBacktrace() const { FILE* file = popen(syscom.c_str(), "r"); if (file != nullptr) { std::array out{}; - char* retstr = nullptr; + const char* retstr = nullptr; std::string buf; while ((retstr = fgets(out.data(), out.size() - 1, file)) != nullptr) { buf += retstr; } int const status = pclose(file); if (status == 0) { - backtrace_message += buf; + backtrace_message = fmt::format("{}{}", backtrace_message, buf); } } } @@ -97,12 +107,6 @@ std::string BoutException::getBacktrace() const { backtrace_message = "Stacktrace not enabled.\n"; #endif - return backtrace_message + msg_stack.getDump() + "\n" + header + message + "\n"; -} - -void BoutException::makeBacktrace() { -#if BOUT_USE_BACKTRACE - trace_size = backtrace(trace.data(), TRACE_MAX); - messages = backtrace_symbols(trace.data(), trace_size); -#endif + return fmt::format("{}{}\n{}{}\n", backtrace_message, msg_stack.getDump(), header, + message); } diff --git a/src/sys/output.cxx b/src/sys/output.cxx index c07cd53474..2c5e06bb7c 100644 --- a/src/sys/output.cxx +++ b/src/sys/output.cxx @@ -23,11 +23,13 @@ * **************************************************************************/ +#include #include -#include -#include -#include -#include + +#include + +#include +#include void Output::enable() { add(std::cout); @@ -68,7 +70,7 @@ void Output::close() { } void Output::write(const std::string& message) { - multioutbuf_init::buf()->sputn(message.c_str(), message.length()); + multioutbuf_init::buf()->sputn(message.c_str(), static_cast(message.length())); } void Output::print(const std::string& message) { @@ -98,6 +100,7 @@ void ConditionalOutput::print(const std::string& message) { } } +// NOLINTBEGIN(cppcoreguidelines-avoid-non-const-global-variables) #ifdef BOUT_USE_OUTPUT_DEBUG ConditionalOutput output_debug(Output::getInstance()); #else @@ -109,3 +112,4 @@ ConditionalOutput output_progress(Output::getInstance()); ConditionalOutput output_error(Output::getInstance()); ConditionalOutput output_verbose(Output::getInstance(), false); ConditionalOutput output(Output::getInstance()); +// NOLINTEND(cppcoreguidelines-avoid-non-const-global-variables) From ab61b9e3c65f974ec5d0ac09ef7c6dd53f15b516 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Thu, 4 Dec 2025 18:12:08 +0000 Subject: [PATCH 101/461] Apply clang-format changes --- include/bout/output.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 205ca3a874..823e359753 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -160,13 +160,13 @@ class ConditionalOutput : public Output { public: /// @param[in] base The Output object which will be written to if enabled /// @param[in] enabled Should this be enabled by default? - ConditionalOutput(Output* base, bool enabled = true) : base(base), enabled(enabled) {}; + ConditionalOutput(Output* base, bool enabled = true) : base(base), enabled(enabled){}; /// Constuctor taking ConditionalOutput. This allows several layers of conditions /// /// @param[in] base A ConditionalOutput which will be written to if enabled /// - ConditionalOutput(ConditionalOutput* base) : base(base), enabled(base->enabled) {}; + ConditionalOutput(ConditionalOutput* base) : base(base), enabled(base->enabled){}; /// If enabled, writes a string using fmt formatting /// by calling base->write From 4323aa527f03af553a6232a582c636cfb2c6d093 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 5 Dec 2025 10:35:33 -0800 Subject: [PATCH 102/461] petsc solver: Catch exceptions from run_rhs If run_rhs throws an exception then it should be caught, and SNES notified that an error occurred. --- src/solver/impls/petsc/petsc.cxx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index ed48d87312..98e58d7afa 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -903,8 +903,19 @@ PetscErrorCode PetscSolver::rhs(BoutReal t, Vec udata, Vec dudata, bool linear) load_vars(const_cast(udata_array)); VecRestoreArrayRead(udata, &udata_array); - // Call RHS function - run_rhs(t, linear); + try { + // Call RHS function + run_rhs(t, linear); + } catch (BoutException& e) { + // Simulation might fail, e.g. negative densities + // if timestep too large + output_warn.write("WARNING: BoutException thrown: {}\n", e.what()); + + // Tell SNES that the input was out of domain + SNESSetFunctionDomainError(snes); + // Note: Returning non-zero error here leaves vectors in locked state + return 0; + } // Save derivatives to PETSc BoutReal* dudata_array; From 210ce59f09a782a58cc95d4797b451bc4558bb3a Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 5 Dec 2025 12:18:12 -0800 Subject: [PATCH 103/461] petsc solver: Add diagnostics compatible with snes solver If solver:diagnose is set then a line will be printed for each internal (TS) step. The format is the same as the `snes` solver, so the same post-processing scripts can be used. --- src/solver/impls/petsc/petsc.cxx | 33 ++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index 98e58d7afa..e7e0296677 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -165,6 +165,39 @@ PetscErrorCode PetscMonitor(TS ts, PetscInt UNUSED(step), PetscReal t, Vec X, vo PetscFunctionBegin; auto* s = static_cast(ctx); + + if (s->diagnose) { + // Print diagnostic information. + // Using the same format at the SNES solver + + SNESConvergedReason reason; + SNESGetConvergedReason(s->snes, &reason); + + PetscReal timestep; + TSGetTimeStep(s->ts, ×tep); + + // SNES failure counter is only reset when TSSolve is called + static PetscInt prev_snes_failures = 0; + PetscInt snes_failures; + TSGetSNESFailures(s->ts, &snes_failures); + snes_failures -= prev_snes_failures; + prev_snes_failures += snes_failures; + + // Get number of iterations + int nl_its; + SNESGetIterationNumber(s->snes, &nl_its); + int lin_its; + SNESGetLinearSolveIterations(s->snes, &lin_its); + + output.print("\r"); // Carriage return for printing to screen + output.write("Time: {}, timestep: {}, nl iter: {}, lin iter: {}, reason: {}", + t, timestep, nl_its, lin_its, static_cast(reason)); + if (snes_failures > 0) { + output.write(", SNES failures: {}", snes_failures); + } + output.write("\n"); + } + if (t < s->next_output) { // Not reached output time yet => return PetscFunctionReturn(0); From 1ddf5b8cff07e67ea47ffc30f83570190aafd390 Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Fri, 5 Dec 2025 20:24:44 +0000 Subject: [PATCH 104/461] Apply clang-format changes --- src/solver/impls/petsc/petsc.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index e7e0296677..d0535f069d 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -190,8 +190,8 @@ PetscErrorCode PetscMonitor(TS ts, PetscInt UNUSED(step), PetscReal t, Vec X, vo SNESGetLinearSolveIterations(s->snes, &lin_its); output.print("\r"); // Carriage return for printing to screen - output.write("Time: {}, timestep: {}, nl iter: {}, lin iter: {}, reason: {}", - t, timestep, nl_its, lin_its, static_cast(reason)); + output.write("Time: {}, timestep: {}, nl iter: {}, lin iter: {}, reason: {}", t, + timestep, nl_its, lin_its, static_cast(reason)); if (snes_failures > 0) { output.write(", SNES failures: {}", snes_failures); } From 666d0e6d68ade7b09a0ee5a594eb6b62b6c40ccf Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 19:06:24 +0000 Subject: [PATCH 105/461] Bump actions/cache from 4 to 5 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f2c3618fab..82f3d6aea0 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -182,7 +182,7 @@ jobs: python -m pip install -r requirements.txt - name: Cache SUNDIALS build - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: /home/runner/local key: bout-sundials-${{ matrix.config.os }}${{ matrix.config.build_petsc }} From 50764ef185a55314f3a8adc7e6a3f7196c55e684 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 19 Dec 2025 08:43:13 -0800 Subject: [PATCH 106/461] snes solver: Wrap all run_rhs calls in try...catch Calls to run_rhs may throw an exception if given out of domain values. Catch and return error code if this happens. --- src/solver/impls/snes/snes.cxx | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index c10095af53..23b82982f4 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -993,7 +993,13 @@ int SNESSolver::run() { ierr = VecRestoreArrayRead(snes_x, &xdata); CHKERRQ(ierr); } - run_rhs(simtime); + + try { + run_rhs(simtime); + } catch (BoutException& e) { + output_error.write("ERROR: BoutException thrown: {}\n", e.what()); + return 1; + } // Copy derivatives back { @@ -1168,7 +1174,12 @@ int SNESSolver::run() { ierr = VecRestoreArrayRead(output_x, &xdata); CHKERRQ(ierr); } - run_rhs(target); // Run RHS to calculate auxilliary variables + + try { + run_rhs(target); // Run RHS to calculate auxilliary variables + } catch (BoutException& e) { + output_error.write("ERROR: BoutException thrown: {}\n", e.what()); + } if (call_monitors(target, s, getNumberOutputSteps()) != 0) { break; // User signalled to quit From 4b86f92296de57149cab592114be901bec751755 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:04:47 +0000 Subject: [PATCH 107/461] Bump externalpackages/googletest from `1b96fa1` to `9156d4c` Bumps [externalpackages/googletest](https://github.com/google/googletest) from `1b96fa1` to `9156d4c`. - [Release notes](https://github.com/google/googletest/releases) - [Commits](https://github.com/google/googletest/compare/1b96fa13f549387b7549cc89e1a785cf143a1a50...9156d4caac880b513264ecbe0aa4746a3fead3d7) --- updated-dependencies: - dependency-name: externalpackages/googletest dependency-version: 9156d4caac880b513264ecbe0aa4746a3fead3d7 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- externalpackages/googletest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externalpackages/googletest b/externalpackages/googletest index 1b96fa13f5..9156d4caac 160000 --- a/externalpackages/googletest +++ b/externalpackages/googletest @@ -1 +1 @@ -Subproject commit 1b96fa13f549387b7549cc89e1a785cf143a1a50 +Subproject commit 9156d4caac880b513264ecbe0aa4746a3fead3d7 From cbea5c5c0327f7e76151d4682291c89a5dc6996b Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 22 Dec 2025 19:04:52 +0000 Subject: [PATCH 108/461] Bump ZedThree/clang-tidy-review from 0.22.2 to 0.22.3 Bumps [ZedThree/clang-tidy-review](https://github.com/zedthree/clang-tidy-review) from 0.22.2 to 0.22.3. - [Release notes](https://github.com/zedthree/clang-tidy-review/releases) - [Changelog](https://github.com/ZedThree/clang-tidy-review/blob/master/CHANGELOG.md) - [Commits](https://github.com/zedthree/clang-tidy-review/compare/v0.22.2...v0.22.3) --- updated-dependencies: - dependency-name: ZedThree/clang-tidy-review dependency-version: 0.22.3 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/clang-tidy-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 06c2f88b03..4e3cef8fe6 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -21,7 +21,7 @@ jobs: submodules: true - name: Run clang-tidy - uses: ZedThree/clang-tidy-review@v0.22.2 + uses: ZedThree/clang-tidy-review@v0.22.3 id: review with: build_dir: build @@ -46,4 +46,4 @@ jobs: -DBOUT_UPDATE_GIT_SUBMODULE=OFF - name: Upload clang-tidy fixes - uses: ZedThree/clang-tidy-review/upload@v0.22.2 + uses: ZedThree/clang-tidy-review/upload@v0.22.3 From f84f7116a3cfde8f16f805a86c6440aedf2b7701 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 29 Dec 2025 10:27:39 -0800 Subject: [PATCH 109/461] Rename is_periodic_y Calls mesh->periodicY internally --- src/field/field_factory.cxx | 2 +- src/field/fieldgenerators.cxx | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/src/field/field_factory.cxx b/src/field/field_factory.cxx index 08a7f14fa3..b93a39eeda 100644 --- a/src/field/field_factory.cxx +++ b/src/field/field_factory.cxx @@ -178,7 +178,7 @@ FieldFactory::FieldFactory(Mesh* localmesh, Options* opt) addGenerator("where", std::make_shared(nullptr, nullptr, nullptr)); // Periodic in the Y direction? - addGenerator("periodicY", std::make_shared()); + addGenerator("is_periodic_y", std::make_shared()); } Field2D FieldFactory::create2D(const std::string& value, const Options* opt, diff --git a/src/field/fieldgenerators.cxx b/src/field/fieldgenerators.cxx index 1f21f5c262..7e728664f8 100644 --- a/src/field/fieldgenerators.cxx +++ b/src/field/fieldgenerators.cxx @@ -1,6 +1,8 @@ #include "fieldgenerators.hxx" +#include + #include #include From 7b30663b262012f600be382735246d3afb74b487 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 29 Dec 2025 14:29:12 -0800 Subject: [PATCH 110/461] Unit test is_periodic_y Extends FakeMesh to mock periodicY methods. --- tests/unit/fake_mesh.hxx | 8 +++- tests/unit/field/test_field_factory.cxx | 54 +++++++++++++++++++++++++ 2 files changed, 60 insertions(+), 2 deletions(-) diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index e6f78f8767..99c8d04546 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -120,8 +120,12 @@ public: } MPI_Comm getXcomm(int UNUSED(jy)) const override { return BoutComm::get(); } MPI_Comm getYcomm(int UNUSED(jx)) const override { return BoutComm::get(); } - bool periodicY(int UNUSED(jx)) const override { return true; } - bool periodicY(int UNUSED(jx), BoutReal& UNUSED(ts)) const override { return true; } + + // Periodic Y + int ix_separatrix {1000000}; // separatrix index + + bool periodicY(int jx) const override { return jx < ix_separatrix; } + bool periodicY(int jx, BoutReal& UNUSED(ts)) const override { return jx < ix_separatrix; } int numberOfYBoundaries() const override { return 1; } std::pair hasBranchCutLower(int UNUSED(jx)) const override { return std::make_pair(false, 0.); diff --git a/tests/unit/field/test_field_factory.cxx b/tests/unit/field/test_field_factory.cxx index 964788b25a..2c47ae90a6 100644 --- a/tests/unit/field/test_field_factory.cxx +++ b/tests/unit/field/test_field_factory.cxx @@ -969,3 +969,57 @@ TEST_F(FieldFactoryCreateAndTransformTest, Create3DCantTransform) { EXPECT_TRUE(IsFieldEqual(output, expected)); } + +TYPED_TEST(FieldFactoryCreationTest, CreatePeriodicY) { + auto output = this->create("is_periodic_y"); + + auto expected = makeField( + [](typename TypeParam::ind_type& index) -> BoutReal { return mesh->periodicY(index.x()); }, mesh); + + EXPECT_TRUE(IsFieldEqual(output, expected)); +} + +TYPED_TEST(FieldFactoryCreationTest, CreatePeriodicYoutsideCore) { + FakeMesh localmesh{5, 1, 1}; + localmesh.createDefaultRegions(); + localmesh.setCoordinates(std::make_shared( + &localmesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, + Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, + Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, + Field2D{0.0}, Field2D{0.0})); + // No call to Coordinates::geometry() needed here + + localmesh.getCoordinates()->setParallelTransform( + bout::utils::make_unique(localmesh)); + + localmesh.ix_separatrix = 0; // All points outside core + + auto output = this->create("is_periodic_y", nullptr, &localmesh); + + auto expected = makeField( + [](typename TypeParam::ind_type& index) -> BoutReal { return 0.0; }, &localmesh); + + EXPECT_TRUE(IsFieldEqual(output, expected)); +} + +TYPED_TEST(FieldFactoryCreationTest, CreatePeriodicYacrossSeparatrix) { + FakeMesh localmesh{5, 1, 1}; + localmesh.createDefaultRegions(); + localmesh.ix_separatrix = 2; // All points in core + localmesh.setCoordinates(std::make_shared( + &localmesh, Field2D{1.0}, Field2D{1.0}, BoutReal{1.0}, Field2D{1.0}, Field2D{0.0}, + Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, + Field2D{1.0}, Field2D{1.0}, Field2D{1.0}, Field2D{0.0}, Field2D{0.0}, Field2D{0.0}, + Field2D{0.0}, Field2D{0.0})); + // No call to Coordinates::geometry() needed here + + localmesh.getCoordinates()->setParallelTransform( + bout::utils::make_unique(localmesh)); + + auto output = this->create("is_periodic_y", nullptr, &localmesh); + + auto expected = makeField( + [&](typename TypeParam::ind_type& index) -> BoutReal { return index.x() < localmesh.ix_separatrix; }, &localmesh); + + EXPECT_TRUE(IsFieldEqual(output, expected)); +} From 1f264d0914e29a352238e7d413b1d3dcacbb9693 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Mon, 29 Dec 2025 14:29:57 -0800 Subject: [PATCH 111/461] Add is_periodic_y to manual page Include in table of values available to expressions. --- manual/sphinx/user_docs/variable_init.rst | 24 ++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/manual/sphinx/user_docs/variable_init.rst b/manual/sphinx/user_docs/variable_init.rst index 4ac13e0ead..1baaba5e84 100644 --- a/manual/sphinx/user_docs/variable_init.rst +++ b/manual/sphinx/user_docs/variable_init.rst @@ -81,17 +81,19 @@ following values are also already defined: .. _tab-initexprvals: .. table:: Initialisation expression values - +--------+------------------------------------------------------------------------------------+ - | Name | Description | - +========+====================================================================================+ - | x | :math:`x` position between :math:`0` and :math:`1` | - +--------+------------------------------------------------------------------------------------+ - | y | :math:`y` angle-like position, definition depends on topology of grid | - +--------+------------------------------------------------------------------------------------+ - | z | :math:`z` position between :math:`0` and :math:`2\pi` (excluding the last point) | - +--------+------------------------------------------------------------------------------------+ - | pi π | :math:`3.1415\ldots` | - +--------+------------------------------------------------------------------------------------+ + +----------------+------------------------------------------------------------------------------------+ + | Name | Description | + +================+====================================================================================+ + | x | :math:`x` position between :math:`0` and :math:`1` | + +----------------+------------------------------------------------------------------------------------+ + | y | :math:`y` angle-like position, definition depends on topology of grid | + +----------------+------------------------------------------------------------------------------------+ + | z | :math:`z` position between :math:`0` and :math:`2\pi` (excluding the last point) | + +----------------+------------------------------------------------------------------------------------+ + | pi π | :math:`3.1415\ldots` | + +----------------+------------------------------------------------------------------------------------+ + | is_periodic_y | :math:`1` in core region where Y is periodic. :math:`0` otherwise | + +----------------+------------------------------------------------------------------------------------+ By default, :math:`x` is defined as ``(i+0.5) / (nx - 2*MXG)``, where ``MXG`` From 67bddf3e9c76440141f5d5c32a5d57c96215145e Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Mon, 29 Dec 2025 22:31:09 +0000 Subject: [PATCH 112/461] Apply clang-format changes --- include/bout/invertable_operator.hxx | 4 ++-- include/bout/output.hxx | 14 +++++++------- src/solver/impls/arkode/arkode.cxx | 3 +-- tests/unit/fake_mesh.hxx | 6 ++++-- tests/unit/field/test_field_factory.cxx | 12 +++++++++--- 5 files changed, 23 insertions(+), 16 deletions(-) diff --git a/include/bout/invertable_operator.hxx b/include/bout/invertable_operator.hxx index fe139986be..99f7d9b675 100644 --- a/include/bout/invertable_operator.hxx +++ b/include/bout/invertable_operator.hxx @@ -575,7 +575,7 @@ public: }; #endif // PETSC -}; // namespace inversion -}; // namespace bout +}; // namespace inversion +}; // namespace bout #endif // HEADER GUARD diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 2862899067..8f50f9029b 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -136,14 +136,14 @@ private: class DummyOutput : public Output { public: template - void write([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args){}; + void write([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args) {}; template - void print([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args){}; - void write([[maybe_unused]] const std::string& message) override{}; - void print([[maybe_unused]] const std::string& message) override{}; - void enable() override{}; - void disable() override{}; - void enable([[maybe_unused]] bool enable){}; + void print([[maybe_unused]] const S& format, [[maybe_unused]] const Args&... args) {}; + void write([[maybe_unused]] const std::string& message) override {}; + void print([[maybe_unused]] const std::string& message) override {}; + void enable() override {}; + void disable() override {}; + void enable([[maybe_unused]] bool enable) {}; bool isEnabled() override { return false; } }; diff --git a/src/solver/impls/arkode/arkode.cxx b/src/solver/impls/arkode/arkode.cxx index 6f20ce11a6..e5c70ae9ba 100644 --- a/src/solver/impls/arkode/arkode.cxx +++ b/src/solver/impls/arkode/arkode.cxx @@ -417,8 +417,7 @@ int ArkodeSolver::init() { if (hasPreconditioner()) { output.write("\tUsing user-supplied preconditioner\n"); - if (ARKodeSetPreconditioner(arkode_mem, nullptr, arkode_pre) - != ARKLS_SUCCESS) { + if (ARKodeSetPreconditioner(arkode_mem, nullptr, arkode_pre) != ARKLS_SUCCESS) { throw BoutException("ARKodeSetPreconditioner failed\n"); } } else { diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 99c8d04546..e8ba4f3e93 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -122,10 +122,12 @@ public: MPI_Comm getYcomm(int UNUSED(jx)) const override { return BoutComm::get(); } // Periodic Y - int ix_separatrix {1000000}; // separatrix index + int ix_separatrix{1000000}; // separatrix index bool periodicY(int jx) const override { return jx < ix_separatrix; } - bool periodicY(int jx, BoutReal& UNUSED(ts)) const override { return jx < ix_separatrix; } + bool periodicY(int jx, BoutReal& UNUSED(ts)) const override { + return jx < ix_separatrix; + } int numberOfYBoundaries() const override { return 1; } std::pair hasBranchCutLower(int UNUSED(jx)) const override { return std::make_pair(false, 0.); diff --git a/tests/unit/field/test_field_factory.cxx b/tests/unit/field/test_field_factory.cxx index 2c47ae90a6..26dc1e2990 100644 --- a/tests/unit/field/test_field_factory.cxx +++ b/tests/unit/field/test_field_factory.cxx @@ -974,7 +974,10 @@ TYPED_TEST(FieldFactoryCreationTest, CreatePeriodicY) { auto output = this->create("is_periodic_y"); auto expected = makeField( - [](typename TypeParam::ind_type& index) -> BoutReal { return mesh->periodicY(index.x()); }, mesh); + [](typename TypeParam::ind_type& index) -> BoutReal { + return mesh->periodicY(index.x()); + }, + mesh); EXPECT_TRUE(IsFieldEqual(output, expected)); } @@ -997,7 +1000,7 @@ TYPED_TEST(FieldFactoryCreationTest, CreatePeriodicYoutsideCore) { auto output = this->create("is_periodic_y", nullptr, &localmesh); auto expected = makeField( - [](typename TypeParam::ind_type& index) -> BoutReal { return 0.0; }, &localmesh); + [](typename TypeParam::ind_type& index) -> BoutReal { return 0.0; }, &localmesh); EXPECT_TRUE(IsFieldEqual(output, expected)); } @@ -1019,7 +1022,10 @@ TYPED_TEST(FieldFactoryCreationTest, CreatePeriodicYacrossSeparatrix) { auto output = this->create("is_periodic_y", nullptr, &localmesh); auto expected = makeField( - [&](typename TypeParam::ind_type& index) -> BoutReal { return index.x() < localmesh.ix_separatrix; }, &localmesh); + [&](typename TypeParam::ind_type& index) -> BoutReal { + return index.x() < localmesh.ix_separatrix; + }, + &localmesh); EXPECT_TRUE(IsFieldEqual(output, expected)); } From 236437b46622f1b045d35ed1105580fc5e8431eb Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 31 Dec 2025 19:01:25 +0000 Subject: [PATCH 113/461] Update ruamel-yaml requirement from ~=0.18 to ~=0.19 Updates the requirements on ruamel-yaml to permit the latest version. --- updated-dependencies: - dependency-name: ruamel-yaml dependency-version: 0.19.0 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- requirements_maint.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/requirements_maint.txt b/requirements_maint.txt index b1d9db5ff8..05375dd39e 100644 --- a/requirements_maint.txt +++ b/requirements_maint.txt @@ -1,3 +1,3 @@ pygithub~=2.8 -ruamel-yaml~=0.18 +ruamel-yaml~=0.19 Unidecode~=1.3 From c430a2659c882e8de5c0d8e3b64df7a62046de09 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 5 Jan 2026 19:05:08 +0000 Subject: [PATCH 114/461] Bump ZedThree/clang-tidy-review from 0.22.3 to 0.23.0 Bumps [ZedThree/clang-tidy-review](https://github.com/zedthree/clang-tidy-review) from 0.22.3 to 0.23.0. - [Release notes](https://github.com/zedthree/clang-tidy-review/releases) - [Changelog](https://github.com/ZedThree/clang-tidy-review/blob/master/CHANGELOG.md) - [Commits](https://github.com/zedthree/clang-tidy-review/compare/v0.22.3...v0.23.0) --- updated-dependencies: - dependency-name: ZedThree/clang-tidy-review dependency-version: 0.23.0 dependency-type: direct:production update-type: version-update:semver-minor ... Signed-off-by: dependabot[bot] --- .github/workflows/clang-tidy-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 4e3cef8fe6..087c910987 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -21,7 +21,7 @@ jobs: submodules: true - name: Run clang-tidy - uses: ZedThree/clang-tidy-review@v0.22.3 + uses: ZedThree/clang-tidy-review@v0.23.0 id: review with: build_dir: build @@ -46,4 +46,4 @@ jobs: -DBOUT_UPDATE_GIT_SUBMODULE=OFF - name: Upload clang-tidy fixes - uses: ZedThree/clang-tidy-review/upload@v0.22.3 + uses: ZedThree/clang-tidy-review/upload@v0.23.0 From bc2dad1d8e4c0d5e7de8f12351b67d7fc73adb33 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:56:42 +0000 Subject: [PATCH 115/461] cmake: Set `_ROOT` variable from corresponding `_DIR` Using the build directory directly for some dependencies (sometimes?) doesn't set the `_ROOT` variable, causing issues with our `bout++Config.cmake` file. --- cmake/SetupBOUTThirdParty.cmake | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 53ceb4351c..1256fbbeba 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -186,6 +186,12 @@ if (BOUT_USE_NETCDF) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${NETCDF_LDFLAGS_STRING}") endif() endif() + if (netCDF_DIR) + set(netCDF_ROOT "${netCDF_DIR}") + endif() + if (netCDFCxx_DIR) + set(netCDFCxx_ROOT "${netCDFCxx_DIR}") + endif() endif() message(STATUS "NetCDF support: ${BOUT_USE_NETCDF}") set(BOUT_HAS_NETCDF ${BOUT_USE_NETCDF}) @@ -299,6 +305,9 @@ if (BOUT_USE_SUNDIALS) message(FATAL_ERROR "SUNDIALS_VERSION 4.0.0 or newer is required. Found version ${SUNDIALS_VERSION}.") endif() endif() + if (SUNDIALS_DIR) + set(SUNDIALS_ROOT "${SUNDIALS_DIR}") + endif() target_link_libraries(bout++ PUBLIC SUNDIALS::nvecparallel) target_link_libraries(bout++ PUBLIC SUNDIALS::cvode) target_link_libraries(bout++ PUBLIC SUNDIALS::ida) From 1034ba5cfeb7910daba034160e97f7ff34122236 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:27:02 +0000 Subject: [PATCH 116/461] Switch use of `fmt` header Technically `base.h` is sufficient, but this causes backwards compatibility issues, and the compilation penalty is minor. --- src/sys/output.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/output.cxx b/src/sys/output.cxx index 2c5e06bb7c..0a9bb5f4c4 100644 --- a/src/sys/output.cxx +++ b/src/sys/output.cxx @@ -26,7 +26,7 @@ #include #include -#include +#include // NOLINT(misc-include-cleaner) #include #include From 489cde624f9ae93e31fffd6ab99de9a39735ce50 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Tue, 6 Jan 2026 17:19:07 +0000 Subject: [PATCH 117/461] Apply clang-format changes --- src/sys/output.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/output.cxx b/src/sys/output.cxx index 0a9bb5f4c4..97b58edec7 100644 --- a/src/sys/output.cxx +++ b/src/sys/output.cxx @@ -26,7 +26,7 @@ #include #include -#include // NOLINT(misc-include-cleaner) +#include // NOLINT(misc-include-cleaner) #include #include From d9e789cd3f67d0e883ab849a476360be367627a2 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 5 Dec 2025 13:30:23 +0000 Subject: [PATCH 118/461] Switch to `cpptrace` for stack traces in exceptions --- .clang-tidy | 2 +- CMakeLists.txt | 13 --- bout++Config.cmake.in | 7 +- cmake/SetupBOUTThirdParty.cmake | 16 ++++ cmake_build_defines.hxx.in | 4 +- include/bout/boutexception.hxx | 9 -- include/bout/physicsmodel.hxx | 3 +- manual/doxygen/Doxyfile | 1 - manual/doxygen/Doxyfile_readthedocs | 1 - src/sys/boutexception.cxx | 92 +++++-------------- .../integrated/test-backtrace/CMakeLists.txt | 4 +- tests/integrated/test-backtrace/runtest | 4 +- tests/unit/sys/test_boutexception.cxx | 25 ++--- 13 files changed, 61 insertions(+), 120 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index 23167e93d0..cf29b759cd 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -20,7 +20,7 @@ CheckOptions: value: 'MPI_Comm' - key: misc-include-cleaner.IgnoreHeaders - value: 'adios2/.*;bits/.*' + value: 'adios2/.*;bits/.*;cpptrace/.*' --- Disabled checks and reasons: diff --git a/CMakeLists.txt b/CMakeLists.txt index 44d100e444..7864705519 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -574,18 +574,6 @@ option(BOUT_ENABLE_SIGFPE "Signalling floating point exceptions" OFF) message(STATUS "Signalling floating point exceptions: BOUT_USE_SIGFPE=${BOUT_ENABLE_SIGFPE}") set(BOUT_USE_SIGFPE ${BOUT_ENABLE_SIGFPE}) -option(BOUT_ENABLE_BACKTRACE "Enable backtrace" ON) -if (BOUT_ENABLE_BACKTRACE) - find_program(ADDR2LINE_FOUND addr2line) - if (NOT ADDR2LINE_FOUND) - message(FATAL_ERROR "addr2line not found. Disable backtrace by setting BOUT_ENABLE_BACKTRACE=Off") - endif() - target_link_libraries(bout++ PUBLIC ${CMAKE_DL_LIBS}) - set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -l${CMAKE_DL_LIBS}") -endif() -message(STATUS "Enable backtrace: BOUT_USE_BACKTRACE=${BOUT_ENABLE_BACKTRACE}") -set(BOUT_USE_BACKTRACE ${BOUT_ENABLE_BACKTRACE}) - option(BOUT_ENABLE_METRIC_3D "Enable 3D metric support" OFF) if(BOUT_ENABLE_METRIC_3D) set(BOUT_METRIC_TYPE "3D") @@ -958,7 +946,6 @@ message(" Output coloring : ${BOUT_USE_COLOR} Field name tracking : ${BOUT_USE_TRACK} Floating point exceptions: ${BOUT_USE_SIGFPE} - Backtrace enabled : ${BOUT_USE_BACKTRACE} RAJA enabled : ${BOUT_HAS_RAJA} Umpire enabled : ${BOUT_HAS_UMPIRE} Caliper enabled : ${BOUT_HAS_CALIPER} diff --git a/bout++Config.cmake.in b/bout++Config.cmake.in index d461e58e4b..a82479f41c 100644 --- a/bout++Config.cmake.in +++ b/bout++Config.cmake.in @@ -6,7 +6,6 @@ set(BOUT_USE_SIGNAL @BOUT_USE_SIGNAL@) set(BOUT_USE_COLOR @BOUT_USE_COLOR@) set(BOUT_USE_TRACK @BOUT_USE_TRACK@) set(BOUT_USE_SIGFPE @BOUT_USE_SIGFPE@) -set(BOUT_USE_BACKTRACE @BOUT_USE_BACKTRACE@) set(BOUT_USE_OPENMP @BOUT_USE_OPENMP@) set(BOUT_HAS_CUDA @BOUT_HAS_CUDA@) set(BOUT_HAS_OUTPUT_DEBUG @BOUT_HAS_OUTPUT_DEBUG@) @@ -109,6 +108,11 @@ elseif(EXISTS "@ADIOS2_BINARY_DIR@") # If we downloaded ADIOS2, then we need to add its build directory to our search paths list(APPEND CMAKE_PREFIX_PATH "@ADIOS2_BINARY_DIR@") endif() +if(EXISTS "@cpptrace_ROOT@") + set(cpptrace_ROOT "@cpptrace_ROOT@") +elseif(EXISTS "@cpptrace_BINARY_DIR@") + list(APPEND CMAKE_PREFIX_PATH "@cpptrace_BINARY_DIR@") +endif() if(@BOUT_USE_SYSTEM_MPARK_VARIANT@) set(mpark_variant_ROOT "@mpark_variant_ROOT@") @@ -160,6 +164,7 @@ if (BOUT_HAS_GETTEXT) endif() find_dependency(mpark_variant @mpark_variant_VERSION@) find_dependency(fmt @fmt_VERSION@) +find_dependency(cpptrace @cpptrace_VERSION@) if (BOUT_HAS_SLEPC) find_dependency(SLEPc @SLEPC_VERSION@) endif() diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 1256fbbeba..2249fb3468 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -369,3 +369,19 @@ if (BOUT_USE_UUID_SYSTEM_GENERATOR) endif() message(STATUS "UUID_SYSTEM_GENERATOR: ${BOUT_USE_UUID_SYSTEM_GENERATOR}") set(BOUT_HAS_UUID_SYSTEM_GENERATOR ${BOUT_USE_UUID_SYSTEM_GENERATOR}) + +option(BOUT_DOWNLOAD_CPPTRACE "Download cpptrace for backtrace support" ON) +if (BOUT_DOWNLOAD_CPPTRACE) + include(FetchContent) + set(CPPTRACE_LIBDWARF_REPO "https://github.com/ZedThree/libdwarf-lite.git" CACHE STRING "" FORCE) + set(CPPTRACE_LIBDWARF_TAG "5f15f145d96278a64440f3c700f475233b4fd0e7" CACHE STRING "" FORCE) + FetchContent_Declare( + cpptrace + GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git + GIT_TAG v1.0.4 + ) + FetchContent_MakeAvailable(cpptrace) +else() + find_package(cpptrace REQUIRED) +endif() +target_link_libraries(bout++ PUBLIC cpptrace::cpptrace) diff --git a/cmake_build_defines.hxx.in b/cmake_build_defines.hxx.in index 4d63a01b7d..9c73679f29 100644 --- a/cmake_build_defines.hxx.in +++ b/cmake_build_defines.hxx.in @@ -21,7 +21,6 @@ #cmakedefine01 BOUT_HAS_SLEPC #cmakedefine01 BOUT_HAS_SUNDIALS #cmakedefine01 BOUT_HAS_UUID_SYSTEM_GENERATOR -#cmakedefine01 BOUT_USE_BACKTRACE #cmakedefine01 BOUT_USE_COLOR #cmakedefine01 BOUT_USE_OPENMP #cmakedefine01 BOUT_USE_OUTPUT_DEBUG @@ -39,4 +38,7 @@ // CMake build does not support legacy interface #define BOUT_HAS_LEGACY_NETCDF 0 +// We now always turn this on +#define BOUT_USE_BACKTRACE 1 + #endif // BOUT_BUILD_CONFIG_HXX diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index ffd4a03a6c..3d8a9cd71d 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -1,9 +1,6 @@ #ifndef BOUT_EXCEPTION_H #define BOUT_EXCEPTION_H -#include "bout/build_defines.hxx" - -#include #include #include #include @@ -37,12 +34,6 @@ public: private: std::string message; -#if BOUT_USE_BACKTRACE - static constexpr unsigned int TRACE_MAX = 128; - std::array trace{}; - int trace_size; - char** messages; -#endif }; class BoutRhsFail : public BoutException { diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index 9fa25d8b0f..baa284c862 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -433,8 +433,7 @@ private: solver->addMonitor(bout_monitor.get(), Solver::BACK); \ solver->solve(); \ } catch (const BoutException& e) { \ - output << "Error encountered: " << e.what(); \ - output << e.getBacktrace() << endl; \ + output.write("Error encountered: {}\n", e.what()); \ MPI_Abort(BoutComm::get(), 1); \ } \ BoutFinalise(); \ diff --git a/manual/doxygen/Doxyfile b/manual/doxygen/Doxyfile index e7998854b0..e2fef90a8c 100644 --- a/manual/doxygen/Doxyfile +++ b/manual/doxygen/Doxyfile @@ -2065,7 +2065,6 @@ PREDEFINED = BACKTRACE \ BOUT_HAS_SLEPC \ BOUT_HAS_SUNDIALS \ BOUT_HAS_UUID_SYSTEM_GENERATOR \ - BOUT_USE_BACKTRACE \ BOUT_USE_COLOR \ BOUT_USE_OPENMP \ BOUT_USE_OUTPUT_DEBUG \ diff --git a/manual/doxygen/Doxyfile_readthedocs b/manual/doxygen/Doxyfile_readthedocs index 73060bb6b4..94113490d9 100644 --- a/manual/doxygen/Doxyfile_readthedocs +++ b/manual/doxygen/Doxyfile_readthedocs @@ -1025,7 +1025,6 @@ PREDEFINED = BACKTRACE \ BOUT_HAS_SLEPC \ BOUT_HAS_SUNDIALS \ BOUT_HAS_UUID_SYSTEM_GENERATOR \ - BOUT_USE_BACKTRACE \ BOUT_USE_COLOR \ BOUT_USE_OPENMP \ BOUT_USE_OUTPUT_DEBUG \ diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index e81e395bf8..edf7509077 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -1,5 +1,3 @@ -#include "bout/build_defines.hxx" - #include #include #include @@ -7,13 +5,10 @@ #include -#if BOUT_USE_BACKTRACE -#include -#include -#include -#endif +#include // IWYU pragma: keep +#include +#include -#include #include #include #include @@ -33,16 +28,10 @@ void BoutParallelThrowRhsFail(int status, const char* message) { } } -BoutException::BoutException(std::string msg) - : message(std::move(msg)) -#if BOUT_USE_BACKTRACE - , - trace_size(backtrace(trace.data(), TRACE_MAX)), - messages(backtrace_symbols(trace.data(), trace_size)) -#endif -{ - if (std::getenv("BOUT_SHOW_BACKTRACE") != nullptr) { - message = fmt::format("{}\n{}", getBacktrace(), message); +BoutException::BoutException(std::string msg) : message(std::move(msg)) { + const char* show_backtrace = std::getenv("BOUT_SHOW_BACKTRACE"); + if (show_backtrace == nullptr or std::string{show_backtrace} != "0") { + message = getBacktrace(); } } @@ -51,62 +40,27 @@ BoutException::~BoutException() { // up the msg_stack. We also won't know how many messages to pop, so // just clear everything msg_stack.clear(); -#if BOUT_USE_BACKTRACE - // Call required for memory allocated by `backtrace_symbols` - free(messages); // NOLINT -#endif } std::string BoutException::getBacktrace() const { - std::string backtrace_message; -#if BOUT_USE_BACKTRACE - backtrace_message = "====== Exception path ======\n"; - // skip first stack frame (points here) - for (int i = trace_size - 1; i > 1; --i) { - backtrace_message = fmt::format(FMT_STRING("{}[bt] #{:d} {:s}\n"), backtrace_message, - i - 1, messages[i]); - // find first occurence of '(' or ' ' in message[i] and assume - // everything before that is the file name. (Don't go beyond 0 though - // (string terminator) - int p = 0; // snprintf %.*s expects int - while (messages[i][p] != '(' && messages[i][p] != ' ' && messages[i][p] != 0) { - ++p; - } + using namespace cpptrace; - // If we are compiled as PIE, need to get base pointer of .so and substract - Dl_info info; - const void* ptr = trace[i]; - if (dladdr(ptr, &info) != 0) { - // Additionally, check whether this is the default offset for an executable - if (info.dli_fbase != reinterpret_cast(0x400000)) { - ptr = reinterpret_cast(reinterpret_cast(ptr) - - reinterpret_cast(info.dli_fbase)); - } - } + const auto colours = isatty(stdout_fileno) || isatty(stderr_fileno) + ? formatter::color_mode::always + : formatter ::color_mode::none; - // Pipe stderr to /dev/null to avoid cluttering output - // when addr2line fails or is not installed - const auto syscom = fmt::format( - FMT_STRING("addr2line {:p} -Cfpie {:.{}s} 2> /dev/null"), ptr, messages[i], p); - // last parameter is the file name of the symbol - FILE* file = popen(syscom.c_str(), "r"); - if (file != nullptr) { - std::array out{}; - const char* retstr = nullptr; - std::string buf; - while ((retstr = fgets(out.data(), out.size() - 1, file)) != nullptr) { - buf += retstr; - } - int const status = pclose(file); - if (status == 0) { - backtrace_message = fmt::format("{}{}", backtrace_message, buf); - } - } - } -#else - backtrace_message = "Stacktrace not enabled.\n"; -#endif + auto formatter = cpptrace::formatter{} + .addresses(formatter::address_mode::none) + .break_before_filename(true) + .colors(colours) + .snippets(true) + .filter([](const stacktrace_frame& frame) { + return frame.symbol.find("BoutException::") == std::string::npos; + }) + .filtered_frame_placeholders(false); + + const std::string backtrace_message = formatter.format(generate_trace()); - return fmt::format("{}{}\n{}{}\n", backtrace_message, msg_stack.getDump(), header, + return fmt::format("{}\n{}\n{}{}\n", backtrace_message, msg_stack.getDump(), header, message); } diff --git a/tests/integrated/test-backtrace/CMakeLists.txt b/tests/integrated/test-backtrace/CMakeLists.txt index b20262f1b1..579f4863ac 100644 --- a/tests/integrated/test-backtrace/CMakeLists.txt +++ b/tests/integrated/test-backtrace/CMakeLists.txt @@ -1,6 +1,4 @@ bout_add_integrated_test(test-backtrace SOURCES boutexcept.cxx - REQUIRES BOUT_USE_BACKTRACE - CONFLICTS CMAKE_SYSTEM_NAME MATCHES "FreeBSD" USE_RUNTEST - ) +) diff --git a/tests/integrated/test-backtrace/runtest b/tests/integrated/test-backtrace/runtest index b41506175e..4010873b56 100755 --- a/tests/integrated/test-backtrace/runtest +++ b/tests/integrated/test-backtrace/runtest @@ -17,13 +17,13 @@ except KeyError: success = True errors = [] -_, output = shell("./boutexcept", pipe=True) +_, output = shell("BOUT_SHOW_BACKTRACE=0 ./boutexcept", pipe=True) if "troublemaker" in output: success = False print("Fail: detected offending function name in output when not expected") -_, output = shell("BOUT_SHOW_BACKTRACE=yes ./boutexcept", pipe=True) +_, output = shell("./boutexcept", pipe=True) if "troublemaker" not in output: success = False diff --git a/tests/unit/sys/test_boutexception.cxx b/tests/unit/sys/test_boutexception.cxx index 322c7d6e42..5eb2c4d719 100644 --- a/tests/unit/sys/test_boutexception.cxx +++ b/tests/unit/sys/test_boutexception.cxx @@ -1,5 +1,3 @@ -#include "bout/build_defines.hxx" - #include "test_extras.hxx" #include "bout/boutexception.hxx" #include "gtest/gtest.h" @@ -7,7 +5,6 @@ #include #include -#include #include TEST(BoutExceptionTest, ThrowCorrect) { @@ -19,13 +16,13 @@ TEST(BoutExceptionTest, What) { try { throw BoutException(test_message); } catch (const BoutException& e) { - EXPECT_EQ(e.what(), test_message); + EXPECT_TRUE(IsSubString(e.what(), test_message)); } try { throw BoutException("this is {}", "second"); } catch (const BoutException& e) { std::string message(e.what()); - EXPECT_EQ(message, "this is second"); + EXPECT_TRUE(IsSubString(message, "this is second")); } } @@ -34,25 +31,19 @@ TEST(BoutExceptionTest, GetBacktrace) { try { throw BoutException(test_message); } catch (const BoutException& e) { - std::string expected_1{"[bt] #1"}; - std::string expected_2{"serial_tests"}; -#if BOUT_USE_BACKTRACE + std::string expected_1{"#2"}; + std::string expected_2{"bout_test_main"}; // Should be able to find something about backtrace - EXPECT_TRUE(IsSubString(e.getBacktrace(), expected_1)); - EXPECT_TRUE(IsSubString(e.getBacktrace(), expected_2)); -#else - // Should *not* be able to find something about backtrace - EXPECT_FALSE(IsSubString(e.getBacktrace(), expected_1)); - EXPECT_FALSE(IsSubString(e.getBacktrace(), expected_2)); -#endif + EXPECT_TRUE(IsSubString(e.what(), expected_1)); + EXPECT_TRUE(IsSubString(e.what(), expected_2)); } } TEST(BoutExceptionTest, FmtJoin) { const std::vector things = {1, 2, 3, 4}; - constexpr std::string_view expected = "list: 1, 2, 3, 4"; + const std::string expected = "list: 1, 2, 3, 4"; const BoutException exception{"list: {}", fmt::join(things, ", ")}; - EXPECT_EQ(exception.what(), expected); + EXPECT_TRUE(IsSubString(std::string{exception.what()}, expected)); } TEST(BoutRhsFailTest, ThrowCorrect) { From 199ba5ae60a58c63722a81b8e0a51bdf0abdfbbd Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 5 Dec 2025 14:35:13 +0000 Subject: [PATCH 119/461] Add some control over `BoutException` backtraces Always generating the backtraces can slow the unit tests down by a factor x2-3 An alternative might be to store the `cpptrace:stacktrace` and only convert to string in `BoutException::what()` --- include/bout/boutexception.hxx | 5 +++++ src/sys/boutexception.cxx | 11 +++++++++-- tests/unit/bout_test_main.cxx | 5 +++++ tests/unit/sys/test_boutexception.cxx | 14 ++++++++++---- tests/unit/test_extras.cxx | 3 ++- 5 files changed, 31 insertions(+), 7 deletions(-) diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index 3d8a9cd71d..565eb6b46d 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -32,8 +32,13 @@ public: /// backtrace (if available) std::string getBacktrace() const; + static void enableBacktrace() { show_backtrace = true; } + static void disableBacktrace() { show_backtrace = false; } + private: std::string message; + + static bool show_backtrace; }; class BoutRhsFail : public BoutException { diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index edf7509077..f08679de83 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -19,6 +19,8 @@ namespace { const std::string header{"====== Exception thrown ======\n"}; } +bool BoutException::show_backtrace = true; + void BoutParallelThrowRhsFail(int status, const char* message) { int allstatus = 0; MPI_Allreduce(&status, &allstatus, 1, MPI_INT, MPI_LOR, BoutComm::get()); @@ -29,8 +31,13 @@ void BoutParallelThrowRhsFail(int status, const char* message) { } BoutException::BoutException(std::string msg) : message(std::move(msg)) { - const char* show_backtrace = std::getenv("BOUT_SHOW_BACKTRACE"); - if (show_backtrace == nullptr or std::string{show_backtrace} != "0") { + const char* show_backtrace_env_var = std::getenv("BOUT_SHOW_BACKTRACE"); + const auto should_show_backtrace = + show_backtrace + and ((show_backtrace_env_var == nullptr) + or (show_backtrace_env_var != nullptr + and std::string{show_backtrace_env_var} != "0")); + if (should_show_backtrace) { message = getBacktrace(); } } diff --git a/tests/unit/bout_test_main.cxx b/tests/unit/bout_test_main.cxx index 999b5b0dc3..09aeeed0ea 100644 --- a/tests/unit/bout_test_main.cxx +++ b/tests/unit/bout_test_main.cxx @@ -1,6 +1,7 @@ #include #include "bout/array.hxx" +#include "bout/boutexception.hxx" #include "bout/fft.hxx" #include "bout/globalindexer.hxx" #include "bout/output.hxx" @@ -22,6 +23,10 @@ GTEST_API_ int main(int argc, char** argv) { printf("Running main() from bout_test_main.cxx\n"); testing::InitGoogleTest(&argc, argv); + // We throw and catch a bunch of exceptions as part of running the tests, and + // the backtrace generation can _significantly_ slow them down + BoutException::disableBacktrace(); + // Explicitly setup and teardown PETSc to avoid reentry problems // with certain MPI implementations (see #1916 for details) output.disable(); diff --git a/tests/unit/sys/test_boutexception.cxx b/tests/unit/sys/test_boutexception.cxx index 5eb2c4d719..1ba12ef943 100644 --- a/tests/unit/sys/test_boutexception.cxx +++ b/tests/unit/sys/test_boutexception.cxx @@ -7,11 +7,17 @@ #include #include -TEST(BoutExceptionTest, ThrowCorrect) { +// NOLINTNEXTLINE(cppcoreguidelines-special-member-functions) +struct BoutExceptionTest : public ::testing::Test { + BoutExceptionTest() { BoutException::enableBacktrace(); } + ~BoutExceptionTest() override { BoutException::disableBacktrace(); } +}; + +TEST_F(BoutExceptionTest, ThrowCorrect) { EXPECT_THROW(throw BoutException("test"), BoutException); } -TEST(BoutExceptionTest, What) { +TEST_F(BoutExceptionTest, What) { std::string test_message{"Test message"}; try { throw BoutException(test_message); @@ -26,7 +32,7 @@ TEST(BoutExceptionTest, What) { } } -TEST(BoutExceptionTest, GetBacktrace) { +TEST_F(BoutExceptionTest, GetBacktrace) { std::string test_message{"Test message"}; try { throw BoutException(test_message); @@ -39,7 +45,7 @@ TEST(BoutExceptionTest, GetBacktrace) { } } -TEST(BoutExceptionTest, FmtJoin) { +TEST_F(BoutExceptionTest, FmtJoin) { const std::vector things = {1, 2, 3, 4}; const std::string expected = "list: 1, 2, 3, 4"; const BoutException exception{"list: {}", fmt::join(things, ", ")}; diff --git a/tests/unit/test_extras.cxx b/tests/unit/test_extras.cxx index 332a53089b..b1caf038d3 100644 --- a/tests/unit/test_extras.cxx +++ b/tests/unit/test_extras.cxx @@ -14,7 +14,8 @@ ::testing::AssertionResult IsSubString(const std::string& str, if (str.find(substring) != std::string::npos) { return ::testing::AssertionSuccess(); } else { - return ::testing::AssertionFailure() << '"' << substring << "\" not found in " << str; + return ::testing::AssertionFailure() + << '"' << substring << "\" not found in \"" << str << '"'; } } From dc865d338a9b62f2406375410426723dc009147c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 5 Dec 2025 15:01:05 +0000 Subject: [PATCH 120/461] Try to ensure `build_defines` is included where it's used --- include/bout/coordinates.hxx | 9 +++++---- include/bout/fv_ops.hxx | 4 ++-- include/bout/interpolation_xz.hxx | 1 + include/bout/output.hxx | 3 ++- src/invert/laplace/invert_laplace.cxx | 1 + src/mesh/boundary_standard.cxx | 1 + src/mesh/coordinates.cxx | 1 + src/mesh/coordinates_accessor.cxx | 1 + src/sys/derivs.cxx | 1 + tests/unit/field/test_vector2d.cxx | 6 ++++-- tests/unit/include/bout/test_single_index_ops.cxx | 1 + tests/unit/mesh/data/test_gridfromoptions.cxx | 2 +- tests/unit/solver/test_solver.cxx | 1 + tests/unit/sys/test_output.cxx | 4 ++-- 14 files changed, 24 insertions(+), 12 deletions(-) diff --git a/include/bout/coordinates.hxx b/include/bout/coordinates.hxx index d7c80ed8bc..e7ead42ee5 100644 --- a/include/bout/coordinates.hxx +++ b/include/bout/coordinates.hxx @@ -33,10 +33,11 @@ #ifndef BOUT_COORDINATES_H #define BOUT_COORDINATES_H -#include "bout/bout_types.hxx" -#include "bout/field2d.hxx" -#include "bout/field3d.hxx" -#include "bout/paralleltransform.hxx" +#include +#include +#include +#include +#include class Mesh; diff --git a/include/bout/fv_ops.hxx b/include/bout/fv_ops.hxx index 94007a57a2..ce98e03008 100644 --- a/include/bout/fv_ops.hxx +++ b/include/bout/fv_ops.hxx @@ -5,11 +5,11 @@ #ifndef BOUT_FV_OPS_H #define BOUT_FV_OPS_H +#include "bout/build_defines.hxx" #include "bout/field3d.hxx" #include "bout/globals.hxx" -#include "bout/vector2d.hxx" - #include "bout/utils.hxx" +#include "bout/vector2d.hxx" #include namespace FV { diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 6c7419f7e4..79e959b3af 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -24,6 +24,7 @@ #ifndef BOUT_INTERP_XZ_H #define BOUT_INTERP_XZ_H +#include "bout/build_defines.hxx" #include "bout/mask.hxx" #define USE_NEW_WEIGHTS 1 diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 823e359753..34b4f19376 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -29,12 +29,13 @@ class Output; #ifndef BOUT_OUTPUT_H #define BOUT_OUTPUT_H -#include "bout/multiostream.hxx" #include #include #include #include "bout/assert.hxx" +#include "bout/build_defines.hxx" +#include "bout/multiostream.hxx" #include "bout/sys/gettext.hxx" // IWYU pragma: keep for gettext _() macro #include "bout/unused.hxx" diff --git a/src/invert/laplace/invert_laplace.cxx b/src/invert/laplace/invert_laplace.cxx index 4032499781..7a8af20fbd 100644 --- a/src/invert/laplace/invert_laplace.cxx +++ b/src/invert/laplace/invert_laplace.cxx @@ -33,6 +33,7 @@ #include #include +#include #include #include #include diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index 8f2c7df5ec..5508f8db71 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -1,5 +1,6 @@ #include #include +#include #include #include #include diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 3dfee6a553..58d54f826e 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -5,6 +5,7 @@ **************************************************************************/ #include +#include #include #include #include diff --git a/src/mesh/coordinates_accessor.cxx b/src/mesh/coordinates_accessor.cxx index aff546c2b0..0ce4b664b5 100644 --- a/src/mesh/coordinates_accessor.cxx +++ b/src/mesh/coordinates_accessor.cxx @@ -1,4 +1,5 @@ #include "bout/coordinates_accessor.hxx" +#include #include "bout/mesh.hxx" diff --git a/src/sys/derivs.cxx b/src/sys/derivs.cxx index ee9bcbcc2c..ed036fffd4 100644 --- a/src/sys/derivs.cxx +++ b/src/sys/derivs.cxx @@ -38,6 +38,7 @@ * **************************************************************************/ +#include #include #include #include diff --git a/tests/unit/field/test_vector2d.cxx b/tests/unit/field/test_vector2d.cxx index df438c3dbf..de662c7df8 100644 --- a/tests/unit/field/test_vector2d.cxx +++ b/tests/unit/field/test_vector2d.cxx @@ -1,8 +1,8 @@ -#include "gtest/gtest.h" +#include -#include "bout/boutexception.hxx" #if not(BOUT_USE_METRIC_3D) #include "test_extras.hxx" +#include "bout/boutexception.hxx" #include "bout/mesh.hxx" #include "bout/mpi_wrapper.hxx" #include "bout/output.hxx" @@ -11,6 +11,8 @@ #include "fake_mesh.hxx" +#include "gtest/gtest.h" + // The unit tests use the global mesh using namespace bout::globals; diff --git a/tests/unit/include/bout/test_single_index_ops.cxx b/tests/unit/include/bout/test_single_index_ops.cxx index 4359d1d282..a809ae3683 100644 --- a/tests/unit/include/bout/test_single_index_ops.cxx +++ b/tests/unit/include/bout/test_single_index_ops.cxx @@ -2,6 +2,7 @@ #include "test_extras.hxx" +#include "bout/build_defines.hxx" #include "bout/derivs.hxx" #include "bout/difops.hxx" #include "bout/single_index_ops.hxx" diff --git a/tests/unit/mesh/data/test_gridfromoptions.cxx b/tests/unit/mesh/data/test_gridfromoptions.cxx index 8d3dbfebdf..b821d2e225 100644 --- a/tests/unit/mesh/data/test_gridfromoptions.cxx +++ b/tests/unit/mesh/data/test_gridfromoptions.cxx @@ -1,13 +1,13 @@ #include "gtest/gtest.h" #include "test_extras.hxx" +#include "bout/build_defines.hxx" #include "bout/constants.hxx" #include "bout/griddata.hxx" #include "bout/mesh.hxx" #include "bout/options.hxx" #include "bout/output.hxx" -#include #include #include diff --git a/tests/unit/solver/test_solver.cxx b/tests/unit/solver/test_solver.cxx index 2e29fd90bc..e9a612103c 100644 --- a/tests/unit/solver/test_solver.cxx +++ b/tests/unit/solver/test_solver.cxx @@ -4,6 +4,7 @@ #include "test_extras.hxx" #include "test_fakesolver.hxx" #include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" #include "bout/physicsmodel.hxx" diff --git a/tests/unit/sys/test_output.cxx b/tests/unit/sys/test_output.cxx index 959823ede3..2f13df9cb7 100644 --- a/tests/unit/sys/test_output.cxx +++ b/tests/unit/sys/test_output.cxx @@ -1,7 +1,7 @@ #include "test_tmpfiles.hxx" -#include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/output.hxx" -#include "bout/output_bout_types.hxx" +#include "bout/output_bout_types.hxx" // IWYU pragma: keep #include "gtest/gtest.h" #include From e0710f8b88ad3ed4cdfadb731e446cd4dcaac75d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 16 Dec 2025 15:43:49 +0000 Subject: [PATCH 121/461] Try to make backtrace unit test more portable --- tests/unit/sys/test_boutexception.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/tests/unit/sys/test_boutexception.cxx b/tests/unit/sys/test_boutexception.cxx index 1ba12ef943..7cde2f57a1 100644 --- a/tests/unit/sys/test_boutexception.cxx +++ b/tests/unit/sys/test_boutexception.cxx @@ -37,11 +37,11 @@ TEST_F(BoutExceptionTest, GetBacktrace) { try { throw BoutException(test_message); } catch (const BoutException& e) { - std::string expected_1{"#2"}; - std::string expected_2{"bout_test_main"}; // Should be able to find something about backtrace - EXPECT_TRUE(IsSubString(e.what(), expected_1)); - EXPECT_TRUE(IsSubString(e.what(), expected_2)); + EXPECT_TRUE(IsSubString(e.what(), "#2")); + // We should be able to see either the main source file or the executable name + EXPECT_TRUE(IsSubString(e.what(), "bout_test_main") + || IsSubString(e.what(), "serial_tests")); } } From 9dd86c53443c8a6253cebab4fbff65bbf03ecebd Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 11:52:10 +0000 Subject: [PATCH 122/461] cmake: Fix some issues with building with cpptrace - Add paths to `FetchContent` dirs in config file - Switch to custom versions of cpptrace/libdward-lite - Includes some cmake fixes in both projects --- bout++Config.cmake.in | 3 +++ cmake/SetupBOUTThirdParty.cmake | 6 +++--- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/bout++Config.cmake.in b/bout++Config.cmake.in index a82479f41c..a8cf041901 100644 --- a/bout++Config.cmake.in +++ b/bout++Config.cmake.in @@ -112,6 +112,9 @@ if(EXISTS "@cpptrace_ROOT@") set(cpptrace_ROOT "@cpptrace_ROOT@") elseif(EXISTS "@cpptrace_BINARY_DIR@") list(APPEND CMAKE_PREFIX_PATH "@cpptrace_BINARY_DIR@") + list(APPEND CMAKE_PREFIX_PATH "@cpptrace_BINARY_DIR@/cmake") + list(APPEND CMAKE_PREFIX_PATH "@libdwarf_BINARY_DIR@/src/lib/libdwarf") + list(APPEND CMAKE_PREFIX_PATH "@zstd_BINARY_DIR@") endif() if(@BOUT_USE_SYSTEM_MPARK_VARIANT@) diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 2249fb3468..2c5b9a1cfb 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -374,11 +374,11 @@ option(BOUT_DOWNLOAD_CPPTRACE "Download cpptrace for backtrace support" ON) if (BOUT_DOWNLOAD_CPPTRACE) include(FetchContent) set(CPPTRACE_LIBDWARF_REPO "https://github.com/ZedThree/libdwarf-lite.git" CACHE STRING "" FORCE) - set(CPPTRACE_LIBDWARF_TAG "5f15f145d96278a64440f3c700f475233b4fd0e7" CACHE STRING "" FORCE) + set(CPPTRACE_LIBDWARF_TAG "ebe10a39afd56b8247de633bfe17666ad50ab95e" CACHE STRING "" FORCE) FetchContent_Declare( cpptrace - GIT_REPOSITORY https://github.com/jeremy-rifkin/cpptrace.git - GIT_TAG v1.0.4 + GIT_REPOSITORY https://github.com/ZedThree/cpptrace.git + GIT_TAG "027f9aee2d34dbe1c98f26224e1fbe1654cb4aae" ) FetchContent_MakeAvailable(cpptrace) else() From 00995ea544670a409f584015ab4e07485683b86b Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 12:03:50 +0000 Subject: [PATCH 123/461] Prettify symbols in cpptrace output --- src/sys/boutexception.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index f08679de83..5bcec420f3 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -54,13 +54,14 @@ std::string BoutException::getBacktrace() const { const auto colours = isatty(stdout_fileno) || isatty(stderr_fileno) ? formatter::color_mode::always - : formatter ::color_mode::none; + : formatter::color_mode::none; auto formatter = cpptrace::formatter{} .addresses(formatter::address_mode::none) .break_before_filename(true) .colors(colours) .snippets(true) + .symbols(formatter::symbol_mode::pretty) .filter([](const stacktrace_frame& frame) { return frame.symbol.find("BoutException::") == std::string::npos; }) From 3fc530d5c45e719546c929a831d1f6daf8eabbce Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 12:11:03 +0000 Subject: [PATCH 124/461] Filter out pre-main functions from stacktrace --- src/sys/boutexception.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index 5bcec420f3..c91bb186bf 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -63,7 +63,12 @@ std::string BoutException::getBacktrace() const { .snippets(true) .symbols(formatter::symbol_mode::pretty) .filter([](const stacktrace_frame& frame) { - return frame.symbol.find("BoutException::") == std::string::npos; + return ( + // Don't include our exception machinery + (frame.symbol.find("BoutException::") == std::string::npos) + // Don't include pre-main functions + and (frame.symbol.find("__libc_start") == std::string::npos) + and (frame.symbol != "_start")); }) .filtered_frame_placeholders(false); From 5f77485d249c17f76b453ee7fe4cb08aea423cdb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 12:12:48 +0000 Subject: [PATCH 125/461] Include filtered placeholders --- src/sys/boutexception.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index c91bb186bf..8509dc40ba 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -69,8 +69,7 @@ std::string BoutException::getBacktrace() const { // Don't include pre-main functions and (frame.symbol.find("__libc_start") == std::string::npos) and (frame.symbol != "_start")); - }) - .filtered_frame_placeholders(false); + }); const std::string backtrace_message = formatter.format(generate_trace()); From cbc4cf20bb3effb66a1656a9fea9424a5e228350 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 15:53:29 +0000 Subject: [PATCH 126/461] Remove `AUTO_TRACE` macro --- include/bout/bout_enum_class.hxx | 4 +- include/bout/deriv_store.hxx | 39 +++++++------------ include/bout/field.hxx | 11 +----- include/bout/hypre_interface.hxx | 6 +-- include/bout/index_derivs.hxx | 6 +-- include/bout/index_derivs_interface.hxx | 31 +++++++-------- include/bout/interpolation.hxx | 2 +- include/bout/invertable_operator.hxx | 13 +++---- include/bout/mesh.hxx | 6 +-- include/bout/msg_stack.hxx | 31 --------------- include/bout/petsc_interface.hxx | 6 +-- include/bout/scorepwrapper.hxx | 13 ++++++- include/bout/sys/timer.hxx | 1 - manual/doxygen/Doxyfile | 1 - manual/doxygen/Doxyfile_readthedocs | 1 - manual/sphinx/developer_docs/debugging.rst | 14 ++----- src/bout++.cxx | 3 -- src/field/field_data.cxx | 7 +--- src/field/field_factory.cxx | 3 -- src/field/initialprofiles.cxx | 4 -- src/field/vecops.cxx | 4 +- .../laplace/impls/petsc3damg/petsc3damg.cxx | 1 - src/mesh/data/gridfromfile.cxx | 1 - src/mesh/index_derivs.cxx | 12 +++--- src/mesh/parallel/shiftedmetricinterp.cxx | 1 - src/physics/smoothing.cxx | 2 +- .../impls/adams_bashforth/adams_bashforth.cxx | 31 +++++++-------- src/solver/impls/split-rk/split-rk.cxx | 2 - src/solver/solver.cxx | 4 +- src/sys/bout_types.cxx | 26 ++++++------- src/sys/msg_stack.cxx | 2 +- .../integrated/test-backtrace/boutexcept.cxx | 15 ++----- 32 files changed, 107 insertions(+), 196 deletions(-) diff --git a/include/bout/bout_enum_class.hxx b/include/bout/bout_enum_class.hxx index 585e5b020e..07140220a4 100644 --- a/include/bout/bout_enum_class.hxx +++ b/include/bout/bout_enum_class.hxx @@ -70,7 +70,7 @@ enum class enumname { __VA_ARGS__ }; \ \ inline std::string toString(enumname e) { \ - AUTO_TRACE(); \ + \ const static std::map toString_map = { \ BOUT_ENUM_CLASS_MAP_ARGS(BOUT_ENUM_CLASS_STR, enumname, __VA_ARGS__)}; \ auto found = toString_map.find(e); \ @@ -81,7 +81,7 @@ } \ \ inline enumname BOUT_MAKE_FROMSTRING_NAME(enumname)(const std::string& s) { \ - AUTO_TRACE(); \ + \ const static std::map fromString_map = { \ BOUT_ENUM_CLASS_MAP_ARGS(BOUT_STR_ENUM_CLASS, enumname, __VA_ARGS__)}; \ auto found = fromString_map.find(s); \ diff --git a/include/bout/deriv_store.hxx b/include/bout/deriv_store.hxx index 6dc44c76ad..866ac558e5 100644 --- a/include/bout/deriv_store.hxx +++ b/include/bout/deriv_store.hxx @@ -75,21 +75,14 @@ struct DerivativeStore { } /// Report if store has any registered methods - bool isEmpty() const { - AUTO_TRACE(); - return registeredMethods.empty(); - }; + bool isEmpty() const { return registeredMethods.empty(); }; /// Report if store has any registered methods for specific type determined by key - bool isEmpty(std::size_t key) const { - AUTO_TRACE(); - return registeredMethods.count(key) == 0; - } + bool isEmpty(std::size_t key) const { return registeredMethods.count(key) == 0; } /// Report if store has any registered methods for specific type bool isEmpty(DERIV derivType, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); // Get the key auto key = getKey(direction, stagger, toString(derivType)); @@ -100,7 +93,6 @@ struct DerivativeStore { /// specified derivative type, direction and stagger. std::set getAvailableMethods(DERIV derivType, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); // Get the key auto key = getKey(direction, stagger, toString(derivType)); @@ -115,7 +107,6 @@ struct DerivativeStore { /// specified derivative type, direction and stagger. void listAvailableMethods(DERIV derivType, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); // Introductory information output_info << "Available methods for derivative type '"; @@ -134,7 +125,7 @@ struct DerivativeStore { /// depends on the derivType input. void registerDerivative(standardFunc func, DERIV derivType, DIRECTION direction, STAGGER stagger, std::string methodName) { - AUTO_TRACE(); + const auto key = getKey(direction, stagger, methodName); switch (derivType) { @@ -176,7 +167,7 @@ struct DerivativeStore { /// depends on the derivType input. void registerDerivative(upwindFunc func, DERIV derivType, DIRECTION direction, STAGGER stagger, std::string methodName) { - AUTO_TRACE(); + const auto key = getKey(direction, stagger, methodName); switch (derivType) { @@ -210,14 +201,14 @@ struct DerivativeStore { template void registerDerivative(standardFunc func, Direction direction, Stagger stagger, Method method) { - AUTO_TRACE(); + registerDerivative(func, method.meta.derivType, direction.lookup(), stagger.lookup(), method.meta.key); } template void registerDerivative(upwindFunc func, Direction direction, Stagger stagger, Method method) { - AUTO_TRACE(); + registerDerivative(func, method.meta.derivType, direction.lookup(), stagger.lookup(), method.meta.key); } @@ -231,7 +222,6 @@ struct DerivativeStore { STAGGER stagger = STAGGER::None, DERIV derivType = DERIV::Standard) const { - AUTO_TRACE(); const auto realName = nameLookup( name, defaultMethods.at(getKey(direction, stagger, toString(derivType)))); const auto key = getKey(direction, stagger, realName); @@ -262,20 +252,20 @@ struct DerivativeStore { standardFunc getStandard2ndDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getStandardDerivative(name, direction, stagger, DERIV::StandardSecond); }; standardFunc getStandard4thDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getStandardDerivative(name, direction, stagger, DERIV::StandardFourth); }; flowFunc getFlowDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None, DERIV derivType = DERIV::Upwind) const { - AUTO_TRACE(); + const auto realName = nameLookup( name, defaultMethods.at(getKey(direction, stagger, toString(derivType)))); const auto key = getKey(direction, stagger, realName); @@ -305,18 +295,17 @@ struct DerivativeStore { upwindFunc getUpwindDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getFlowDerivative(name, direction, stagger, DERIV::Upwind); }; fluxFunc getFluxDerivative(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return getFlowDerivative(name, direction, stagger, DERIV::Flux); }; void initialise(Options* options) { - AUTO_TRACE(); // To replicate the existing behaviour we first search for a section called //"dd?" and if the option isn't in there we search a section called "diff" @@ -490,7 +479,7 @@ private: std::string getMethodName(std::string name, DIRECTION direction, STAGGER stagger = STAGGER::None) const { - AUTO_TRACE(); + return name + " (" + toString(direction) + ", " + toString(stagger) + ")"; }; @@ -506,7 +495,7 @@ private: /// methods with the same function interface in the same map, which /// might be nice. std::size_t getKey(DIRECTION direction, STAGGER stagger, std::string key) const { - AUTO_TRACE(); + // Note this key is indepedent of the field type (and hence the // key is the same for 3D/2D fields) as we have to use different // maps to store the different field types as the signature is @@ -524,7 +513,7 @@ private: /// that can be used to account for run-time choices template std::size_t getKey() const { - AUTO_TRACE(); + // Note this key is indepedent of the field type (and hence the // key is the same for 3D/2D fields) as we have to use different // maps to store the different field types as the signature is diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 61edff6723..35ce42716d 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -239,7 +239,6 @@ namespace bout { template inline void checkFinite(const T& f, const std::string& name = "field", const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); if (!f.isAllocated()) { throw BoutException("{:s} is not allocated", name); @@ -263,7 +262,6 @@ inline void checkFinite(const T& f, const std::string& name = "field", template inline void checkPositive(const T& f, const std::string& name = "field", const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); if (!f.isAllocated()) { throw BoutException("{:s} is not allocated", name); @@ -307,7 +305,6 @@ inline T fromFieldAligned(const T& f, const std::string& region = "RGN_ALL") { template > inline BoutReal min(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { - AUTO_TRACE(); checkData(f); @@ -392,7 +389,6 @@ inline BoutReal getUniform(const T& f, [[maybe_unused]] bool allpe = false, template > inline BoutReal max(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { - AUTO_TRACE(); checkData(f); @@ -426,7 +422,6 @@ inline BoutReal max(const T& f, bool allpe = false, template > inline BoutReal mean(const T& f, bool allpe = false, const std::string& rgn = "RGN_NOBNDRY") { - AUTO_TRACE(); checkData(f); @@ -457,7 +452,6 @@ inline BoutReal mean(const T& f, bool allpe = false, /// If CHECK >= 3 then the result will be checked for non-finite numbers template > T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); ASSERT1(areFieldsCompatible(lhs, rhs)); @@ -471,7 +465,6 @@ T pow(const T& lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { template > T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); // Check if the inputs are allocated checkData(lhs); @@ -487,7 +480,6 @@ T pow(const T& lhs, BoutReal rhs, const std::string& rgn = "RGN_ALL") { template > T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); // Check if the inputs are allocated checkData(lhs); @@ -524,7 +516,7 @@ T pow(BoutReal lhs, const T& rhs, const std::string& rgn = "RGN_ALL") { #define FIELD_FUNC(name, func) \ template > \ inline T name(const T& f, const std::string& rgn = "RGN_ALL") { \ - AUTO_TRACE(); \ + \ /* Check if the input is allocated */ \ checkData(f); \ /* Define and allocate the output result */ \ @@ -632,7 +624,6 @@ FIELD_FUNC(tanh, ::tanh) /// default (can be changed using the \p rgn argument template > inline bool finite(const T& f, const std::string& rgn = "RGN_ALL") { - AUTO_TRACE(); if (!f.isAllocated()) { return false; diff --git a/include/bout/hypre_interface.hxx b/include/bout/hypre_interface.hxx index ae392de4f3..65d8875a9a 100644 --- a/include/bout/hypre_interface.hxx +++ b/include/bout/hypre_interface.hxx @@ -444,19 +444,19 @@ public: value = matrix->getVal(row, column); } Element& operator=(const Element& other) { - AUTO_TRACE(); + ASSERT3(finite(static_cast(other))); return *this = static_cast(other); } Element& operator=(BoutReal value_) { - AUTO_TRACE(); + ASSERT3(finite(value_)); value = value_; setValues(value); return *this; } Element& operator+=(BoutReal value_) { - AUTO_TRACE(); + ASSERT3(finite(value_)); auto column_position = std::find(cbegin(positions), cend(positions), column); if (column_position != cend(positions)) { diff --git a/include/bout/index_derivs.hxx b/include/bout/index_derivs.hxx index 456f98f8c2..7c2be52ded 100644 --- a/include/bout/index_derivs.hxx +++ b/include/bout/index_derivs.hxx @@ -84,7 +84,7 @@ class DerivativeType { public: template void standard(const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::Standard || meta.derivType == DERIV::StandardSecond || meta.derivType == DERIV::StandardFourth) ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); @@ -98,7 +98,7 @@ public: template void upwindOrFlux(const T& vel, const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::Upwind || meta.derivType == DERIV::Flux) ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); @@ -133,7 +133,7 @@ struct registerMethod { template void operator()(Direction, Stagger, FieldTypeContainer, Method) { - AUTO_TRACE(); + using namespace std::placeholders; // Now we want to get the actual field type out of the TypeContainer diff --git a/include/bout/index_derivs_interface.hxx b/include/bout/index_derivs_interface.hxx index 8f7e41a68e..242492c3f8 100644 --- a/include/bout/index_derivs_interface.hxx +++ b/include/bout/index_derivs_interface.hxx @@ -45,7 +45,6 @@ namespace index { template T flowDerivative(const T& vel, const T& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - AUTO_TRACE(); // Checks static_assert(bout::utils::is_Field2D_v || bout::utils::is_Field3D_v, @@ -110,7 +109,6 @@ T flowDerivative(const T& vel, const T& f, CELL_LOC outloc, const std::string& m template T standardDerivative(const T& f, CELL_LOC outloc, const std::string& method, const std::string& region) { - AUTO_TRACE(); // Checks static_assert(bout::utils::is_Field2D_v || bout::utils::is_Field3D_v, @@ -174,14 +172,14 @@ T standardDerivative(const T& f, CELL_LOC outloc, const std::string& method, template T DDX(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } template T D2DX2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -189,7 +187,7 @@ T D2DX2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T D4DX4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -199,7 +197,7 @@ T D4DX4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T DDY(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + if (f.hasParallelSlices()) { ASSERT1(f.getDirectionY() == YDirectionType::Standard); return standardDerivative(f, outloc, @@ -216,7 +214,7 @@ T DDY(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "D template T D2DY2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + if (f.hasParallelSlices()) { ASSERT1(f.getDirectionY() == YDirectionType::Standard); return standardDerivative( @@ -233,7 +231,7 @@ T D2DY2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T D4DY4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + if (f.hasParallelSlices()) { ASSERT1(f.getDirectionY() == YDirectionType::Standard); return standardDerivative( @@ -251,14 +249,14 @@ T D4DY4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T DDZ(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } template T D2DZ2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -266,7 +264,7 @@ T D2DZ2(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T D4DZ4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return standardDerivative(f, outloc, method, region); } @@ -291,14 +289,14 @@ T D4DZ4(const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = template T VDDX(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } template T FDDX(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } @@ -307,7 +305,6 @@ T FDDX(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, template T VDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); // Note the following chunk is copy+pasted from flowDerivative // above. Not pulled out as a separate function due the number of @@ -363,7 +360,7 @@ T VDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, template T FDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + const bool fHasParallelSlices = (f.hasParallelSlices()); const bool velHasParallelSlices = (vel.hasParallelSlices()); if (fHasParallelSlices && velHasParallelSlices) { @@ -389,14 +386,14 @@ T FDDY(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, template T VDDZ(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } template T FDDZ(const T& vel, const T& f, CELL_LOC outloc = CELL_DEFAULT, const std::string& method = "DEFAULT", const std::string& region = "RGN_NOBNDRY") { - AUTO_TRACE(); + return flowDerivative(vel, f, outloc, method, region); } diff --git a/include/bout/interpolation.hxx b/include/bout/interpolation.hxx index 2c7df4472d..1753dfe0c0 100644 --- a/include/bout/interpolation.hxx +++ b/include/bout/interpolation.hxx @@ -56,7 +56,7 @@ inline BoutReal interp(const stencil& s) { */ template const T interp_to(const T& var, CELL_LOC loc, const std::string region = "RGN_ALL") { - AUTO_TRACE(); + static_assert(bout::utils::is_Field2D_v || bout::utils::is_Field3D_v, "interp_to must be templated with one of Field2D or Field3D."); ASSERT1(loc != CELL_DEFAULT); // doesn't make sense to interplote to CELL_DEFAULT diff --git a/include/bout/invertable_operator.hxx b/include/bout/invertable_operator.hxx index 9b9dbba41a..ffc0910dce 100644 --- a/include/bout/invertable_operator.hxx +++ b/include/bout/invertable_operator.hxx @@ -61,7 +61,7 @@ namespace inversion { /// No-op function to use as a default -- may wish to remove once testing phase complete template T identity(const T& in) { - AUTO_TRACE(); + return in; }; @@ -137,8 +137,8 @@ public: opt(optIn == nullptr ? Options::getRoot()->getSection("invertableOperator") : optIn), localmesh(localmeshIn == nullptr ? bout::globals::mesh : localmeshIn), lib(opt) { - AUTO_TRACE(); - }; + + }; /// Destructor just has to cleanup the PETSc owned objects. ~InvertableOperator() { @@ -177,10 +177,7 @@ public: } /// Provide a synonym for applying the operator to a Field - T apply(const T& input) { - AUTO_TRACE(); - return operator()(input); - } + T apply(const T& input) { return operator()(input); } /// Sets up the PETSc objects required for inverting the operator /// Currently also takes the functor that applies the operator this class @@ -386,7 +383,7 @@ public: // but suspect it's not as there are KSPGuess objects // to deal with. T invert(const T& rhsField, const T& guess) { - AUTO_TRACE(); + auto ierr = fieldToPetscVec(guess, lhs); CHKERRQ(ierr); return invert(rhsField); diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 2e27bed777..0d9471d1d2 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -652,7 +652,7 @@ public: /// Returns the non-CELL_CENTRE location /// allowed as a staggered location static CELL_LOC getAllowedStaggerLoc(DIRECTION direction) { - AUTO_TRACE(); + switch (direction) { case (DIRECTION::X): return CELL_XLOW; @@ -670,7 +670,7 @@ public: /// Returns the number of grid points in the /// particular direction int getNpoints(DIRECTION direction) const { - AUTO_TRACE(); + switch (direction) { case (DIRECTION::X): return LocalNx; @@ -688,7 +688,7 @@ public: /// Returns the number of guard points in the /// particular direction int getNguard(DIRECTION direction) const { - AUTO_TRACE(); + switch (direction) { case (DIRECTION::X): return xstart; diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 6d19469e25..765a3120cf 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -40,18 +40,6 @@ class MsgStack; #include #include -/// The __PRETTY_FUNCTION__ variable is defined by GCC (and some other families) but is -/// not a part of the standard. The __func__ variable *is* a part of the c++11 standard so -/// we'd like to fall back to this if possible. However as these are variables/constants -/// and not macros we can't just check if __PRETTY_FUNCITON__ is defined or not. Instead -/// we need to say if we support this or not by defining BOUT_HAS_PRETTY_FUNCTION (to be -/// implemented in configure) -#if BOUT_HAS_PRETTY_FUNCTION -#define __thefunc__ __PRETTY_FUNCTION__ -#else -#define __thefunc__ __func__ -#endif - /*! * Message stack * @@ -193,23 +181,4 @@ private: #define TRACE(...) #endif -/*! - * The AUTO_TRACE macro provides a convenient way to put messages onto the msg_stack - * It pushes a message onto the stack, and pops it when the scope ends - * The message is automatically derived from the function signature - * as identified by the compiler. This will be PRETTY_FUNCTION if available - * else it will be the mangled form. - * - * This is implemented as a use of the TRACE macro with specific arguments. - * - * Example - * ------- - * - * { - * AUTO_TRACE(); - * - * } // Scope ends, message popped - */ -#define AUTO_TRACE() TRACE(__thefunc__) // NOLINT - #endif // BOUT_MSG_STACK_H diff --git a/include/bout/petsc_interface.hxx b/include/bout/petsc_interface.hxx index 3ae02f41a6..5edd42a441 100644 --- a/include/bout/petsc_interface.hxx +++ b/include/bout/petsc_interface.hxx @@ -363,7 +363,7 @@ public: } } Element& operator=(const Element& other) { - AUTO_TRACE(); + if (this == &other) { return *this; } @@ -372,14 +372,14 @@ public: return *this; } Element& operator=(BoutReal val) { - AUTO_TRACE(); + ASSERT3(finite(val)); value = val; setValues(val, INSERT_VALUES); return *this; } Element& operator+=(BoutReal val) { - AUTO_TRACE(); + ASSERT3(finite(val)); auto columnPosition = std::find(positions.begin(), positions.end(), petscCol); if (columnPosition != positions.end()) { diff --git a/include/bout/scorepwrapper.hxx b/include/bout/scorepwrapper.hxx index 81761530f0..40c2232040 100644 --- a/include/bout/scorepwrapper.hxx +++ b/include/bout/scorepwrapper.hxx @@ -3,7 +3,6 @@ #include "bout/build_defines.hxx" -#include "bout/msg_stack.hxx" #include #if BOUT_HAS_SCOREP @@ -14,6 +13,18 @@ #define SCOREPLVL 0 #endif +/// The __PRETTY_FUNCTION__ variable is defined by GCC (and some other families) but is +/// not a part of the standard. The __func__ variable *is* a part of the c++11 standard so +/// we'd like to fall back to this if possible. However as these are variables/constants +/// and not macros we can't just check if __PRETTY_FUNCITON__ is defined or not. Instead +/// we need to say if we support this or not by defining BOUT_HAS_PRETTY_FUNCTION (to be +/// implemented in configure) +#if BOUT_HAS_PRETTY_FUNCTION +#define __thefunc__ __PRETTY_FUNCTION__ +#else +#define __thefunc__ __func__ +#endif + /// Instrument a function with scorep /// /// The scorep call is identical for all levels, so just define it here. diff --git a/include/bout/sys/timer.hxx b/include/bout/sys/timer.hxx index f3beba27b1..90fd632f62 100644 --- a/include/bout/sys/timer.hxx +++ b/include/bout/sys/timer.hxx @@ -133,5 +133,4 @@ public: static void printTimeReport(); }; -#define AUTO_TIME() Timer CONCATENATE(time_, __LINE__)(__thefunc__) #endif // BOUT_TIMER_H diff --git a/manual/doxygen/Doxyfile b/manual/doxygen/Doxyfile index e2fef90a8c..82cfa5a8a4 100644 --- a/manual/doxygen/Doxyfile +++ b/manual/doxygen/Doxyfile @@ -2059,7 +2059,6 @@ PREDEFINED = BACKTRACE \ BOUT_HAS_LAPACK \ BOUT_HAS_NETCDF \ BOUT_HAS_PETSC \ - BOUT_HAS_PRETTY_FUNCTION \ BOUT_HAS_PVODE \ BOUT_HAS_SCOREP \ BOUT_HAS_SLEPC \ diff --git a/manual/doxygen/Doxyfile_readthedocs b/manual/doxygen/Doxyfile_readthedocs index 94113490d9..0ddca11de0 100644 --- a/manual/doxygen/Doxyfile_readthedocs +++ b/manual/doxygen/Doxyfile_readthedocs @@ -1019,7 +1019,6 @@ PREDEFINED = BACKTRACE \ BOUT_HAS_LAPACK \ BOUT_HAS_NETCDF \ BOUT_HAS_PETSC \ - BOUT_HAS_PRETTY_FUNCTION \ BOUT_HAS_PVODE \ BOUT_HAS_SCOREP \ BOUT_HAS_SLEPC \ diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index 805cb5409a..fc8d80d9fc 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -37,11 +37,10 @@ add a ``-v -v`` flag to see ``output_debug`` messages. Message Stack ============= -The second utility BOUT++ has to help debugging is the message stack -using the `TRACE` (and related `AUTO_TRACE`) macro. These are very -useful for when a bug only occurs after a long time of running, and/or -only occasionally. The ``TRACE`` macro can simply be dropped in -anywhere in the code:: +The second utility BOUT++ has to help debugging is the message stack using the +`TRACE` macro. This is very useful for when a bug only occurs after a long time +of running, and/or only occasionally. The ``TRACE`` macro can simply be dropped +in anywhere in the code:: { TRACE("Some message here"); // message pushed @@ -95,11 +94,6 @@ the ``fmt`` syntax also used by the loggers:: TRACE("Value of i={}, some arbitrary {}", i, "string"); -There is also an ``AUTO_TRACE`` macro that automatically captures the -name of the function it's used in. This is used throughout the main -library, especially in functions where numerical issues are likely to -arise. - Backtrace ========= diff --git a/src/bout++.cxx b/src/bout++.cxx index 48ab96298b..9c859aaaa7 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -566,8 +566,6 @@ void printCompileTimeOptions() { output_info.write(_("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), netcdf_flavour); output_info.write(_("\tADIOS2 support {}\n"), is_enabled(has_adios2)); output_info.write(_("\tPETSc support {}\n"), is_enabled(has_petsc)); - output_info.write(_("\tPretty function name support {}\n"), - is_enabled(has_pretty_function)); output_info.write(_("\tPVODE support {}\n"), is_enabled(has_pvode)); output_info.write(_("\tScore-P support {}\n"), is_enabled(has_scorep)); output_info.write(_("\tSLEPc support {}\n"), is_enabled(has_slepc)); @@ -696,7 +694,6 @@ void addBuildFlagsToOptions(Options& options) { options["has_umpire"].force(bout::build::has_umpire); options["has_caliper"].force(bout::build::has_caliper); options["has_raja"].force(bout::build::has_raja); - options["has_pretty_function"].force(bout::build::has_pretty_function); options["has_pvode"].force(bout::build::has_pvode); options["has_scorep"].force(bout::build::has_scorep); options["has_slepc"].force(bout::build::has_slepc); diff --git a/src/field/field_data.cxx b/src/field/field_data.cxx index b95c0462d9..b925cb1426 100644 --- a/src/field/field_data.cxx +++ b/src/field/field_data.cxx @@ -15,7 +15,6 @@ namespace bout { /// Throws if checks are enabled and trying to use a staggered /// location on a non-staggered mesh CELL_LOC normaliseLocation(CELL_LOC location, Mesh* mesh) { - AUTO_TRACE(); // CELL_DEFAULT always means CELL_CENTRE if (location == CELL_DEFAULT) { @@ -216,7 +215,6 @@ Mesh* FieldData::getMesh() const { } FieldData& FieldData::setLocation(CELL_LOC new_location) { - AUTO_TRACE(); location = bout::normaliseLocation(new_location, getMesh()); @@ -228,10 +226,7 @@ FieldData& FieldData::setLocation(CELL_LOC new_location) { return *this; } -CELL_LOC FieldData::getLocation() const { - AUTO_TRACE(); - return location; -} +CELL_LOC FieldData::getLocation() const { return location; } Coordinates* FieldData::getCoordinates() const { auto fieldCoordinates_shared = fieldCoordinates.lock(); diff --git a/src/field/field_factory.cxx b/src/field/field_factory.cxx index b93a39eeda..188a64cf0c 100644 --- a/src/field/field_factory.cxx +++ b/src/field/field_factory.cxx @@ -188,7 +188,6 @@ Field2D FieldFactory::create2D(const std::string& value, const Options* opt, Field2D FieldFactory::create2D(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC loc, BoutReal t) const { - AUTO_TRACE(); if (localmesh == nullptr) { if (fieldmesh == nullptr) { @@ -220,7 +219,6 @@ Field3D FieldFactory::create3D(const std::string& value, const Options* opt, Field3D FieldFactory::create3D(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC loc, BoutReal t) const { - AUTO_TRACE(); if (localmesh == nullptr) { if (fieldmesh == nullptr) { @@ -271,7 +269,6 @@ FieldPerp FieldFactory::createPerp(const std::string& value, const Options* opt, FieldPerp FieldFactory::createPerp(FieldGeneratorPtr gen, Mesh* localmesh, CELL_LOC loc, BoutReal t) const { - AUTO_TRACE(); if (localmesh == nullptr) { if (fieldmesh == nullptr) { diff --git a/src/field/initialprofiles.cxx b/src/field/initialprofiles.cxx index ded5aa9882..5e96d6005a 100644 --- a/src/field/initialprofiles.cxx +++ b/src/field/initialprofiles.cxx @@ -45,7 +45,6 @@ #include void initial_profile(const std::string& name, Field3D& var) { - AUTO_TRACE(); Mesh* localmesh = var.getMesh(); @@ -65,7 +64,6 @@ void initial_profile(const std::string& name, Field3D& var) { } void initial_profile(const std::string& name, Field2D& var) { - AUTO_TRACE(); Mesh* localmesh = var.getMesh(); @@ -85,7 +83,6 @@ void initial_profile(const std::string& name, Field2D& var) { } void initial_profile(const std::string& name, Vector2D& var) { - AUTO_TRACE(); if (var.covariant) { initial_profile(name + "_x", var.x); @@ -99,7 +96,6 @@ void initial_profile(const std::string& name, Vector2D& var) { } void initial_profile(const std::string& name, Vector3D& var) { - AUTO_TRACE(); if (var.covariant) { initial_profile(name + "_x", var.x); diff --git a/src/field/vecops.cxx b/src/field/vecops.cxx index 5f34e2af02..4d62d1a1d7 100644 --- a/src/field/vecops.cxx +++ b/src/field/vecops.cxx @@ -119,7 +119,7 @@ Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, const std::string& method) } Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc, const std::string& method) { - AUTO_TRACE(); + SCOREP0(); ASSERT1(outloc == CELL_DEFAULT || outloc == f.getLocation()); @@ -370,7 +370,7 @@ Field3D V_dot_Grad(const Vector3D& v, const Field3D& f) { // operation (addition) between the two input types. template R V_dot_Grad(const T& v, const F& a) { - AUTO_TRACE(); + SCOREP0(); ASSERT1(v.getLocation() == a.getLocation()); ASSERT1(v.getLocation() != CELL_VSHIFT); diff --git a/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx b/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx index efca1d70be..3be5e43e63 100644 --- a/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx +++ b/src/invert/laplace/impls/petsc3damg/petsc3damg.cxx @@ -174,7 +174,6 @@ void setBC(PetscVector& rhs, const Field3D& b_in, } Field3D LaplacePetsc3dAmg::solve(const Field3D& b_in, const Field3D& x0) { - AUTO_TRACE(); // Timing reported in the log files. Includes any matrix construction. // The timing for just the solve phase can be retrieved from the "petscsolve" diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 5078882136..fba64eaa59 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -143,7 +143,6 @@ bool GridFile::getField(Mesh* m, T& var, const std::string& name, BoutReal def, "templated GridFile::getField only works for Field2D, Field3D or FieldPerp"); Timer timer("io"); - AUTO_TRACE(); if (not data.isSet(name)) { // Variable not found diff --git a/src/mesh/index_derivs.cxx b/src/mesh/index_derivs.cxx index add67a77c8..595081e3ae 100644 --- a/src/mesh/index_derivs.cxx +++ b/src/mesh/index_derivs.cxx @@ -423,7 +423,7 @@ class FFTDerivativeType { public: template void standard(const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::Standard) ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); ASSERT2(direction == DIRECTION::Z); // Only in Z for now @@ -479,7 +479,7 @@ class FFTDerivativeType { template void upwindOrFlux(const T& UNUSED(vel), const T& UNUSED(var), T& UNUSED(result), const std::string& UNUSED(region)) const { - AUTO_TRACE(); + throw BoutException("The FFT METHOD isn't available in upwind/Flux"); } static constexpr metaData meta{"FFT", 0, DERIV::Standard}; @@ -489,7 +489,7 @@ class FFT2ndDerivativeType { public: template void standard(const T& var, T& result, const std::string& region) const { - AUTO_TRACE(); + ASSERT2(meta.derivType == DERIV::StandardSecond); ASSERT2(var.getMesh()->getNguard(direction) >= nGuards); ASSERT2(direction == DIRECTION::Z); // Only in Z for now @@ -536,7 +536,7 @@ class FFT2ndDerivativeType { template void upwindOrFlux(const T& UNUSED(vel), const T& UNUSED(var), T& UNUSED(result), const std::string& UNUSED(region)) const { - AUTO_TRACE(); + throw BoutException("The FFT METHOD isn't available in upwind/Flux"); } static constexpr metaData meta{"FFT", 0, DERIV::StandardSecond}; @@ -552,14 +552,14 @@ class SplitFluxDerivativeType { public: template void standard(const T&, T&, const std::string) const { - AUTO_TRACE(); + throw BoutException("The SPLIT method isn't available for standard"); } template void upwindOrFlux(const T& vel, const T& var, T& result, const std::string region) const { - AUTO_TRACE(); + // Split into an upwind and a central differencing part // d/dx(v*f) = v*d/dx(f) + f*d/dx(v) result = bout::derivatives::index::flowDerivative( diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index 7f3637e79c..4543c4c3fb 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -211,7 +211,6 @@ void ShiftedMetricInterp::checkInputGrid() { * Calculate the Y up and down fields */ void ShiftedMetricInterp::calcParallelSlices(Field3D& f) { - AUTO_TRACE(); // Ensure that yup and ydown are different fields f.splitParallelSlices(); diff --git a/src/physics/smoothing.cxx b/src/physics/smoothing.cxx index 0a1391907f..d8f7fb8b6b 100644 --- a/src/physics/smoothing.cxx +++ b/src/physics/smoothing.cxx @@ -352,7 +352,7 @@ BoutReal Average_XY(const Field2D& var) { BoutReal Vol_Integral(const Field2D& var) { #if BOUT_USE_METRIC_3D - AUTO_TRACE(); + throw BoutException("Vol_Intregral currently incompatible with 3D metrics"); #else Mesh* mesh = var.getMesh(); diff --git a/src/solver/impls/adams_bashforth/adams_bashforth.cxx b/src/solver/impls/adams_bashforth/adams_bashforth.cxx index 79161fcdbf..bb81e3d53f 100644 --- a/src/solver/impls/adams_bashforth/adams_bashforth.cxx +++ b/src/solver/impls/adams_bashforth/adams_bashforth.cxx @@ -14,7 +14,6 @@ namespace { BoutReal lagrange_at_position_denominator(const std::deque& grid, const int position, const int order) { - AUTO_TRACE(); const auto xj = grid[position]; @@ -28,7 +27,7 @@ BoutReal lagrange_at_position_denominator(const std::deque& grid, BoutReal lagrange_at_position_numerator(const BoutReal varX, const std::deque& grid, const int position, const int order) { - AUTO_TRACE(); + BoutReal result = 1.0; for (int i = 0; i < order; i++) { result *= (i != position) ? (varX - grid[i]) : 1.0; @@ -55,7 +54,7 @@ BoutReal lagrange_interpolate(BoutReal start, BoutReal end, BoutReal integrate_lagrange_curve_nc9(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 9; constexpr BoutReal fac = 4.0 / 14175.0; constexpr std::array facs{989.0 * fac, 5888.0 * fac, -928.0 * fac, @@ -68,7 +67,7 @@ BoutReal integrate_lagrange_curve_nc9(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc8(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 8; constexpr BoutReal fac = 7.0 / 17280.0; constexpr std::array facs{751.0 * fac, 3577.0 * fac, 1323.0 * fac, @@ -81,7 +80,7 @@ BoutReal integrate_lagrange_curve_nc8(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc7(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 7; constexpr BoutReal fac = 1.0 / 140.0; constexpr std::array facs{41.0 * fac, 216.0 * fac, 27.0 * fac, @@ -94,7 +93,7 @@ BoutReal integrate_lagrange_curve_nc7(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc6(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 6; constexpr BoutReal fac = 5.0 / 288.0; constexpr std::array facs{19.0 * fac, 75.0 * fac, 50.0 * fac, @@ -106,7 +105,7 @@ BoutReal integrate_lagrange_curve_nc6(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc5(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 5; constexpr BoutReal fac = 2.0 / 45.0; constexpr std::array facs{7.0 * fac, 32.0 * fac, 12.0 * fac, 32.0 * fac, @@ -118,7 +117,7 @@ BoutReal integrate_lagrange_curve_nc5(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc4(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 4; constexpr BoutReal fac = 3.0 / 8.0; constexpr std::array facs{1.0 * fac, 3.0 * fac, 3.0 * fac, 1.0 * fac}; @@ -129,7 +128,7 @@ BoutReal integrate_lagrange_curve_nc4(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc3(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 3; constexpr BoutReal fac = 1.0 / 3.0; constexpr std::array facs{1.0 * fac, 4.0 * fac, 1.0 * fac}; @@ -140,7 +139,7 @@ BoutReal integrate_lagrange_curve_nc3(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve_nc2(const BoutReal start, const BoutReal end, const std::deque& points, const int position) { - AUTO_TRACE(); + constexpr std::size_t size = 2; constexpr BoutReal fac = 1.0 / 2.0; constexpr std::array facs{1.0 * fac, 1.0 * fac}; @@ -151,7 +150,6 @@ BoutReal integrate_lagrange_curve_nc2(const BoutReal start, const BoutReal end, BoutReal integrate_lagrange_curve(const BoutReal start, const BoutReal end, const std::deque& points, const int position, const int order) { - AUTO_TRACE(); switch (order) { case 1: @@ -179,7 +177,7 @@ BoutReal integrate_lagrange_curve(const BoutReal start, const BoutReal end, std::vector get_adams_bashforth_coefficients(const BoutReal nextPoint, const std::deque& points, const int order) { - AUTO_TRACE(); + ASSERT2(static_cast(order) <= points.size()); std::vector result; @@ -235,7 +233,7 @@ BoutReal get_timestep_limit(const BoutReal error, const BoutReal tolerance, /// over all processors. BoutReal get_error(const Array& stateApprox, const Array& stateAccurate) { - AUTO_TRACE(); + BoutReal local_result = 0.0; BoutReal err = 0.0; @@ -287,12 +285,12 @@ AdamsBashforthSolver::AdamsBashforthSolver(Options* options) .withDefault(getOutputTimestep())), timestep( (*options)["timestep"].doc("Starting timestep").withDefault(max_timestep)) { - AUTO_TRACE(); + canReset = true; } void AdamsBashforthSolver::setMaxTimestep(BoutReal dt) { - AUTO_TRACE(); + if (dt > timestep) { return; // Already less than this } @@ -345,7 +343,6 @@ int AdamsBashforthSolver::init() { } void AdamsBashforthSolver::resetInternalFields() { - AUTO_TRACE(); // History and times history.clear(); @@ -360,7 +357,6 @@ void AdamsBashforthSolver::resetInternalFields() { } int AdamsBashforthSolver::run() { - AUTO_TRACE(); // Just for developer diagnostics int nwasted = 0; @@ -563,7 +559,6 @@ int AdamsBashforthSolver::run() { BoutReal AdamsBashforthSolver::take_step(const BoutReal timeIn, const BoutReal dt, const int order, Array& current, Array& result) { - AUTO_TRACE(); Array full_update = AB_integrate(nlocal, timeIn + dt, times, history, order); diff --git a/src/solver/impls/split-rk/split-rk.cxx b/src/solver/impls/split-rk/split-rk.cxx index cd6bd1718c..fd9a99a6c5 100644 --- a/src/solver/impls/split-rk/split-rk.cxx +++ b/src/solver/impls/split-rk/split-rk.cxx @@ -35,7 +35,6 @@ SplitRK::SplitRK(Options* opts) } int SplitRK::init() { - AUTO_TRACE(); Solver::init(); output.write(_("\n\tSplit Runge-Kutta-Legendre and SSP-RK3 solver\n")); @@ -79,7 +78,6 @@ int SplitRK::init() { } int SplitRK::run() { - AUTO_TRACE(); for (int step = 0; step < getNumberOutputSteps(); step++) { // Take an output step diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index a3f6a874f6..ea7866051b 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -636,7 +636,7 @@ std::string Solver::createRunID() const { } std::string Solver::getRunID() const { - AUTO_TRACE(); + if (run_id == default_run_id) { throw BoutException("run_id not set!"); } @@ -644,7 +644,7 @@ std::string Solver::getRunID() const { } std::string Solver::getRunRestartFrom() const { - AUTO_TRACE(); + // Check against run_id, because this might not be a restarted run if (run_id == default_run_id) { throw BoutException("run_restart_from not set!"); diff --git a/src/sys/bout_types.cxx b/src/sys/bout_types.cxx index 35d37f11be..f5e971af78 100644 --- a/src/sys/bout_types.cxx +++ b/src/sys/bout_types.cxx @@ -6,7 +6,7 @@ namespace { template const std::string& safeAt(const std::map& mymap, T t) { - AUTO_TRACE(); + auto found = mymap.find(t); if (found == mymap.end()) { throw BoutException("Did not find enum {:d}", static_cast(t)); @@ -16,7 +16,7 @@ const std::string& safeAt(const std::map& mymap, T t) { template const T& safeAt(const std::map& mymap, const std::string& s) { - AUTO_TRACE(); + auto found = mymap.find(s); if (found == mymap.end()) { throw BoutException("Did not find enum {:s}", s); @@ -26,7 +26,7 @@ const T& safeAt(const std::map& mymap, const std::string& s) { } // namespace std::string toString(CELL_LOC location) { - AUTO_TRACE(); + const static std::map CELL_LOCtoString = { ENUMSTR(CELL_DEFAULT), ENUMSTR(CELL_CENTRE), ENUMSTR(CELL_XLOW), ENUMSTR(CELL_YLOW), ENUMSTR(CELL_ZLOW), ENUMSTR(CELL_VSHIFT)}; @@ -35,7 +35,7 @@ std::string toString(CELL_LOC location) { } CELL_LOC CELL_LOCFromString(const std::string& location_string) { - AUTO_TRACE(); + const static std::map stringtoCELL_LOC = { STRENUM(CELL_DEFAULT), STRENUM(CELL_CENTRE), STRENUM(CELL_XLOW), STRENUM(CELL_YLOW), STRENUM(CELL_ZLOW), STRENUM(CELL_VSHIFT)}; @@ -44,7 +44,7 @@ CELL_LOC CELL_LOCFromString(const std::string& location_string) { } std::string toString(DIFF_METHOD location) { - AUTO_TRACE(); + const static std::map DIFF_METHODtoString = { {DIFF_DEFAULT, "DEFAULT"}, {DIFF_U1, "U1"}, {DIFF_U2, "U2"}, {DIFF_U3, "U3"}, {DIFF_C2, "C2"}, {DIFF_C4, "C4"}, {DIFF_S2, "S2"}, {DIFF_W2, "W2"}, @@ -54,7 +54,7 @@ std::string toString(DIFF_METHOD location) { } std::string toString(REGION region) { - AUTO_TRACE(); + const static std::map REGIONtoString = { ENUMSTR(RGN_ALL), ENUMSTR(RGN_NOBNDRY), ENUMSTR(RGN_NOX), ENUMSTR(RGN_NOY), ENUMSTR(RGN_NOZ)}; @@ -62,7 +62,7 @@ std::string toString(REGION region) { } std::string toString(DIRECTION direction) { - AUTO_TRACE(); + const static std::map DIRECTIONtoString = { {DIRECTION::X, "X"}, {DIRECTION::Y, "Y"}, @@ -116,7 +116,7 @@ bool areDirectionsCompatible(const DirectionTypes& d1, const DirectionTypes& d2) } std::string toString(STAGGER stagger) { - AUTO_TRACE(); + const static std::map STAGGERtoString = { {STAGGER::None, "No staggering"}, {STAGGER::C2L, "Centre to Low"}, @@ -126,7 +126,7 @@ std::string toString(STAGGER stagger) { } std::string toString(DERIV deriv) { - AUTO_TRACE(); + const static std::map DERIVtoString = { {DERIV::Standard, "Standard"}, {DERIV::StandardSecond, "Standard -- second order"}, @@ -138,7 +138,7 @@ std::string toString(DERIV deriv) { } std::string toString(YDirectionType d) { - AUTO_TRACE(); + const static std::map YDirectionTypeToString = { {YDirectionType::Standard, "Standard"}, {YDirectionType::Aligned, "Aligned"}}; @@ -146,7 +146,7 @@ std::string toString(YDirectionType d) { } YDirectionType YDirectionTypeFromString(const std::string& y_direction_string) { - AUTO_TRACE(); + const static std::map stringToYDirectionType = { {"Standard", YDirectionType::Standard}, {"Aligned", YDirectionType::Aligned}}; @@ -154,7 +154,7 @@ YDirectionType YDirectionTypeFromString(const std::string& y_direction_string) { } std::string toString(ZDirectionType d) { - AUTO_TRACE(); + const static std::map ZDirectionTypeToString = { {ZDirectionType::Standard, "Standard"}, {ZDirectionType::Average, "Average"}}; @@ -162,7 +162,7 @@ std::string toString(ZDirectionType d) { } ZDirectionType ZDirectionTypeFromString(const std::string& z_direction_string) { - AUTO_TRACE(); + const static std::map stringToZDirectionType = { {"Standard", ZDirectionType::Standard}, {"Average", ZDirectionType::Average}}; diff --git a/src/sys/msg_stack.cxx b/src/sys/msg_stack.cxx index 502836324c..81eb6c4b0f 100644 --- a/src/sys/msg_stack.cxx +++ b/src/sys/msg_stack.cxx @@ -27,7 +27,7 @@ #include "bout/openmpwrap.hxx" #include #include -#include + #include #if BOUT_USE_OPENMP diff --git a/tests/integrated/test-backtrace/boutexcept.cxx b/tests/integrated/test-backtrace/boutexcept.cxx index bd965a9c2b..ed33a4c018 100644 --- a/tests/integrated/test-backtrace/boutexcept.cxx +++ b/tests/integrated/test-backtrace/boutexcept.cxx @@ -1,18 +1,9 @@ #include "bout/boutexception.hxx" #include "bout/msg_stack.hxx" -void troublemaker() { - AUTO_TRACE(); - throw BoutException("test"); -} -void f() { - AUTO_TRACE(); - troublemaker(); -} -void e() { - AUTO_TRACE(); - f(); -} +void troublemaker() { throw BoutException("test"); } +void f() { troublemaker(); } +void e() { f(); } int main() { e(); From 505d5adde26e2f70a129582b676bd99fcb85fafd Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 15:11:05 +0000 Subject: [PATCH 127/461] Remove uses of `TRACE` which are just the function name --- examples/6field-simple/elm_6f.cxx | 4 -- .../invertable_operator.cxx | 5 +-- include/bout/cyclic_reduction.hxx | 4 +- include/bout/invertable_operator.hxx | 25 ++++------- .../sphinx/user_docs/invertable_operator.rst | 2 - src/field/field2d.cxx | 36 ++++------------ src/field/field3d.cxx | 43 ++----------------- src/field/fieldperp.cxx | 3 -- src/field/vecops.cxx | 26 +++++------ src/field/vector2d.cxx | 2 +- src/field/vector3d.cxx | 2 +- .../laplace/impls/cyclic/cyclic_laplace.cxx | 1 - .../iterative_parallel_tri.cxx | 1 - .../impls/multigrid/multigrid_laplace.cxx | 5 --- src/invert/laplace/impls/pcr/pcr.cxx | 1 - .../laplace/impls/pcr_thomas/pcr_thomas.cxx | 1 - .../laplace/impls/petsc/petsc_laplace.cxx | 1 - src/invert/laplace/invert_laplace.cxx | 2 - .../impls/cyclic/laplacexz-cyclic.cxx | 2 +- .../laplacexz/impls/petsc/laplacexz-petsc.cxx | 8 ---- src/invert/parderiv/impls/cyclic/cyclic.cxx | 2 +- .../pardiv/impls/cyclic/pardiv_cyclic.cxx | 2 +- src/mesh/boundary_standard.cxx | 5 --- src/mesh/coordinates.cxx | 28 ++++++------ src/mesh/data/gridfromfile.cxx | 9 ++-- src/mesh/difops.cxx | 11 ----- src/mesh/fv_ops.cxx | 1 - src/mesh/impls/bout/boutmesh.cxx | 2 - src/mesh/index_derivs.cxx | 4 -- src/mesh/interpolation_xz.cxx | 4 -- src/mesh/invert3x3.hxx | 1 - src/mesh/mesh.cxx | 3 -- src/mesh/parallel/fci.cxx | 3 -- src/physics/physicsmodel.cxx | 1 - src/physics/smoothing.cxx | 14 +----- src/physics/sourcex.cxx | 4 -- .../impls/adams_bashforth/adams_bashforth.cxx | 2 - src/solver/impls/arkode/arkode.cxx | 3 -- src/solver/impls/cvode/cvode.cxx | 5 +-- src/solver/impls/euler/euler.cxx | 2 - src/solver/impls/ida/ida.cxx | 3 -- src/solver/impls/imex-bdf2/imex-bdf2.cxx | 3 -- src/solver/impls/petsc/petsc.cxx | 3 -- src/solver/impls/power/power.cxx | 2 - src/solver/impls/pvode/pvode.cxx | 2 - src/solver/impls/rk3-ssp/rk3-ssp.cxx | 2 - src/solver/impls/rk4/rk4.cxx | 4 -- src/solver/impls/rkgeneric/rkgeneric.cxx | 4 -- src/solver/impls/slepc/slepc.cxx | 2 - src/solver/impls/snes/snes.cxx | 5 --- src/solver/solver.cxx | 3 -- src/sys/options.cxx | 4 -- src/sys/options/options_ini.cxx | 6 +-- src/sys/optionsreader.cxx | 3 -- .../invertable_operator.cxx | 3 +- tests/integrated/test-vec/testVec.cxx | 2 - 56 files changed, 60 insertions(+), 271 deletions(-) diff --git a/examples/6field-simple/elm_6f.cxx b/examples/6field-simple/elm_6f.cxx index 40e4f7b3b3..24256333df 100644 --- a/examples/6field-simple/elm_6f.cxx +++ b/examples/6field-simple/elm_6f.cxx @@ -273,8 +273,6 @@ class Elm_6f : public PhysicsModel { * This function implements d2/dy2 where y is the poloidal coordinate theta */ - TRACE("Grad2_par2new( Field3D )"); - Field3D result = D2DY2(f); #if BOUT_USE_TRACK @@ -342,8 +340,6 @@ class Elm_6f : public PhysicsModel { // Parallel gradient along perturbed field-line Field3D Grad_parP(const Field3D& f, CELL_LOC loc = CELL_DEFAULT) { - TRACE("Grad_parP"); - Field3D result; if (parallel_lagrange || parallel_project) { diff --git a/examples/invertable_operator/invertable_operator.cxx b/examples/invertable_operator/invertable_operator.cxx index 57f9e24b08..99db7f7a1e 100644 --- a/examples/invertable_operator/invertable_operator.cxx +++ b/examples/invertable_operator/invertable_operator.cxx @@ -1,5 +1,4 @@ #include -#include #include #include @@ -26,7 +25,7 @@ class HW : public PhysicsModel { // Drop C term for now Field3D operator()(const Field3D& input) { - TRACE("myLaplacian::operator()"); + Timer timer("invertable_operator_operate"); Field3D result = A * input + D * Delp2(input); @@ -43,7 +42,7 @@ class HW : public PhysicsModel { // Drop C term for now Field3D operator()(const Field3D& input) { - TRACE("myLaplacian::operator()"); + Timer timer("invertable_operator_operate"); Field3D result = A * input + B * Laplace_perp(input); if (withDiv) { diff --git a/include/bout/cyclic_reduction.hxx b/include/bout/cyclic_reduction.hxx index d4c0920910..cf54b63059 100644 --- a/include/bout/cyclic_reduction.hxx +++ b/include/bout/cyclic_reduction.hxx @@ -47,7 +47,6 @@ //#define DIAGNOSE 1 #include "mpi.h" -#include "bout/msg_stack.hxx" #include "bout/utils.hxx" #include @@ -118,7 +117,6 @@ public: /// @param[in] b Diagonal values. Should have size [nsys][N] /// @param[in] c Right diagonal. Should have size [nsys][N] void setCoefs(const Matrix& a, const Matrix& b, const Matrix& c) { - TRACE("CyclicReduce::setCoefs"); int nsys = std::get<0>(a.shape()); @@ -169,7 +167,7 @@ public: /// @param[in] rhs Matrix storing Values of the rhs for each system /// @param[out] x Matrix storing the result for each system void solve(const Matrix& rhs, Matrix& x) { - TRACE("CyclicReduce::solve"); + ASSERT2(static_cast(std::get<0>(rhs.shape())) == Nsys); ASSERT2(static_cast(std::get<0>(x.shape())) == Nsys); ASSERT2(static_cast(std::get<1>(rhs.shape())) == N); diff --git a/include/bout/invertable_operator.hxx b/include/bout/invertable_operator.hxx index ffc0910dce..6dcd92897a 100644 --- a/include/bout/invertable_operator.hxx +++ b/include/bout/invertable_operator.hxx @@ -42,7 +42,6 @@ class InvertableOperator; #include #include #include -#include #include #include #include @@ -68,7 +67,7 @@ T identity(const T& in) { /// Pack a PetscVec from a Field template PetscErrorCode fieldToPetscVec(const T& in, Vec out) { - TRACE("fieldToPetscVec"); + Timer timer("invertable_operator_packing"); PetscScalar* vecData; @@ -93,7 +92,7 @@ PetscErrorCode fieldToPetscVec(const T& in, Vec out) { /// Pack a Field from a PetscVec template PetscErrorCode petscVecToField(Vec in, T& out) { - TRACE("petscVecToField"); + Timer timer("invertable_operator_packing"); const PetscScalar* vecData; @@ -142,7 +141,6 @@ public: /// Destructor just has to cleanup the PETSc owned objects. ~InvertableOperator() { - TRACE("InvertableOperator::destructor"); KSPDestroy(&ksp); MatDestroy(&matOperator); @@ -157,7 +155,7 @@ public: /// do this they can set alsoSetPreconditioner to false. void setOperatorFunction(const function_signature& func, bool alsoSetPreconditioner = true) { - TRACE("InvertableOperator::setOperatorFunction"); + operatorFunction = func; if (alsoSetPreconditioner) { preconditionerFunction = func; @@ -166,15 +164,12 @@ public: /// Allow the user to override the existing preconditioner function void setPreconditionerFunction(const function_signature& func) { - TRACE("InvertableOperator::setPreconditionerFunction"); + preconditionerFunction = func; } /// Provide a way to apply the operator to a Field - T operator()(const T& input) { - TRACE("InvertableOperator::operator()"); - return operatorFunction(input); - } + T operator()(const T& input) { return operatorFunction(input); } /// Provide a synonym for applying the operator to a Field T apply(const T& input) { return operator()(input); } @@ -184,7 +179,6 @@ public: /// represents. Not actually required by any of the setup so this should /// probably be moved to a separate place (maybe the constructor). PetscErrorCode setup() { - TRACE("InvertableOperator::setup"); Timer timer("invertable_operator_setup"); if (doneSetup) { @@ -394,7 +388,7 @@ public: /// of the operator we represent. Should probably provide an overload or similar as a /// way of setting the initial guess. T invert(const T& rhsField) { - TRACE("InvertableOperator::invert"); + Timer timer("invertable_operator_invert"); if (!doneSetup) { @@ -441,7 +435,6 @@ public: /// applying the registered function on the calculated inverse gives /// back the initial values. bool verify(const T& rhsIn, BoutReal tol = 1.0e-5) { - TRACE("InvertableOperator::verify"); T result = invert(rhsIn); localmesh->communicate(result); @@ -460,7 +453,7 @@ public: /// that as the Timer "labels" are not unique to an instance the time /// reported is summed across all different instances. static void reportTime() { - TRACE("InvertableOperator::reportTime"); + BoutReal time_setup = Timer::resetTime("invertable_operator_setup"); BoutReal time_invert = Timer::resetTime("invertable_operator_invert"); BoutReal time_packing = Timer::resetTime("invertable_operator_packing"); @@ -500,7 +493,7 @@ private: /// Copies data from v1 into a field of type T, calls the function on this and then /// copies the result into the v2 argument. static PetscErrorCode functionWrapper(Mat m, Vec v1, Vec v2) { - TRACE("InvertableOperator::functionWrapper"); + InvertableOperator* ctx; auto ierr = MatShellGetContext(m, &ctx); T tmpField(ctx->localmesh); @@ -531,7 +524,7 @@ private: /// Copies data from v1 into a field of type T, calls the function on this and then /// copies the result into the v2 argument. static PetscErrorCode preconditionerWrapper(Mat m, Vec v1, Vec v2) { - TRACE("InvertableOperator::functionWrapper"); + InvertableOperator* ctx; auto ierr = MatShellGetContext(m, &ctx); T tmpField(ctx->localmesh); diff --git a/manual/sphinx/user_docs/invertable_operator.rst b/manual/sphinx/user_docs/invertable_operator.rst index 92b7efd288..8f92796c9c 100644 --- a/manual/sphinx/user_docs/invertable_operator.rst +++ b/manual/sphinx/user_docs/invertable_operator.rst @@ -48,7 +48,6 @@ the ``operator()`` call:: // Drop C term for now Field3D operator()(const Field3D &input) { - TRACE("myLaplacian::operator()"); Timer timer("invertable_operator_operate"); Field3D result = A * input + D * Delp2(input); @@ -68,7 +67,6 @@ A more complete example is :: // Drop C term for now Field3D operator()(const Field3D &input) { - TRACE("myLaplacian::operator()"); Timer timer("invertable_operator_operate"); Field3D result = A * input + D * Delp2(input); diff --git a/src/field/field2d.cxx b/src/field/field2d.cxx index c8b9ebb689..01f2d8c4a6 100644 --- a/src/field/field2d.cxx +++ b/src/field/field2d.cxx @@ -25,28 +25,21 @@ * **************************************************************************/ -#include "bout/build_config.hxx" - -#include -#include - -#include // for mesh - -#include - -#include +#include "bout/build_defines.hxx" +#include #include #include - +#include #include +#include +#include // for mesh #include -#include - #include -#include +#include +#include -#include +#include Field2D::Field2D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in) : Field(localmesh, location_in, directions_in) { @@ -62,7 +55,6 @@ Field2D::Field2D(Mesh* localmesh, CELL_LOC location_in, DirectionTypes direction } Field2D::Field2D(const Field2D& f) : Field(f), data(f.data) { - TRACE("Field2D(Field2D&)"); #if BOUT_USE_TRACK name = f.name; @@ -145,8 +137,6 @@ Field2D& Field2D::operator=(const Field2D& rhs) { return (*this); // skip this assignment } - TRACE("Field2D: Assignment from Field2D"); - Field::operator=(rhs); // Copy the data and data sizes @@ -165,8 +155,6 @@ Field2D& Field2D::operator=(Field2D&& rhs) noexcept { return (*this); // skip this assignment } - TRACE("Field2D: Move assignment from Field2D"); - // Move the data and data sizes nx = rhs.nx; ny = rhs.ny; @@ -185,7 +173,6 @@ Field2D& Field2D::operator=(const BoutReal rhs) { name = ""; #endif - TRACE("Field2D = BoutReal"); allocate(); BOUT_FOR(i, getRegion("RGN_ALL")) { (*this)[i] = rhs; } @@ -196,7 +183,6 @@ Field2D& Field2D::operator=(const BoutReal rhs) { ///////////////////// BOUNDARY CONDITIONS ////////////////// void Field2D::applyBoundary(bool init) { - TRACE("Field2D::applyBoundary()"); #if CHECK > 0 if (init) { @@ -218,7 +204,6 @@ void Field2D::applyBoundary(bool init) { } void Field2D::applyBoundary(BoutReal time) { - TRACE("Field2D::applyBoundary(time)"); #if CHECK > 0 if (not isBoundarySet()) { @@ -234,7 +219,6 @@ void Field2D::applyBoundary(BoutReal time) { } void Field2D::applyBoundary(const std::string& condition) { - TRACE("Field2D::applyBoundary(condition)"); checkData(*this); @@ -268,7 +252,7 @@ void Field2D::applyBoundary(const std::string& condition) { } void Field2D::applyBoundary(const std::string& region, const std::string& condition) { - TRACE("Field2D::applyBoundary(string, string)"); + checkData(*this); /// Get the boundary factory (singleton) @@ -310,7 +294,6 @@ void Field2D::applyBoundary(const std::string& region, const std::string& condit } void Field2D::applyTDerivBoundary() { - TRACE("Field2D::applyTDerivBoundary()"); checkData(*this); ASSERT1(deriv != nullptr); @@ -322,7 +305,6 @@ void Field2D::applyTDerivBoundary() { } void Field2D::setBoundaryTo(const Field2D& f2d) { - TRACE("Field2D::setBoundary(const Field2D&)"); checkData(f2d); diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 4c5ffa9748..9821c638f7 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -25,7 +25,7 @@ * **************************************************************************/ -#include "bout/build_config.hxx" +#include "bout/build_defines.hxx" #include #include @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -67,8 +66,6 @@ Field3D::Field3D(const Field3D& f) : Field(f), data(f.data), yup_fields(f.yup_fields), ydown_fields(f.ydown_fields), regionID(f.regionID) { - TRACE("Field3D(Field3D&)"); - if (fieldmesh) { nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; @@ -78,8 +75,6 @@ Field3D::Field3D(const Field3D& f) Field3D::Field3D(const Field2D& f) : Field(f) { - TRACE("Field3D: Copy constructor from Field2D"); - nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; nz = fieldmesh->LocalNz; @@ -89,15 +84,12 @@ Field3D::Field3D(const Field2D& f) : Field(f) { Field3D::Field3D(const BoutReal val, Mesh* localmesh) : Field3D(localmesh) { - TRACE("Field3D: Copy constructor from value"); - *this = val; } Field3D::Field3D(Array data_in, Mesh* localmesh, CELL_LOC datalocation, DirectionTypes directions_in) : Field(localmesh, datalocation, directions_in), data(std::move(data_in)) { - TRACE("Field3D: Copy constructor from Array and Mesh"); nx = fieldmesh->LocalNx; ny = fieldmesh->LocalNy; @@ -137,7 +129,6 @@ Field3D* Field3D::timeDeriv() { } void Field3D::splitParallelSlices() { - TRACE("Field3D::splitParallelSlices"); if (hasParallelSlices()) { return; @@ -152,7 +143,6 @@ void Field3D::splitParallelSlices() { } void Field3D::clearParallelSlices() { - TRACE("Field3D::clearParallelSlices"); if (!hasParallelSlices()) { return; @@ -245,8 +235,6 @@ Field3D& Field3D::operator=(const Field3D& rhs) { return (*this); // skip this assignment } - TRACE("Field3D: Assignment from Field3D"); - // Copy base slice Field::operator=(rhs); @@ -266,7 +254,6 @@ Field3D& Field3D::operator=(const Field3D& rhs) { } Field3D& Field3D::operator=(Field3D&& rhs) { - TRACE("Field3D: Assignment from Field3D"); // Move parallel slices or delete existing ones. yup_fields = std::move(rhs.yup_fields); @@ -287,7 +274,6 @@ Field3D& Field3D::operator=(Field3D&& rhs) { } Field3D& Field3D::operator=(const Field2D& rhs) { - TRACE("Field3D = Field2D"); /// Check that the data is allocated ASSERT1(rhs.isAllocated()); @@ -314,7 +300,6 @@ Field3D& Field3D::operator=(const Field2D& rhs) { } void Field3D::operator=(const FieldPerp& rhs) { - TRACE("Field3D = FieldPerp"); ASSERT1_FIELDS_COMPATIBLE(*this, rhs); /// Check that the data is allocated @@ -333,7 +318,6 @@ void Field3D::operator=(const FieldPerp& rhs) { } Field3D& Field3D::operator=(const BoutReal val) { - TRACE("Field3D = BoutReal"); // Delete existing parallel slices. We don't copy parallel slices, so any // that currently exist will be incorrect. @@ -355,7 +339,6 @@ Field3D& Field3D::calcParallelSlices() { ///////////////////// BOUNDARY CONDITIONS ////////////////// void Field3D::applyBoundary(bool init) { - TRACE("Field3D::applyBoundary()"); #if CHECK > 0 if (init) { @@ -378,7 +361,6 @@ void Field3D::applyBoundary(bool init) { } void Field3D::applyBoundary(BoutReal t) { - TRACE("Field3D::applyBoundary()"); #if CHECK > 0 if (not isBoundarySet()) { @@ -395,7 +377,6 @@ void Field3D::applyBoundary(BoutReal t) { } void Field3D::applyBoundary(const std::string& condition) { - TRACE("Field3D::applyBoundary(condition)"); checkData(*this); @@ -413,7 +394,7 @@ void Field3D::applyBoundary(const std::string& condition) { } void Field3D::applyBoundary(const std::string& region, const std::string& condition) { - TRACE("Field3D::applyBoundary(string, string)"); + checkData(*this); /// Get the boundary factory (singleton) @@ -439,7 +420,6 @@ void Field3D::applyBoundary(const std::string& region, const std::string& condit } void Field3D::applyTDerivBoundary() { - TRACE("Field3D::applyTDerivBoundary()"); checkData(*this); ASSERT1(deriv != nullptr); @@ -451,7 +431,6 @@ void Field3D::applyTDerivBoundary() { } void Field3D::setBoundaryTo(const Field3D& f3d) { - TRACE("Field3D::setBoundary(const Field3D&)"); checkData(f3d); @@ -475,8 +454,6 @@ void Field3D::setBoundaryTo(const Field3D& f3d) { void Field3D::applyParallelBoundary() { - TRACE("Field3D::applyParallelBoundary()"); - checkData(*this); if (isFci()) { ASSERT1(hasParallelSlices()); @@ -493,8 +470,6 @@ void Field3D::applyParallelBoundary() { void Field3D::applyParallelBoundary(BoutReal t) { - TRACE("Field3D::applyParallelBoundary(t)"); - checkData(*this); if (isFci()) { ASSERT1(hasParallelSlices()); @@ -511,8 +486,6 @@ void Field3D::applyParallelBoundary(BoutReal t) { void Field3D::applyParallelBoundary(const std::string& condition) { - TRACE("Field3D::applyParallelBoundary(condition)"); - checkData(*this); if (isFci()) { ASSERT1(hasParallelSlices()); @@ -535,8 +508,6 @@ void Field3D::applyParallelBoundary(const std::string& condition) { void Field3D::applyParallelBoundary(const std::string& region, const std::string& condition) { - TRACE("Field3D::applyParallelBoundary(region, condition)"); - checkData(*this); if (isFci()) { ASSERT1(hasParallelSlices()); @@ -562,8 +533,6 @@ void Field3D::applyParallelBoundary(const std::string& region, void Field3D::applyParallelBoundary(const std::string& region, const std::string& condition, Field3D* f) { - TRACE("Field3D::applyParallelBoundary(region, condition, f)"); - checkData(*this); if (isFci()) { ASSERT1(hasParallelSlices()); @@ -599,7 +568,7 @@ Field3D operator-(const Field3D& f) { return -1.0 * f; } //////////////// NON-MEMBER FUNCTIONS ////////////////// Field3D pow(const Field3D& lhs, const Field2D& rhs, const std::string& rgn) { - TRACE("pow(Field3D, Field2D)"); + // Check if the inputs are allocated checkData(lhs); checkData(rhs); @@ -615,7 +584,6 @@ Field3D pow(const Field3D& lhs, const Field2D& rhs, const std::string& rgn) { } FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, const std::string& rgn) { - TRACE("pow(Field3D, FieldPerp)"); checkData(lhs); checkData(rhs); @@ -635,7 +603,6 @@ FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, const std::string& rgn) // Friend functions Field3D filter(const Field3D& var, int N0, const std::string& rgn) { - TRACE("filter(Field3D, int)"); checkData(var); @@ -681,7 +648,6 @@ Field3D filter(const Field3D& var, int N0, const std::string& rgn) { // Fourier filter in z with zmin Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string& rgn) { - TRACE("lowPass(Field3D, {}, {})", zmax, keep_zonal); checkData(var); int ncz = var.getNz(); @@ -731,7 +697,7 @@ Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string * Use FFT to shift by an angle in the Z direction */ void shiftZ(Field3D& var, int jx, int jy, double zangle) { - TRACE("shiftZ"); + checkData(var); var.allocate(); // Ensure that var is unique Mesh* localmesh = var.getMesh(); @@ -800,7 +766,6 @@ void checkData(const Field3D& f, const std::string& region) { #endif Field2D DC(const Field3D& f, const std::string& rgn) { - TRACE("DC(Field3D)"); checkData(f); diff --git a/src/field/fieldperp.cxx b/src/field/fieldperp.cxx index ca9bdc0397..d74a986473 100644 --- a/src/field/fieldperp.cxx +++ b/src/field/fieldperp.cxx @@ -31,7 +31,6 @@ #include #include #include -#include #include FieldPerp::FieldPerp(Mesh* localmesh, CELL_LOC location_in, int yindex_in, @@ -51,7 +50,6 @@ FieldPerp::FieldPerp(Array data_in, Mesh* localmesh, CELL_LOC location int yindex_in, DirectionTypes directions) : Field(localmesh, location_in, directions), yindex(yindex_in), nx(fieldmesh->LocalNx), nz(fieldmesh->LocalNz), data(std::move(data_in)) { - TRACE("FieldPerp: Copy constructor from Array and Mesh"); ASSERT1(data.size() == nx * nz); } @@ -97,7 +95,6 @@ FieldPerp& FieldPerp::operator=(const FieldPerp& rhs) { } FieldPerp& FieldPerp::operator=(const BoutReal rhs) { - TRACE("FieldPerp = BoutReal"); allocate(); diff --git a/src/field/vecops.cxx b/src/field/vecops.cxx index 4d62d1a1d7..1677187fcd 100644 --- a/src/field/vecops.cxx +++ b/src/field/vecops.cxx @@ -29,7 +29,6 @@ #include #include -#include #include #include #include @@ -39,7 +38,7 @@ **************************************************************************/ Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Grad( Field2D )"); + SCOREP0(); CELL_LOC outloc_x, outloc_y, outloc_z; if (outloc == CELL_VSHIFT) { @@ -68,7 +67,7 @@ Vector2D Grad(const Field2D& f, CELL_LOC outloc, const std::string& method) { } Vector3D Grad(const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Grad( Field3D )"); + SCOREP0(); CELL_LOC outloc_x, outloc_y, outloc_z; if (outloc == CELL_VSHIFT) { @@ -97,7 +96,7 @@ Vector3D Grad(const Field3D& f, CELL_LOC outloc, const std::string& method) { } Vector3D Grad_perp(const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Grad_perp( Field3D )"); + SCOREP0(); ASSERT1(outloc == CELL_DEFAULT || outloc == f.getLocation()); @@ -145,7 +144,7 @@ Vector2D Grad_perp(const Field2D& f, CELL_LOC outloc, const std::string& method) Coordinates::FieldMetric Div(const Vector2D& v, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector2D )"); + SCOREP0(); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -170,7 +169,7 @@ Coordinates::FieldMetric Div(const Vector2D& v, CELL_LOC outloc, } Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector3D )"); + SCOREP0(); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -209,7 +208,7 @@ Field3D Div(const Vector3D& v, CELL_LOC outloc, const std::string& method) { Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector2D, Field2D )"); + SCOREP0(); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -236,7 +235,6 @@ Coordinates::FieldMetric Div(const Vector2D& v, const Field2D& f, CELL_LOC outlo Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Div( Vector3D, Field3D )"); if (outloc == CELL_DEFAULT) { outloc = v.getLocation(); @@ -265,8 +263,6 @@ Field3D Div(const Vector3D& v, const Field3D& f, CELL_LOC outloc, Vector2D Curl(const Vector2D& v) { - TRACE("Curl( Vector2D )"); - ASSERT1(v.getLocation() != CELL_VSHIFT); Mesh* localmesh = v.getMesh(); auto metric = v.x.getCoordinates(); @@ -292,7 +288,7 @@ Vector2D Curl(const Vector2D& v) { } Vector3D Curl(const Vector3D& v) { - TRACE("Curl( Vector3D )"); + SCOREP0(); ASSERT1(v.getLocation() != CELL_VSHIFT); @@ -323,7 +319,7 @@ Vector3D Curl(const Vector3D& v) { * Upwinding operators **************************************************************************/ Coordinates::FieldMetric V_dot_Grad(const Vector2D& v, const Field2D& f) { - TRACE("V_dot_Grad( Vector2D , Field2D )"); + SCOREP0(); // Get contravariant components of v @@ -334,7 +330,7 @@ Coordinates::FieldMetric V_dot_Grad(const Vector2D& v, const Field2D& f) { } Field3D V_dot_Grad(const Vector2D& v, const Field3D& f) { - TRACE("V_dot_Grad( Vector2D , Field3D )"); + SCOREP0(); // Get contravariant components of v @@ -345,7 +341,7 @@ Field3D V_dot_Grad(const Vector2D& v, const Field3D& f) { } Field3D V_dot_Grad(const Vector3D& v, const Field2D& f) { - TRACE("V_dot_Grad( Vector3D , Field2D )"); + SCOREP0(); // Get contravariant components of v @@ -356,7 +352,7 @@ Field3D V_dot_Grad(const Vector3D& v, const Field2D& f) { } Field3D V_dot_Grad(const Vector3D& v, const Field3D& f) { - TRACE("V_dot_Grad( Vector3D , Field3D )"); + SCOREP0(); // Get contravariant components of v diff --git a/src/field/vector2d.cxx b/src/field/vector2d.cxx index 74d3a88a22..ab2d993549 100644 --- a/src/field/vector2d.cxx +++ b/src/field/vector2d.cxx @@ -429,7 +429,7 @@ CELL_LOC Vector2D::getLocation() const { Vector2D& Vector2D::setLocation(CELL_LOC loc) { SCOREP0(); - TRACE("Vector2D::setLocation"); + if (loc == CELL_DEFAULT) { loc = CELL_CENTRE; } diff --git a/src/field/vector3d.cxx b/src/field/vector3d.cxx index 56124595b3..7fb4a88918 100644 --- a/src/field/vector3d.cxx +++ b/src/field/vector3d.cxx @@ -551,7 +551,7 @@ CELL_LOC Vector3D::getLocation() const { Vector3D& Vector3D::setLocation(CELL_LOC loc) { SCOREP0(); - TRACE("Vector3D::setLocation"); + if (loc == CELL_DEFAULT) { loc = CELL_CENTRE; } diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index 837ac3755f..99aacea4f8 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -304,7 +304,6 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { } Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { - TRACE("LaplaceCyclic::solve(Field3D, Field3D)"); ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx index 5255ea939d..926facd9e6 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx @@ -234,7 +234,6 @@ FieldPerp LaplaceIPT::solve(const FieldPerp& b, const FieldPerp& x0) { ASSERT1(localmesh == b.getMesh() && localmesh == x0.getMesh()); ASSERT1(b.getLocation() == location); ASSERT1(x0.getLocation() == location); - TRACE("LaplaceIPT::solve(const, const)"); FieldPerp x{emptyFrom(b)}; diff --git a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx index 04c8c7e0c9..48137a13bf 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx +++ b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx @@ -46,8 +46,6 @@ LaplaceMultigrid::LaplaceMultigrid(Options* opt, const CELL_LOC loc, Mesh* mesh_ Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), A(0.0), C1(1.0), C2(1.0), D(1.0) { - TRACE("LaplaceMultigrid::LaplaceMultigrid(Options *opt)"); - // periodic x-direction not handled: see MultigridAlg::communications ASSERT1(!localmesh->periodicX); @@ -223,8 +221,6 @@ LaplaceMultigrid::LaplaceMultigrid(Options* opt, const CELL_LOC loc, Mesh* mesh_ FieldPerp LaplaceMultigrid::solve(const FieldPerp& b_in, const FieldPerp& x0) { - TRACE("LaplaceMultigrid::solve(const FieldPerp, const FieldPerp)"); - ASSERT1(localmesh == b_in.getMesh() && localmesh == x0.getMesh()); ASSERT1(b_in.getLocation() == location); ASSERT1(x0.getLocation() == location); @@ -578,7 +574,6 @@ FieldPerp LaplaceMultigrid::solve(const FieldPerp& b_in, const FieldPerp& x0) { } void LaplaceMultigrid::generateMatrixF(int level) { - TRACE("LaplaceMultigrid::generateMatrixF(int)"); // Set (fine-level) matrix entries diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index b619d80100..60169c6eb4 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -308,7 +308,6 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { } Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { - TRACE("LaplacePCR::solve(Field3D, Field3D)"); ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index b48b20f246..600166aa6b 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -304,7 +304,6 @@ FieldPerp LaplacePCR_THOMAS::solve(const FieldPerp& rhs, const FieldPerp& x0) { } Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { - TRACE("LaplacePCR_THOMAS::solve(Field3D, Field3D)"); ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 5f417d4faa..65c80ec48c 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -336,7 +336,6 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b) { return solve(b, b); } * \returns sol The solution x of the problem Ax=b. */ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { - TRACE("LaplacePetsc::solve"); ASSERT1(localmesh == b.getMesh() && localmesh == x0.getMesh()); ASSERT1(b.getLocation() == location); diff --git a/src/invert/laplace/invert_laplace.cxx b/src/invert/laplace/invert_laplace.cxx index 7a8af20fbd..b8650d1d68 100644 --- a/src/invert/laplace/invert_laplace.cxx +++ b/src/invert/laplace/invert_laplace.cxx @@ -156,7 +156,6 @@ void Laplacian::cleanup() { instance.reset(); } **********************************************************************************/ Field3D Laplacian::solve(const Field3D& b) { - TRACE("Laplacian::solve(Field3D)"); ASSERT1(b.getLocation() == location); ASSERT1(localmesh == b.getMesh()); @@ -217,7 +216,6 @@ Field2D Laplacian::solve(const Field2D& b) { * \returns x All the y-slices of x_slice in the equation A*x_slice = b_slice */ Field3D Laplacian::solve(const Field3D& b, const Field3D& x0) { - TRACE("Laplacian::solve(Field3D, Field3D)"); ASSERT1(b.getLocation() == location); ASSERT1(x0.getLocation() == location); diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx index a242bf3432..1de9d58b26 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx @@ -61,7 +61,7 @@ LaplaceXZcyclic::LaplaceXZcyclic(Mesh* m, Options* options, const CELL_LOC loc) } void LaplaceXZcyclic::setCoefs(const Field2D& A2D, const Field2D& B2D) { - TRACE("LaplaceXZcyclic::setCoefs"); + Timer timer("invert"); ASSERT1(A2D.getMesh() == localmesh); diff --git a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx index 769e797352..d81446d522 100644 --- a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx +++ b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx @@ -97,8 +97,6 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) * given by b */ - TRACE("LaplaceXZpetsc::LaplaceXZpetsc"); - if (opt == nullptr) { // If no options supplied, use default opt = &(Options::root())["laplacexz"]; @@ -260,8 +258,6 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) LaplaceXZpetsc::~LaplaceXZpetsc() { - TRACE("LaplaceXZpetsc::~LaplaceXZpetsc"); - PetscBool petsc_is_finalised; PetscFinalized(&petsc_is_finalised); @@ -293,8 +289,6 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { * Bin - The B coefficient in div(A grad_perp(B)) + Bf = b */ - TRACE("LaplaceXZpetsc::setCoefs"); - ASSERT1(Ain.getMesh() == localmesh); ASSERT1(Bin.getMesh() == localmesh); ASSERT1(Ain.getLocation() == location); @@ -767,8 +761,6 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { * result - The solved x (returned as a Field3D) in the matrix problem Ax=b */ - TRACE("LaplaceXZpetsc::solve"); - ASSERT1(bin.getMesh() == localmesh); ASSERT1(x0in.getMesh() == localmesh); ASSERT1(bin.getLocation() == location); diff --git a/src/invert/parderiv/impls/cyclic/cyclic.cxx b/src/invert/parderiv/impls/cyclic/cyclic.cxx index 5a798ca1d5..692d5f2660 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.cxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.cxx @@ -63,7 +63,7 @@ InvertParCR::InvertParCR(Options* opt, CELL_LOC location, Mesh* mesh_in) } const Field3D InvertParCR::solve(const Field3D& f) { - TRACE("InvertParCR::solve(Field3D)"); + ASSERT1(localmesh == f.getMesh()); ASSERT1(location == f.getLocation()); diff --git a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx index be65d5ab0b..bd644dc625 100644 --- a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx +++ b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx @@ -59,7 +59,7 @@ InvertParDivCR::InvertParDivCR(Options* opt, CELL_LOC location, Mesh* mesh_in) } Field3D InvertParDivCR::solve(const Field3D& f) { - TRACE("InvertParDivCR::solve(Field3D)"); + ASSERT1(localmesh == f.getMesh()); ASSERT1(location == f.getLocation()); diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index 5508f8db71..4019fc4eea 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include @@ -30,8 +29,6 @@ using bout::generator::Context; */ #if CHECK > 0 void verifyNumPoints(BoundaryRegion* region, int ptsRequired) { - TRACE("Verifying number of points available for BC"); - int ptsAvailGlobal, ptsAvailLocal, ptsAvail; std::string side, gridType; Mesh* mesh = region->localmesh; @@ -3606,7 +3603,6 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } void BoundaryRelax::apply_ddt(Field2D & f) { - TRACE("BoundaryRelax::apply_ddt(Field2D)"); // Make a copy of f Field2D g = f; @@ -3622,7 +3618,6 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } void BoundaryRelax::apply_ddt(Field3D & f) { - TRACE("BoundaryRelax::apply_ddt(Field3D)"); Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 58d54f826e..b275f0c8de 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -932,7 +932,7 @@ const Field2D& Coordinates::zlength() const { int Coordinates::geometry(bool recalculate_staggered, bool force_interpolate_from_centre) { - TRACE("Coordinates::geometry"); + localmesh->communicate_no_slices(dx, dy, dz, g11, g22, g33, g12, g13, g23, g_11, g_22, g_33, g_12, g_13, g_23, J, Bxy); @@ -1202,7 +1202,6 @@ int Coordinates::geometry(bool recalculate_staggered, } int Coordinates::calcCovariant(const std::string& region) { - TRACE("Coordinates::calcCovariant"); // Make sure metric elements are allocated g_11.allocate(); @@ -1265,7 +1264,6 @@ int Coordinates::calcCovariant(const std::string& region) { } int Coordinates::calcContravariant(const std::string& region) { - TRACE("Coordinates::calcContravariant"); // Make sure metric elements are allocated g11.allocate(); @@ -1320,7 +1318,7 @@ int Coordinates::calcContravariant(const std::string& region) { } int Coordinates::jacobian() { - TRACE("Coordinates::jacobian"); + // calculate Jacobian using g^-1 = det[g^ij], J = sqrt(g) const bool extrapolate_x = not localmesh->sourceHasXBoundaryGuards(); @@ -1522,7 +1520,7 @@ Field3D Coordinates::DDZ(const Field3D& f, CELL_LOC outloc, const std::string& m Coordinates::FieldMetric Coordinates::Grad_par(const Field2D& var, [[maybe_unused]] CELL_LOC outloc, const std::string& UNUSED(method)) { - TRACE("Coordinates::Grad_par( Field2D )"); + ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == var.getLocation())); @@ -1531,7 +1529,7 @@ Coordinates::FieldMetric Coordinates::Grad_par(const Field2D& var, Field3D Coordinates::Grad_par(const Field3D& var, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Grad_par( Field3D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); return ::DDY(var, outloc, method) * invSg(); @@ -1561,7 +1559,7 @@ Field3D Coordinates::Vpar_Grad_par(const Field3D& v, const Field3D& f, CELL_LOC Coordinates::FieldMetric Coordinates::Div_par(const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Div_par( Field2D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); // Need Bxy at location of f, which might be different from location of this @@ -1573,7 +1571,7 @@ Coordinates::FieldMetric Coordinates::Div_par(const Field2D& f, CELL_LOC outloc, Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Div_par( Field3D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); // Need Bxy at location of f, which might be different from location of this @@ -1602,7 +1600,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, Coordinates::FieldMetric Coordinates::Grad2_par2(const Field2D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Grad2_par2( Field2D )"); + ASSERT1(location == outloc || (outloc == CELL_DEFAULT && location == f.getLocation())); auto result = Grad2_par2_DDY_invSg(outloc, method) * DDY(f, outloc, method) @@ -1613,7 +1611,7 @@ Coordinates::FieldMetric Coordinates::Grad2_par2(const Field2D& f, CELL_LOC outl Field3D Coordinates::Grad2_par2(const Field3D& f, CELL_LOC outloc, const std::string& method) { - TRACE("Coordinates::Grad2_par2( Field3D )"); + if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); } @@ -1637,7 +1635,7 @@ Field3D Coordinates::Grad2_par2(const Field3D& f, CELL_LOC outloc, Coordinates::FieldMetric Coordinates::Delp2(const Field2D& f, CELL_LOC outloc, bool UNUSED(useFFT)) { - TRACE("Coordinates::Delp2( Field2D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); auto result = G1 * DDX(f, outloc) + g11 * D2DX2(f, outloc); @@ -1646,7 +1644,6 @@ Coordinates::FieldMetric Coordinates::Delp2(const Field2D& f, CELL_LOC outloc, } Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { - TRACE("Coordinates::Delp2( Field3D )"); if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); @@ -1712,7 +1709,6 @@ Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { } FieldPerp Coordinates::Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { - TRACE("Coordinates::Delp2( FieldPerp )"); if (outloc == CELL_DEFAULT) { outloc = f.getLocation(); @@ -1789,7 +1785,7 @@ Field3D Coordinates::Laplace_par(const Field3D& f, CELL_LOC outloc) { Coordinates::FieldMetric Coordinates::Laplace(const Field2D& f, CELL_LOC outloc, const std::string& dfdy_boundary_conditions, const std::string& dfdy_dy_region) { - TRACE("Coordinates::Laplace( Field2D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); auto result = G1 * DDX(f, outloc) + G2 * DDY(f, outloc) + g11 * D2DX2(f, outloc) @@ -1804,7 +1800,7 @@ Coordinates::FieldMetric Coordinates::Laplace(const Field2D& f, CELL_LOC outloc, Field3D Coordinates::Laplace(const Field3D& f, CELL_LOC outloc, const std::string& dfdy_boundary_conditions, const std::string& dfdy_dy_region) { - TRACE("Coordinates::Laplace( Field3D )"); + ASSERT1(location == outloc || outloc == CELL_DEFAULT); Field3D result = G1 * ::DDX(f, outloc) + G2 * ::DDY(f, outloc) + G3 * ::DDZ(f, outloc) @@ -1823,7 +1819,7 @@ Field3D Coordinates::Laplace(const Field3D& f, CELL_LOC outloc, // solver Field2D Coordinates::Laplace_perpXY([[maybe_unused]] const Field2D& A, [[maybe_unused]] const Field2D& f) { - TRACE("Coordinates::Laplace_perpXY( Field2D )"); + #if not(BOUT_USE_METRIC_3D) Field2D result; result.allocate(); diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index fba64eaa59..952ea22243 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -20,7 +20,6 @@ GridFile::GridFile(std::string gridfilename) : GridDataSource(true), data(bout::OptionsIO::create(gridfilename)->read()), filename(std::move(gridfilename)) { - TRACE("GridFile constructor"); // Get number of y-boundary guard cells saved in the grid file grid_yguards = data["y_boundary_guards"].withDefault(0); @@ -60,7 +59,7 @@ bool GridFile::hasVar(const std::string& name) { return data.isSet(name); } bool GridFile::get(Mesh* UNUSED(m), std::string& sval, const std::string& name, const std::string& def) { Timer timer("io"); - TRACE("GridFile::get(std::string)"); + const bool success = data.isSet(name); if (not success) { // Override any previously set defaults @@ -93,7 +92,7 @@ bool GridFile::get(Mesh* UNUSED(m), std::string& sval, const std::string& name, */ bool GridFile::get(Mesh* UNUSED(m), int& ival, const std::string& name, int def) { Timer timer("io"); - TRACE("GridFile::get(int)"); + const bool success = data.isSet(name); if (not success) { // Override any previously set defaults @@ -110,7 +109,7 @@ bool GridFile::get(Mesh* UNUSED(m), int& ival, const std::string& name, int def) bool GridFile::get(Mesh* UNUSED(m), BoutReal& rval, const std::string& name, BoutReal def) { Timer timer("io"); - TRACE("GridFile::get(BoutReal)"); + const bool success = data.isSet(name); if (not success) { // Override any previously set defaults @@ -472,7 +471,6 @@ bool GridFile::get([[maybe_unused]] Mesh* m, [[maybe_unused]] std::vector& [[maybe_unused]] const std::string& name, [[maybe_unused]] int len, [[maybe_unused]] int offset, [[maybe_unused]] GridDataSource::Direction dir) { - TRACE("GridFile::get(vector)"); if (not data.isSet(name)) { return false; @@ -498,7 +496,6 @@ bool GridFile::get([[maybe_unused]] Mesh* m, [[maybe_unused]] std::vector& bool GridFile::get(Mesh* UNUSED(m), std::vector& var, const std::string& name, int len, int offset, GridDataSource::Direction UNUSED(dir)) { - TRACE("GridFile::get(vector)"); if (not data.isSet(name)) { return false; diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 42fa4d6ca5..9084f90465 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -30,7 +30,6 @@ #include #include #include -#include #include #include #include @@ -458,8 +457,6 @@ Field2D Laplace_perpXY(const Field2D& A, const Field2D& f) { Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field2D , Field2D )"); - if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); } @@ -489,7 +486,6 @@ Coordinates::FieldMetric b0xGrad_dot_Grad(const Field2D& phi, const Field2D& A, } Field3D b0xGrad_dot_Grad(const Field2D& phi, const Field3D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field2D , Field3D )"); if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); @@ -531,7 +527,6 @@ Field3D b0xGrad_dot_Grad(const Field2D& phi, const Field3D& A, CELL_LOC outloc) } Field3D b0xGrad_dot_Grad(const Field3D& p, const Field2D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field3D , Field2D )"); if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); @@ -566,7 +561,6 @@ Field3D b0xGrad_dot_Grad(const Field3D& p, const Field2D& A, CELL_LOC outloc) { } Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, CELL_LOC outloc) { - TRACE("b0xGrad_dot_Grad( Field3D , Field3D )"); if (outloc == CELL_DEFAULT) { outloc = A.getLocation(); @@ -614,7 +608,6 @@ Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, CELL_LOC outloc) Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, BRACKET_METHOD method, CELL_LOC outloc, Solver* UNUSED(solver)) { - TRACE("bracket(Field2D, Field2D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { @@ -637,7 +630,6 @@ Coordinates::FieldMetric bracket(const Field2D& f, const Field2D& g, Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, CELL_LOC outloc, Solver* solver) { - TRACE("bracket(Field3D, Field2D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { @@ -826,7 +818,6 @@ Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method, CELL_LOC outloc, Solver* solver) { - TRACE("bracket(Field2D, Field3D)"); ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { @@ -863,8 +854,6 @@ Field3D bracket(const Field2D& f, const Field3D& g, BRACKET_METHOD method, Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, CELL_LOC outloc, [[maybe_unused]] Solver* solver) { - TRACE("Field3D, Field3D"); - ASSERT1_FIELDS_COMPATIBLE(f, g); if (outloc == CELL_DEFAULT) { outloc = g.getLocation(); diff --git a/src/mesh/fv_ops.cxx b/src/mesh/fv_ops.cxx index fe5422b4d1..2f3398b2c0 100644 --- a/src/mesh/fv_ops.cxx +++ b/src/mesh/fv_ops.cxx @@ -181,7 +181,6 @@ Field3D Div_a_Grad_perp(const Field3D& a, const Field3D& f) { const Field3D Div_par_K_Grad_par(const Field3D& Kin, const Field3D& fin, bool bndry_flux) { - TRACE("FV::Div_par_K_Grad_par"); ASSERT2(Kin.getLocation() == fin.getLocation()); diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 8917fa3753..8eb09f84ec 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -431,7 +431,6 @@ void BoutMesh::setDerivedGridSizes() { } int BoutMesh::load() { - TRACE("BoutMesh::load()"); output_progress << _("Loading mesh") << endl; @@ -1363,7 +1362,6 @@ comm_handle BoutMesh::sendY(FieldGroup& g, comm_handle handle) { } int BoutMesh::wait(comm_handle handle) { - TRACE("BoutMesh::wait(comm_handle)"); if (handle == nullptr) { return 1; diff --git a/src/mesh/index_derivs.cxx b/src/mesh/index_derivs.cxx index 595081e3ae..48284c3536 100644 --- a/src/mesh/index_derivs.cxx +++ b/src/mesh/index_derivs.cxx @@ -25,7 +25,6 @@ #include "bout/traits.hxx" #include #include -#include #include /******************************************************************************* @@ -34,7 +33,6 @@ /// Initialise the derivative methods. Must be called before any derivatives are used void Mesh::derivs_init(Options* options) { - TRACE("Initialising derivatives"); // For each direction need to set what the default method is for each type // of derivative. DerivativeStore::getInstance().initialise(options); @@ -45,7 +43,6 @@ void Mesh::derivs_init(Options* options) { STAGGER Mesh::getStagger(const CELL_LOC inloc, const CELL_LOC outloc, const CELL_LOC allowedStaggerLoc) const { - TRACE("Mesh::getStagger -- three arguments"); ASSERT1(outloc == inloc || (outloc == CELL_CENTRE && inloc == allowedStaggerLoc) || (outloc == allowedStaggerLoc && inloc == CELL_CENTRE)); @@ -61,7 +58,6 @@ STAGGER Mesh::getStagger(const CELL_LOC inloc, const CELL_LOC outloc, STAGGER Mesh::getStagger(const CELL_LOC vloc, [[maybe_unused]] const CELL_LOC inloc, const CELL_LOC outloc, const CELL_LOC allowedStaggerLoc) const { - TRACE("Mesh::getStagger -- four arguments"); ASSERT1(inloc == outloc); ASSERT1(vloc == inloc || (vloc == CELL_CENTRE && inloc == allowedStaggerLoc) || (vloc == allowedStaggerLoc && inloc == CELL_CENTRE)); diff --git a/src/mesh/interpolation_xz.cxx b/src/mesh/interpolation_xz.cxx index f7f0b457f2..a58554dc82 100644 --- a/src/mesh/interpolation_xz.cxx +++ b/src/mesh/interpolation_xz.cxx @@ -25,7 +25,6 @@ #include #include -#include #include #include @@ -36,7 +35,6 @@ const char* strLocation(CELL_LOC loc) { return toString(loc).c_str(); } const Field3D interpolate(const Field3D& f, const Field3D& delta_x, const Field3D& delta_z) { - TRACE("Interpolating 3D field"); XZLagrange4pt interpolateMethod{f.getMesh()}; return interpolateMethod.interpolate(f, delta_x, delta_z); } @@ -47,8 +45,6 @@ const Field3D interpolate(const Field2D& f, const Field3D& delta_x, } const Field3D interpolate(const Field2D& f, const Field3D& delta_x) { - TRACE("interpolate(Field2D, Field3D)"); - Mesh* mesh = f.getMesh(); ASSERT1(mesh == delta_x.getMesh()); Field3D result{emptyFrom(delta_x)}; diff --git a/src/mesh/invert3x3.hxx b/src/mesh/invert3x3.hxx index c011f55bf7..746765d913 100644 --- a/src/mesh/invert3x3.hxx +++ b/src/mesh/invert3x3.hxx @@ -37,7 +37,6 @@ /// return. Otherwise, an empty `std::optional` is return namespace bout { inline std::optional invert3x3(Matrix& a) { - TRACE("invert3x3"); // Calculate the first co-factors const BoutReal A = a(1, 1) * a(2, 2) - a(1, 2) * a(2, 1); diff --git a/src/mesh/mesh.cxx b/src/mesh/mesh.cxx index 1d3f3b0dbe..8964a6b797 100644 --- a/src/mesh/mesh.cxx +++ b/src/mesh/mesh.cxx @@ -298,7 +298,6 @@ bool Mesh::sourceHasYBoundaryGuards() { return source->hasYBoundaryGuards(); } **************************************************************************/ void Mesh::communicateXZ(FieldGroup& g) { - TRACE("Mesh::communicate(FieldGroup&)"); // Send data comm_handle h = sendX(g); @@ -308,7 +307,6 @@ void Mesh::communicateXZ(FieldGroup& g) { } void Mesh::communicateYZ(FieldGroup& g) { - TRACE("Mesh::communicate(FieldGroup&)"); // Send data comm_handle h = sendY(g); @@ -325,7 +323,6 @@ void Mesh::communicateYZ(FieldGroup& g) { } void Mesh::communicate(FieldGroup& g) { - TRACE("Mesh::communicate(FieldGroup&)"); if (include_corner_cells) { // Send data in y-direction diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index e8d3af1cdb..08e56584e1 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -251,7 +251,6 @@ FCIMap::FCIMap(Mesh& mesh, const Coordinates::FieldMetric& UNUSED(dy), Options& } Field3D FCIMap::integrate(Field3D& f) const { - TRACE("FCIMap::integrate"); ASSERT1(f.getDirectionY() == YDirectionType::Standard); ASSERT1(&map_mesh == f.getMesh()); @@ -315,7 +314,6 @@ void FCITransform::checkInputGrid() { } void FCITransform::calcParallelSlices(Field3D& f) { - TRACE("FCITransform::calcParallelSlices"); ASSERT1(f.getDirectionY() == YDirectionType::Standard); // Only have forward_map/backward_map for CELL_CENTRE, so can only deal with @@ -333,7 +331,6 @@ void FCITransform::calcParallelSlices(Field3D& f) { } void FCITransform::integrateParallelSlices(Field3D& f) { - TRACE("FCITransform::integrateParallelSlices"); ASSERT1(f.getDirectionY() == YDirectionType::Standard); // Only have forward_map/backward_map for CELL_CENTRE, so can only deal with diff --git a/src/physics/physicsmodel.cxx b/src/physics/physicsmodel.cxx index f4be3bde2a..a1bde18248 100644 --- a/src/physics/physicsmodel.cxx +++ b/src/physics/physicsmodel.cxx @@ -164,7 +164,6 @@ void PhysicsModel::bout_solve(Vector3D& var, const char* name, } int PhysicsModel::postInit(bool restarting) { - TRACE("PhysicsModel::postInit"); if (restarting) { solver->readEvolvingVariablesFromOptions(restart_options); diff --git a/src/physics/smoothing.cxx b/src/physics/smoothing.cxx index d8f7fb8b6b..309fff1542 100644 --- a/src/physics/smoothing.cxx +++ b/src/physics/smoothing.cxx @@ -37,7 +37,6 @@ #include #include #include -#include #include #include @@ -46,7 +45,6 @@ // Smooth using simple 1-2-1 filter const Field3D smooth_x(const Field3D& f) { - TRACE("smooth_x"); Mesh* mesh = f.getMesh(); Field3D result{emptyFrom(f)}; @@ -76,7 +74,6 @@ const Field3D smooth_x(const Field3D& f) { } const Field3D smooth_y(const Field3D& f) { - TRACE("smooth_y"); Mesh* mesh = f.getMesh(); Field3D result{emptyFrom(f)}; @@ -117,7 +114,6 @@ const Field3D smooth_y(const Field3D& f) { so no processor/branch cuts in X */ const Field2D averageX(const Field2D& f) { - TRACE("averageX(Field2D)"); Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; @@ -176,8 +172,6 @@ const Field2D averageX(const Field2D& f) { */ const Field3D averageX(const Field3D& f) { - TRACE("averageX(Field3D)"); - Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; @@ -230,8 +224,6 @@ const Field3D averageX(const Field3D& f) { } const Field2D averageY(const Field2D& f) { - TRACE("averageY(Field2D)"); - Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; int ngy = mesh->LocalNy; @@ -275,8 +267,6 @@ const Field2D averageY(const Field2D& f) { } const Field3D averageY(const Field3D& f) { - TRACE("averageY(Field3D)"); - Mesh* mesh = f.getMesh(); int ngx = mesh->LocalNx; @@ -431,7 +421,7 @@ void nl_filter(rvec& f, BoutReal w) { } const Field3D nl_filter_x(const Field3D& f, BoutReal w) { - TRACE("nl_filter_x( Field3D )"); + Mesh* mesh = f.getMesh(); Field3D result{emptyFrom(f)}; @@ -453,7 +443,6 @@ const Field3D nl_filter_x(const Field3D& f, BoutReal w) { } const Field3D nl_filter_y(const Field3D& f, BoutReal w) { - TRACE("nl_filter_x( Field3D )"); Mesh* mesh = f.getMesh(); @@ -481,7 +470,6 @@ const Field3D nl_filter_y(const Field3D& f, BoutReal w) { } const Field3D nl_filter_z(const Field3D& fs, BoutReal w) { - TRACE("nl_filter_z( Field3D )"); Mesh* mesh = fs.getMesh(); Field3D result{emptyFrom(fs)}; diff --git a/src/physics/sourcex.cxx b/src/physics/sourcex.cxx index 1cfc2fdc6e..b3244a4109 100644 --- a/src/physics/sourcex.cxx +++ b/src/physics/sourcex.cxx @@ -77,7 +77,6 @@ const Field3D sink_tanhx(const Field2D& UNUSED(f0), const Field3D& f, BoutReal s // create radial buffer zones to set jpar zero near radial boundaries const Field3D mask_x(const Field3D& f, bool UNUSED(BoutRealspace)) { - TRACE("mask_x"); Mesh* localmesh = f.getMesh(); @@ -101,7 +100,6 @@ const Field3D mask_x(const Field3D& f, bool UNUSED(BoutRealspace)) { // create radial buffer zones to set jpar zero near radial boundaries const Field3D sink_tanhxl(const Field2D& UNUSED(f0), const Field3D& f, BoutReal swidth, BoutReal slength, bool UNUSED(BoutRealspace)) { - TRACE("sink_tanhx"); Mesh* localmesh = f.getMesh(); @@ -123,7 +121,6 @@ const Field3D sink_tanhxl(const Field2D& UNUSED(f0), const Field3D& f, BoutReal // create radial buffer zones to set jpar zero near radial boundaries const Field3D sink_tanhxr(const Field2D& UNUSED(f0), const Field3D& f, BoutReal swidth, BoutReal slength, bool UNUSED(BoutRealspace)) { - TRACE("sink_tanhxr"); Mesh* localmesh = f.getMesh(); @@ -144,7 +141,6 @@ const Field3D sink_tanhxr(const Field2D& UNUSED(f0), const Field3D& f, BoutReal // create radial buffer zones to damp Psi to zero near radial boundaries const Field3D buff_x(const Field3D& f, bool UNUSED(BoutRealspace)) { - TRACE("buff_x"); Mesh* localmesh = f.getMesh(); diff --git a/src/solver/impls/adams_bashforth/adams_bashforth.cxx b/src/solver/impls/adams_bashforth/adams_bashforth.cxx index bb81e3d53f..f911e0045a 100644 --- a/src/solver/impls/adams_bashforth/adams_bashforth.cxx +++ b/src/solver/impls/adams_bashforth/adams_bashforth.cxx @@ -304,8 +304,6 @@ void AdamsBashforthSolver::setMaxTimestep(BoutReal dt) { int AdamsBashforthSolver::init() { - TRACE("Initialising AdamsBashforth solver"); - Solver::init(); output << "\n\tAdams-Bashforth (explicit) multistep solver\n"; diff --git a/src/solver/impls/arkode/arkode.cxx b/src/solver/impls/arkode/arkode.cxx index 0151f90167..804e67a803 100644 --- a/src/solver/impls/arkode/arkode.cxx +++ b/src/solver/impls/arkode/arkode.cxx @@ -188,7 +188,6 @@ ArkodeSolver::~ArkodeSolver() { **************************************************************************/ int ArkodeSolver::init() { - TRACE("Initialising ARKODE solver"); Solver::init(); @@ -507,8 +506,6 @@ int ArkodeSolver::init() { **************************************************************************/ int ArkodeSolver::run() { - TRACE("ArkodeSolver::run()"); - if (!initialised) { throw BoutException("ArkodeSolver not initialised\n"); } diff --git a/src/solver/impls/cvode/cvode.cxx b/src/solver/impls/cvode/cvode.cxx index 8b81a66f6d..4f72e65933 100644 --- a/src/solver/impls/cvode/cvode.cxx +++ b/src/solver/impls/cvode/cvode.cxx @@ -177,7 +177,6 @@ CvodeSolver::~CvodeSolver() { **************************************************************************/ int CvodeSolver::init() { - TRACE("Initialising CVODE solver"); Solver::init(); @@ -346,7 +345,6 @@ int CvodeSolver::init() { } } else { output_info.write("\tUsing Newton iteration\n"); - TRACE("Setting preconditioner"); const auto prectype = use_precon ? (rightprec ? SUN_PREC_RIGHT : SUN_PREC_LEFT) : SUN_PREC_NONE; @@ -486,7 +484,6 @@ CvodeSolver::create_constraints(const std::vector>& fields) { **************************************************************************/ int CvodeSolver::run() { - TRACE("CvodeSolver::run()"); if (!cvode_initialised) { throw BoutException("CvodeSolver not initialised\n"); @@ -814,7 +811,7 @@ void CvodeSolver::loop_vector_option_values_op(Ind2D UNUSED(i2d), BoutReal* opti } void CvodeSolver::resetInternalFields() { - TRACE("CvodeSolver::resetInternalFields"); + save_vars(N_VGetArrayPointer(uvec)); if (CVodeReInit(cvode_mem, simtime, uvec) != CV_SUCCESS) { diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 3976f4402c..7e31576a3f 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -33,7 +33,6 @@ void EulerSolver::setMaxTimestep(BoutReal dt) { } int EulerSolver::init() { - TRACE("Initialising Euler solver"); Solver::init(); @@ -63,7 +62,6 @@ int EulerSolver::init() { } int EulerSolver::run() { - TRACE("EulerSolver::run()"); for (int s = 0; s < getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); diff --git a/src/solver/impls/ida/ida.cxx b/src/solver/impls/ida/ida.cxx index e0354e8b57..84a0bd4abd 100644 --- a/src/solver/impls/ida/ida.cxx +++ b/src/solver/impls/ida/ida.cxx @@ -100,8 +100,6 @@ IdaSolver::~IdaSolver() { int IdaSolver::init() { - TRACE("Initialising IDA solver"); - Solver::init(); output.write("Initialising IDA solver\n"); @@ -233,7 +231,6 @@ int IdaSolver::init() { **************************************************************************/ int IdaSolver::run() { - TRACE("IDA IdaSolver::run()"); if (!initialised) { throw BoutException("IdaSolver not initialised\n"); diff --git a/src/solver/impls/imex-bdf2/imex-bdf2.cxx b/src/solver/impls/imex-bdf2/imex-bdf2.cxx index 07188110ab..f18550ed80 100644 --- a/src/solver/impls/imex-bdf2/imex-bdf2.cxx +++ b/src/solver/impls/imex-bdf2/imex-bdf2.cxx @@ -136,8 +136,6 @@ static PetscErrorCode imexbdf2PCapply(PC pc, Vec x, Vec y) { */ int IMEXBDF2::init() { - TRACE("Initialising IMEX-BDF2 solver"); - Solver::init(); output << "\n\tIMEX-BDF2 time-integration solver\n"; @@ -741,7 +739,6 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } int IMEXBDF2::run() { - TRACE("IMEXBDF2::run()"); // Multi-step scheme, so first steps are different int order = 1; diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index d0535f069d..41e43c31ed 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -325,8 +325,6 @@ PetscSolver::~PetscSolver() { int PetscSolver::init() { - TRACE("Initialising PETSc-dev solver"); - Solver::init(); int nlocal = getLocalN(); // Number of evolving variables on this processor @@ -970,7 +968,6 @@ PetscErrorCode PetscSolver::formFunction(Vec U, Vec F) { // Matrix-free preconditioner function PetscErrorCode PetscSolver::pre(Vec x, Vec y) { - TRACE("PetscSolver::pre()"); BoutReal* data; diff --git a/src/solver/impls/power/power.cxx b/src/solver/impls/power/power.cxx index 888338b94c..486129562d 100644 --- a/src/solver/impls/power/power.cxx +++ b/src/solver/impls/power/power.cxx @@ -15,7 +15,6 @@ PowerSolver::PowerSolver(Options* opts) curtime((*options)["curtime"].doc("Simulation time (fixed)").withDefault(0.0)) {} int PowerSolver::init() { - TRACE("Initialising Power solver"); Solver::init(); output << "\n\tPower eigenvalue solver\n"; @@ -44,7 +43,6 @@ int PowerSolver::init() { } int PowerSolver::run() { - TRACE("PowerSolver::run()"); // Make sure that f0 has a norm of 1 divide(f0, norm(f0)); diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 63d3c11753..cb6e81d80a 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -90,7 +90,6 @@ PvodeSolver::~PvodeSolver() { **************************************************************************/ int PvodeSolver::init() { - TRACE("Initialising PVODE solver"); int mudq, mldq, mukeep, mlkeep; boole optIn; @@ -218,7 +217,6 @@ int PvodeSolver::init() { **************************************************************************/ int PvodeSolver::run() { - TRACE("PvodeSolver::run()"); if (!pvode_initialised) { throw BoutException("PvodeSolver not initialised\n"); diff --git a/src/solver/impls/rk3-ssp/rk3-ssp.cxx b/src/solver/impls/rk3-ssp/rk3-ssp.cxx index e13d996c00..14a3c3b0f9 100644 --- a/src/solver/impls/rk3-ssp/rk3-ssp.cxx +++ b/src/solver/impls/rk3-ssp/rk3-ssp.cxx @@ -28,7 +28,6 @@ void RK3SSP::setMaxTimestep(BoutReal dt) { } int RK3SSP::init() { - TRACE("Initialising RK3 SSP solver"); Solver::init(); output << "\n\tRunge-Kutta 3rd-order SSP solver\n"; @@ -63,7 +62,6 @@ int RK3SSP::init() { } int RK3SSP::run() { - TRACE("RK3SSP::run()"); for (int s = 0; s < getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); diff --git a/src/solver/impls/rk4/rk4.cxx b/src/solver/impls/rk4/rk4.cxx index 0e7a942a45..ca77f3fc77 100644 --- a/src/solver/impls/rk4/rk4.cxx +++ b/src/solver/impls/rk4/rk4.cxx @@ -39,8 +39,6 @@ void RK4Solver::setMaxTimestep(BoutReal dt) { int RK4Solver::init() { - TRACE("Initialising RK4 solver"); - Solver::init(); output << "\n\tRunge-Kutta 4th-order solver\n"; @@ -77,8 +75,6 @@ int RK4Solver::init() { } int RK4Solver::run() { - TRACE("RK4Solver::run()"); - for (int s = 0; s < getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); diff --git a/src/solver/impls/rkgeneric/rkgeneric.cxx b/src/solver/impls/rkgeneric/rkgeneric.cxx index 1c332d26de..c5d4ea35e7 100644 --- a/src/solver/impls/rkgeneric/rkgeneric.cxx +++ b/src/solver/impls/rkgeneric/rkgeneric.cxx @@ -40,8 +40,6 @@ void RKGenericSolver::setMaxTimestep(BoutReal dt) { int RKGenericSolver::init() { - TRACE("Initialising RKGeneric solver"); - Solver::init(); output << "\n\tRunge-Kutta generic solver with scheme type " << scheme->getType() << "\n"; @@ -86,8 +84,6 @@ void RKGenericSolver::resetInternalFields() { } int RKGenericSolver::run() { - TRACE("RKGenericSolver::run()"); - for (int s = 0; s < getNumberOutputSteps(); s++) { BoutReal target = simtime + getOutputTimestep(); diff --git a/src/solver/impls/slepc/slepc.cxx b/src/solver/impls/slepc/slepc.cxx index 13657a4553..d2eef6d59f 100644 --- a/src/solver/impls/slepc/slepc.cxx +++ b/src/solver/impls/slepc/slepc.cxx @@ -241,8 +241,6 @@ SlepcSolver::~SlepcSolver() { int SlepcSolver::init() { - TRACE("Initialising SLEPc solver"); - // Report initialisation output.write("Initialising SLEPc solver\n"); if (selfSolve) { diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 311bc371fe..05c3cbdc5c 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -607,9 +607,6 @@ SNESSolver::SNESSolver(Options* opts) .withDefault(false)) {} int SNESSolver::init() { - - TRACE("Initialising SNES solver"); - Solver::init(); output << "\n\tSNES steady state solver\n"; @@ -829,8 +826,6 @@ int SNESSolver::init() { } int SNESSolver::run() { - TRACE("SNESSolver::run()"); - // Set initial guess at the solution from variables { BoutReal* xdata = nullptr; diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index ea7866051b..ac3d632419 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -664,9 +664,6 @@ void Solver::writeToModelOutputFile(const Options& options) { **************************************************************************/ int Solver::init() { - - TRACE("Solver::init()"); - if (initialised) { throw BoutException(_("ERROR: Solver is already initialised\n")); } diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 077b2a6d85..bb4c920b90 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -106,8 +106,6 @@ Options::Options(InitializerList values, Options* parent_instance, } Options& Options::operator[](const std::string& name) { - TRACE("Options::operator[]"); - if (isValue()) { throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " "value, not a section.\n" @@ -145,8 +143,6 @@ Options& Options::operator[](const std::string& name) { } const Options& Options::operator[](const std::string& name) const { - TRACE("Options::operator[] const"); - if (isValue()) { throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " "value, not a section.\n" diff --git a/src/sys/options/options_ini.cxx b/src/sys/options/options_ini.cxx index d1889f993b..939e100f1b 100644 --- a/src/sys/options/options_ini.cxx +++ b/src/sys/options/options_ini.cxx @@ -50,12 +50,10 @@ **************************************************************************/ #include "options_ini.hxx" + #include -#include #include -#include - using namespace std; /************************************************************************** @@ -151,8 +149,6 @@ void OptionINI::read(Options* options, const string& filename) { } void OptionINI::write(Options* options, const std::string& filename) { - TRACE("OptionsINI::write"); - std::ofstream fout; fout.open(filename, ios::out | ios::trunc); diff --git a/src/sys/optionsreader.cxx b/src/sys/optionsreader.cxx index 8624f646df..e16df31c97 100644 --- a/src/sys/optionsreader.cxx +++ b/src/sys/optionsreader.cxx @@ -1,6 +1,5 @@ #include #include -#include #include #include @@ -23,7 +22,6 @@ OptionsReader* OptionsReader::getInstance() { } void OptionsReader::read(Options* options, const std::string& filename) { - TRACE("OptionsReader::read"); if (filename.empty()) { throw BoutException("OptionsReader::read passed empty filename\n"); } @@ -34,7 +32,6 @@ void OptionsReader::read(Options* options, const std::string& filename) { } void OptionsReader::write(Options* options, const std::string& filename) { - TRACE("OptionsReader::write"); if (filename.empty()) { throw BoutException("OptionsReader::write passed empty filename\n"); } diff --git a/tests/integrated/test-invertable-operator/invertable_operator.cxx b/tests/integrated/test-invertable-operator/invertable_operator.cxx index 3c99c3dbbd..9f00fad304 100644 --- a/tests/integrated/test-invertable-operator/invertable_operator.cxx +++ b/tests/integrated/test-invertable-operator/invertable_operator.cxx @@ -1,5 +1,4 @@ #include -#include #include #include @@ -16,7 +15,7 @@ class InvertableOperatorTest : public PhysicsModel { // Drop C term for now Field3D operator()(const Field3D& input) { - TRACE("myLaplacian::operator()"); + Field3D result = A * input + D * Delp2(input); // Ensure boundary points are set appropriately as given by the input field. diff --git a/tests/integrated/test-vec/testVec.cxx b/tests/integrated/test-vec/testVec.cxx index bd0e5f2b80..905ffac01a 100644 --- a/tests/integrated/test-vec/testVec.cxx +++ b/tests/integrated/test-vec/testVec.cxx @@ -14,13 +14,11 @@ class VecTest : public PhysicsModel { }; int VecTest::init(bool UNUSED(restarting)) { - TRACE("Halt in VecTest::init"); SOLVE_FOR(n); return 0; } int VecTest::rhs(BoutReal UNUSED(t)) { - TRACE("Halt in VecTest::rhs"); mesh->communicate(n); gradPerpN = Grad_perp(n); mesh->communicate(gradPerpN); From 63e78ae3738fa89ee2b4ac9f309805c296ff906c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 15:56:59 +0000 Subject: [PATCH 128/461] Fix unnecessary includes of `msg_stack.hxx` --- examples/elm-pb-outerloop/elm_pb_outerloop.cxx | 1 - examples/elm-pb/elm_pb.cxx | 1 - include/bout/bout_enum_class.hxx | 9 ++++----- include/bout/deriv_store.hxx | 1 - include/bout/field.hxx | 2 -- include/bout/index_derivs.hxx | 1 - include/bout/mask.hxx | 1 - include/bout/sys/timer.hxx | 1 - include/bout/utils.hxx | 1 - src/field/field.cxx | 1 - src/field/initialprofiles.cxx | 1 - src/invert/laplace/impls/multigrid/multigrid_laplace.cxx | 1 - src/invert/laplace/invert_laplace.cxx | 1 - src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx | 1 - src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx | 4 +--- src/invert/parderiv/impls/cyclic/cyclic.cxx | 4 +--- src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx | 1 - src/mesh/coordinates.cxx | 1 - src/mesh/data/gridfromfile.cxx | 1 - src/mesh/fv_ops.cxx | 1 - src/physics/sourcex.cxx | 8 +++----- src/solver/impls/adams_bashforth/adams_bashforth.cxx | 4 +--- src/solver/impls/euler/euler.cxx | 6 +----- src/solver/impls/imex-bdf2/imex-bdf2.cxx | 1 - src/solver/impls/power/power.cxx | 4 +--- src/solver/impls/rk3-ssp/rk3-ssp.cxx | 2 -- src/solver/impls/rk4/rk4.cxx | 4 +--- src/solver/impls/rkgeneric/rkgeneric.cxx | 6 +----- src/solver/impls/slepc/slepc.cxx | 1 - src/solver/impls/snes/snes.cxx | 6 ++---- src/sys/bout_types.cxx | 2 +- src/sys/derivs.cxx | 8 ++------ src/sys/timer.cxx | 2 ++ tests/integrated/test-backtrace/boutexcept.cxx | 1 - 34 files changed, 21 insertions(+), 69 deletions(-) diff --git a/examples/elm-pb-outerloop/elm_pb_outerloop.cxx b/examples/elm-pb-outerloop/elm_pb_outerloop.cxx index 4ff8e90dcb..3cc5227b30 100644 --- a/examples/elm-pb-outerloop/elm_pb_outerloop.cxx +++ b/examples/elm-pb-outerloop/elm_pb_outerloop.cxx @@ -39,7 +39,6 @@ #include #include #include -#include #include #include // Defines BOUT_FOR_RAJA #include diff --git a/examples/elm-pb/elm_pb.cxx b/examples/elm-pb/elm_pb.cxx index fe18a756ae..643f36f5bb 100644 --- a/examples/elm-pb/elm_pb.cxx +++ b/examples/elm-pb/elm_pb.cxx @@ -13,7 +13,6 @@ #include #include #include -#include #include #include diff --git a/include/bout/bout_enum_class.hxx b/include/bout/bout_enum_class.hxx index 07140220a4..fa4abc8090 100644 --- a/include/bout/bout_enum_class.hxx +++ b/include/bout/bout_enum_class.hxx @@ -22,13 +22,12 @@ #ifndef BOUT_ENUM_CLASS_H #define BOUT_ENUM_CLASS_H -#include "bout/boutexception.hxx" +#include "bout/boutexception.hxx" // IWYU pragma: keep #include "bout/macro_for_each.hxx" -#include "bout/msg_stack.hxx" -#include "bout/options.hxx" +#include "bout/options.hxx" // IWYU pragma: keep -#include -#include +#include // IWYU pragma: keep +#include // IWYU pragma: keep /// Create some macro magic similar to bout/macro_for_each.hxx, but allowing for the enum /// class name to be passed through to each _call diff --git a/include/bout/deriv_store.hxx b/include/bout/deriv_store.hxx index 866ac558e5..0e03d6a32e 100644 --- a/include/bout/deriv_store.hxx +++ b/include/bout/deriv_store.hxx @@ -38,7 +38,6 @@ #include #include -#include #include /// Here we have a templated singleton that is used to store DerivativeFunctions diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 35ce42716d..4e41aa3632 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -30,14 +30,12 @@ class Field; #define FIELD_H #include -#include #include #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" #include "bout/field_data.hxx" -#include "bout/msg_stack.hxx" #include "bout/region.hxx" #include "bout/traits.hxx" #include "bout/utils.hxx" diff --git a/include/bout/index_derivs.hxx b/include/bout/index_derivs.hxx index 7c2be52ded..ccce9a7f5e 100644 --- a/include/bout/index_derivs.hxx +++ b/include/bout/index_derivs.hxx @@ -43,7 +43,6 @@ #include #include #include -#include #include #include diff --git a/include/bout/mask.hxx b/include/bout/mask.hxx index fd90ae7345..fc5f814676 100644 --- a/include/bout/mask.hxx +++ b/include/bout/mask.hxx @@ -26,7 +26,6 @@ #include "bout/globals.hxx" #include "bout/mesh.hxx" -#include "bout/msg_stack.hxx" /** * 3D array of bools to mask Field3Ds diff --git a/include/bout/sys/timer.hxx b/include/bout/sys/timer.hxx index 90fd632f62..c6a0c9020a 100644 --- a/include/bout/sys/timer.hxx +++ b/include/bout/sys/timer.hxx @@ -7,7 +7,6 @@ #include #include "bout/msg_stack.hxx" -#include "bout/output.hxx" /*! * Timing class for performance benchmarking and diagnosis diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index f8d6f08856..04aa0b281d 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -35,7 +35,6 @@ #include "bout/assert.hxx" #include "bout/bout_types.hxx" #include "bout/boutexception.hxx" -#include "bout/msg_stack.hxx" #include "bout/region.hxx" #include "bout/unused.hxx" diff --git a/src/field/field.cxx b/src/field/field.cxx index 797df6c405..e9b01f0bcb 100644 --- a/src/field/field.cxx +++ b/src/field/field.cxx @@ -27,7 +27,6 @@ #include #include #include -#include #include #include diff --git a/src/field/initialprofiles.cxx b/src/field/initialprofiles.cxx index 5e96d6005a..2c34caa6e1 100644 --- a/src/field/initialprofiles.cxx +++ b/src/field/initialprofiles.cxx @@ -42,7 +42,6 @@ #include #include #include -#include void initial_profile(const std::string& name, Field3D& var) { diff --git a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx index 48137a13bf..4be5bfbad3 100644 --- a/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx +++ b/src/invert/laplace/impls/multigrid/multigrid_laplace.cxx @@ -33,7 +33,6 @@ #if not BOUT_USE_METRIC_3D #include -#include #include #if BOUT_USE_OPENMP diff --git a/src/invert/laplace/invert_laplace.cxx b/src/invert/laplace/invert_laplace.cxx index b8650d1d68..e96248ef7e 100644 --- a/src/invert/laplace/invert_laplace.cxx +++ b/src/invert/laplace/invert_laplace.cxx @@ -38,7 +38,6 @@ #include #include #include -#include #include #include #include diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx index 1de9d58b26..dc987edc75 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx @@ -5,7 +5,6 @@ #include #include -#include #include #include diff --git a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx index d81446d522..be23d8d014 100644 --- a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx +++ b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx @@ -15,10 +15,8 @@ #if BOUT_HAS_PETSC // Requires PETSc #include -#include - -#include #include +#include LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) : LaplaceXZ(m, opt, loc), lib(opt == nullptr ? &(Options::root()["laplacexz"]) : opt), diff --git a/src/invert/parderiv/impls/cyclic/cyclic.cxx b/src/invert/parderiv/impls/cyclic/cyclic.cxx index 692d5f2660..61d23be823 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.cxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.cxx @@ -46,10 +46,8 @@ #include #include #include -#include -#include - #include +#include #include diff --git a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx index bd644dc625..d4e773f017 100644 --- a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx +++ b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx @@ -46,7 +46,6 @@ #include #include #include -#include #include #include diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index b275f0c8de..8453a5205f 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -8,7 +8,6 @@ #include #include #include -#include #include #include #include diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 952ea22243..10f6f6926e 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -6,7 +6,6 @@ #include #include #include -#include #include #include #include diff --git a/src/mesh/fv_ops.cxx b/src/mesh/fv_ops.cxx index 2f3398b2c0..a0a6e1d3c1 100644 --- a/src/mesh/fv_ops.cxx +++ b/src/mesh/fv_ops.cxx @@ -1,6 +1,5 @@ #include #include -#include #include #include diff --git a/src/physics/sourcex.cxx b/src/physics/sourcex.cxx index b3244a4109..659abbf409 100644 --- a/src/physics/sourcex.cxx +++ b/src/physics/sourcex.cxx @@ -2,15 +2,13 @@ * radial source and mask operators **************************************************************/ -#include -#include - #include +#include #include -#include #include +#include -#include "bout/unused.hxx" +#include BoutReal TanH(BoutReal a) { BoutReal temp = exp(a); diff --git a/src/solver/impls/adams_bashforth/adams_bashforth.cxx b/src/solver/impls/adams_bashforth/adams_bashforth.cxx index f911e0045a..72748fd52e 100644 --- a/src/solver/impls/adams_bashforth/adams_bashforth.cxx +++ b/src/solver/impls/adams_bashforth/adams_bashforth.cxx @@ -4,10 +4,8 @@ #include #include -#include -#include - #include +#include #include diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 7e31576a3f..2a9e5813f2 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -3,13 +3,9 @@ #include #include -#include #include -#include - -#include - #include +#include EulerSolver::EulerSolver(Options* options) : Solver(options), mxstep((*options)["mxstep"] diff --git a/src/solver/impls/imex-bdf2/imex-bdf2.cxx b/src/solver/impls/imex-bdf2/imex-bdf2.cxx index f18550ed80..062979b59a 100644 --- a/src/solver/impls/imex-bdf2/imex-bdf2.cxx +++ b/src/solver/impls/imex-bdf2/imex-bdf2.cxx @@ -8,7 +8,6 @@ #include #include #include -#include #include #include diff --git a/src/solver/impls/power/power.cxx b/src/solver/impls/power/power.cxx index 486129562d..5dfccfedda 100644 --- a/src/solver/impls/power/power.cxx +++ b/src/solver/impls/power/power.cxx @@ -3,13 +3,11 @@ #include "power.hxx" #include -#include +#include #include #include -#include - PowerSolver::PowerSolver(Options* opts) : Solver(opts), curtime((*options)["curtime"].doc("Simulation time (fixed)").withDefault(0.0)) {} diff --git a/src/solver/impls/rk3-ssp/rk3-ssp.cxx b/src/solver/impls/rk3-ssp/rk3-ssp.cxx index 14a3c3b0f9..7c00d44b34 100644 --- a/src/solver/impls/rk3-ssp/rk3-ssp.cxx +++ b/src/solver/impls/rk3-ssp/rk3-ssp.cxx @@ -3,10 +3,8 @@ #include #include -#include #include #include -#include #include diff --git a/src/solver/impls/rk4/rk4.cxx b/src/solver/impls/rk4/rk4.cxx index ca77f3fc77..35b3cc2268 100644 --- a/src/solver/impls/rk4/rk4.cxx +++ b/src/solver/impls/rk4/rk4.cxx @@ -3,14 +3,12 @@ #include #include -#include #include +#include #include #include -#include - RK4Solver::RK4Solver(Options* opts) : Solver(opts), atol((*options)["atol"].doc("Absolute tolerance").withDefault(1.e-5)), rtol((*options)["rtol"].doc("Relative tolerance").withDefault(1.e-3)), diff --git a/src/solver/impls/rkgeneric/rkgeneric.cxx b/src/solver/impls/rkgeneric/rkgeneric.cxx index c5d4ea35e7..edf8ed50e3 100644 --- a/src/solver/impls/rkgeneric/rkgeneric.cxx +++ b/src/solver/impls/rkgeneric/rkgeneric.cxx @@ -4,12 +4,8 @@ #include #include -#include -#include - -#include - #include +#include RKGenericSolver::RKGenericSolver(Options* opts) : Solver(opts), atol((*options)["atol"].doc("Absolute tolerance").withDefault(1.e-5)), diff --git a/src/solver/impls/slepc/slepc.cxx b/src/solver/impls/slepc/slepc.cxx index d2eef6d59f..2c52cb3b38 100644 --- a/src/solver/impls/slepc/slepc.cxx +++ b/src/solver/impls/slepc/slepc.cxx @@ -31,7 +31,6 @@ #include #include #include -#include #include #include diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 05c3cbdc5c..e1ef477105 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -1,4 +1,4 @@ -#include "bout/build_config.hxx" +#include "bout/build_defines.hxx" #if BOUT_HAS_PETSC @@ -7,7 +7,7 @@ #include #include #include -#include +#include #include #include #include @@ -18,8 +18,6 @@ #include #include -#include - #include "petscerror.h" #include "petscmat.h" #include "petscpc.h" diff --git a/src/sys/bout_types.cxx b/src/sys/bout_types.cxx index f5e971af78..b27fab7ae7 100644 --- a/src/sys/bout_types.cxx +++ b/src/sys/bout_types.cxx @@ -1,6 +1,6 @@ #include #include -#include + #include namespace { diff --git a/src/sys/derivs.cxx b/src/sys/derivs.cxx index ed036fffd4..13be388e97 100644 --- a/src/sys/derivs.cxx +++ b/src/sys/derivs.cxx @@ -44,14 +44,10 @@ #include #include #include -#include -#include -#include - -#include - #include +#include #include +#include /******************************************************************************* * First central derivatives diff --git a/src/sys/timer.cxx b/src/sys/timer.cxx index 314d599905..410cffad94 100644 --- a/src/sys/timer.cxx +++ b/src/sys/timer.cxx @@ -1,5 +1,7 @@ #include "bout/sys/timer.hxx" +#include + #include #include diff --git a/tests/integrated/test-backtrace/boutexcept.cxx b/tests/integrated/test-backtrace/boutexcept.cxx index ed33a4c018..70bf480862 100644 --- a/tests/integrated/test-backtrace/boutexcept.cxx +++ b/tests/integrated/test-backtrace/boutexcept.cxx @@ -1,5 +1,4 @@ #include "bout/boutexception.hxx" -#include "bout/msg_stack.hxx" void troublemaker() { throw BoutException("test"); } void f() { troublemaker(); } From c42afa3bd3de747bd1daf3a63dede907112e5fa8 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 16:37:07 +0000 Subject: [PATCH 129/461] Don't duplicate exception message when thrown from `BoutInitialise` --- include/bout/physicsmodel.hxx | 27 ++++----- src/bout++.cxx | 106 ++++++++++++++++------------------ 2 files changed, 62 insertions(+), 71 deletions(-) diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index baa284c862..d952c45efb 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -418,26 +418,26 @@ private: */ #define BOUTMAIN(ModelClass) \ int main(int argc, char** argv) { \ - int init_err = BoutInitialise(argc, argv); \ - if (init_err < 0) { \ - return 0; \ - } \ - if (init_err > 0) { \ - return init_err; \ - } \ try { \ + int init_err = BoutInitialise(argc, argv); \ + if (init_err < 0) { \ + return 0; \ + } \ + if (init_err > 0) { \ + return init_err; \ + } \ auto model = bout::utils::make_unique(); \ auto solver = Solver::create(); \ solver->setModel(model.get()); \ auto bout_monitor = bout::utils::make_unique(); \ solver->addMonitor(bout_monitor.get(), Solver::BACK); \ solver->solve(); \ + BoutFinalise(); \ + return 0; \ } catch (const BoutException& e) { \ output.write("Error encountered: {}\n", e.what()); \ MPI_Abort(BoutComm::get(), 1); \ } \ - BoutFinalise(); \ - return 0; \ } /// Macro to replace solver->add, passing variable name @@ -480,8 +480,7 @@ private: /// Add fields to the solver. /// This should accept up to ten arguments -#define SOLVE_FOR(...) \ - { MACRO_FOR_EACH(SOLVE_FOR1, __VA_ARGS__) } +#define SOLVE_FOR(...) {MACRO_FOR_EACH(SOLVE_FOR1, __VA_ARGS__)} /// Write this variable once to the grid file #define SAVE_ONCE1(var) dump.addOnce(var, #var); @@ -521,8 +520,7 @@ private: dump.addOnce(var6, #var6); \ } -#define SAVE_ONCE(...) \ - { MACRO_FOR_EACH(SAVE_ONCE1, __VA_ARGS__) } +#define SAVE_ONCE(...) {MACRO_FOR_EACH(SAVE_ONCE1, __VA_ARGS__)} /// Write this variable every timestep #define SAVE_REPEAT1(var) dump.addRepeat(var, #var); @@ -562,7 +560,6 @@ private: dump.addRepeat(var6, #var6); \ } -#define SAVE_REPEAT(...) \ - { MACRO_FOR_EACH(SAVE_REPEAT1, __VA_ARGS__) } +#define SAVE_REPEAT(...) {MACRO_FOR_EACH(SAVE_REPEAT1, __VA_ARGS__)} #endif // BOUT_PHYSICS_MODEL_H diff --git a/src/bout++.cxx b/src/bout++.cxx index 9c859aaaa7..ba49a208be 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -141,74 +141,68 @@ int BoutInitialise(int& argc, char**& argv) { return 1; } - try { - checkDataDirectoryIsAccessible(args.data_dir); + checkDataDirectoryIsAccessible(args.data_dir); - // Set the command-line arguments - SlepcLib::setArgs(argc, argv); // SLEPc initialisation - PetscLib::setArgs(argc, argv); // PETSc initialisation - Solver::setArgs(argc, argv); // Solver initialisation - BoutComm::setArgs(argc, argv); // MPI initialisation + // Set the command-line arguments + SlepcLib::setArgs(argc, argv); // SLEPc initialisation + PetscLib::setArgs(argc, argv); // PETSc initialisation + Solver::setArgs(argc, argv); // Solver initialisation + BoutComm::setArgs(argc, argv); // MPI initialisation - const int MYPE = BoutComm::rank(); + const int MYPE = BoutComm::rank(); - setupBoutLogColor(args.color_output, MYPE); + setupBoutLogColor(args.color_output, MYPE); - setupOutput(args.data_dir, args.log_file, args.verbosity, MYPE); + setupOutput(args.data_dir, args.log_file, args.verbosity, MYPE); - savePIDtoFile(args.data_dir, MYPE); + savePIDtoFile(args.data_dir, MYPE); #if BOUT_HAS_ADIOS2 - bout::ADIOSInit(BoutComm::get()); + bout::ADIOSInit(BoutComm::get()); #endif - // Print the different parts of the startup info - printStartupHeader(MYPE, BoutComm::size()); - printCompileTimeOptions(); - printCommandLineArguments(args.original_argv); - - // Load settings file - OptionsReader* reader = OptionsReader::getInstance(); - // Ideally we'd use the long options for `datadir` and - // `optionfile` here, but we'd need to call parseCommandLine - // _first_ in order to do that and set the source, etc., but we - // need to call that _second_ in order to override the input file - reader->read(Options::getRoot(), "{}", (args.data_dir / args.opt_file).string()); - - // Get options override from command-line - reader->parseCommandLine(Options::getRoot(), args.argv); - - // Get the variables back out so they count as having been used - // when checking for unused options. They normally _do_ get used, - // but it's possible that only happens in BoutFinalise, which is - // too late for that check. - const auto datadir = Options::root()["datadir"].withDefault(DEFAULT_DIR); - [[maybe_unused]] const auto optionfile = - Options::root()["optionfile"].withDefault(args.opt_file); - const auto settingsfile = - Options::root()["settingsfile"].withDefault(args.set_file); - - setRunStartInfo(Options::root()); - - if (MYPE == 0) { - writeSettingsFile(Options::root(), datadir, settingsfile); - } - - bout::globals::mpi = new MpiWrapper(); + // Print the different parts of the startup info + printStartupHeader(MYPE, BoutComm::size()); + printCompileTimeOptions(); + printCommandLineArguments(args.original_argv); + + // Load settings file + OptionsReader* reader = OptionsReader::getInstance(); + // Ideally we'd use the long options for `datadir` and + // `optionfile` here, but we'd need to call parseCommandLine + // _first_ in order to do that and set the source, etc., but we + // need to call that _second_ in order to override the input file + reader->read(Options::getRoot(), "{}", (args.data_dir / args.opt_file).string()); + + // Get options override from command-line + reader->parseCommandLine(Options::getRoot(), args.argv); + + // Get the variables back out so they count as having been used + // when checking for unused options. They normally _do_ get used, + // but it's possible that only happens in BoutFinalise, which is + // too late for that check. + const auto datadir = Options::root()["datadir"].withDefault(DEFAULT_DIR); + [[maybe_unused]] const auto optionfile = + Options::root()["optionfile"].withDefault(args.opt_file); + const auto settingsfile = + Options::root()["settingsfile"].withDefault(args.set_file); + + setRunStartInfo(Options::root()); + + if (MYPE == 0) { + writeSettingsFile(Options::root(), datadir, settingsfile); + } - // Create the mesh - bout::globals::mesh = Mesh::create(); - // Load from sources. Required for Field initialisation - bout::globals::mesh->load(); + bout::globals::mpi = new MpiWrapper(); - // time_report options are used in BoutFinalise, i.e. after we - // check for unused options - Options::root()["time_report"].setConditionallyUsed(); + // Create the mesh + bout::globals::mesh = Mesh::create(); + // Load from sources. Required for Field initialisation + bout::globals::mesh->load(); - } catch (const BoutException& e) { - output_error.write(_("Error encountered during initialisation: {:s}\n"), e.what()); - throw; - } + // time_report options are used in BoutFinalise, i.e. after we + // check for unused options + Options::root()["time_report"].setConditionallyUsed(); return 0; } From 6d89b6fd6543482d8b5bda02c1d276682eb2423e Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 16:38:11 +0000 Subject: [PATCH 130/461] Don't print `MsgStack` dump if it's empty --- include/bout/msg_stack.hxx | 3 +++ src/sys/boutexception.cxx | 15 ++++++++------- 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 765a3120cf..9e218e9efb 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -89,6 +89,9 @@ public: std::string getDump() { return ""; } #endif + /// Current stack size + std::size_t size() const { return position; } + private: std::vector stack; ///< Message stack; std::vector::size_type position{0}; ///< Position in stack diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index 8509dc40ba..931720d537 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -15,10 +15,6 @@ #include -namespace { -const std::string header{"====== Exception thrown ======\n"}; -} - bool BoutException::show_backtrace = true; void BoutParallelThrowRhsFail(int status, const char* message) { @@ -71,8 +67,13 @@ std::string BoutException::getBacktrace() const { and (frame.symbol != "_start")); }); - const std::string backtrace_message = formatter.format(generate_trace()); + std::string backtrace_message = + fmt::format("{}\n\n====== Exception thrown ======\n{}", + formatter.format(generate_trace()), message); - return fmt::format("{}\n{}\n{}{}\n", backtrace_message, msg_stack.getDump(), header, - message); + if (msg_stack.size() > 0) { + return fmt::format("{}\n\nAdditional information:\n{}", backtrace_message, + msg_stack.getDump()); + } + return backtrace_message; } From 8896f6eb05674772d7fe00e08d35471eae8319d1 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Mon, 5 Jan 2026 16:43:08 +0000 Subject: [PATCH 131/461] Apply clang-format changes --- include/bout/bout_enum_class.hxx | 5 +++-- include/bout/invertable_operator.hxx | 4 ++-- include/bout/physicsmodel.hxx | 9 ++++++--- 3 files changed, 11 insertions(+), 7 deletions(-) diff --git a/include/bout/bout_enum_class.hxx b/include/bout/bout_enum_class.hxx index fa4abc8090..6f632747ed 100644 --- a/include/bout/bout_enum_class.hxx +++ b/include/bout/bout_enum_class.hxx @@ -85,11 +85,12 @@ BOUT_ENUM_CLASS_MAP_ARGS(BOUT_STR_ENUM_CLASS, enumname, __VA_ARGS__)}; \ auto found = fromString_map.find(s); \ if (found == fromString_map.end()) { \ - std::string valid_values {}; \ + std::string valid_values{}; \ for (auto const& entry : fromString_map) { \ valid_values += std::string(" ") + entry.first; \ } \ - throw BoutException("Did not find enum {:s}. Valid values: {:s}", s, valid_values); \ + throw BoutException("Did not find enum {:s}. Valid values: {:s}", s, \ + valid_values); \ } \ return found->second; \ } \ diff --git a/include/bout/invertable_operator.hxx b/include/bout/invertable_operator.hxx index 6dcd92897a..d61c258654 100644 --- a/include/bout/invertable_operator.hxx +++ b/include/bout/invertable_operator.hxx @@ -135,9 +135,9 @@ public: : operatorFunction(func), preconditionerFunction(func), opt(optIn == nullptr ? Options::getRoot()->getSection("invertableOperator") : optIn), - localmesh(localmeshIn == nullptr ? bout::globals::mesh : localmeshIn), lib(opt) { + localmesh(localmeshIn == nullptr ? bout::globals::mesh : localmeshIn), lib(opt){ - }; + }; /// Destructor just has to cleanup the PETSc owned objects. ~InvertableOperator() { diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index d952c45efb..81f17a39f0 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -480,7 +480,8 @@ private: /// Add fields to the solver. /// This should accept up to ten arguments -#define SOLVE_FOR(...) {MACRO_FOR_EACH(SOLVE_FOR1, __VA_ARGS__)} +#define SOLVE_FOR(...) \ + { MACRO_FOR_EACH(SOLVE_FOR1, __VA_ARGS__) } /// Write this variable once to the grid file #define SAVE_ONCE1(var) dump.addOnce(var, #var); @@ -520,7 +521,8 @@ private: dump.addOnce(var6, #var6); \ } -#define SAVE_ONCE(...) {MACRO_FOR_EACH(SAVE_ONCE1, __VA_ARGS__)} +#define SAVE_ONCE(...) \ + { MACRO_FOR_EACH(SAVE_ONCE1, __VA_ARGS__) } /// Write this variable every timestep #define SAVE_REPEAT1(var) dump.addRepeat(var, #var); @@ -560,6 +562,7 @@ private: dump.addRepeat(var6, #var6); \ } -#define SAVE_REPEAT(...) {MACRO_FOR_EACH(SAVE_REPEAT1, __VA_ARGS__)} +#define SAVE_REPEAT(...) \ + { MACRO_FOR_EACH(SAVE_REPEAT1, __VA_ARGS__) } #endif // BOUT_PHYSICS_MODEL_H From e497fcd3a0f9811538cd374369b97bb9631f9abf Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 16:59:29 +0000 Subject: [PATCH 132/461] docs: Use build directory for building library Required because cpptrace prevents in-source builds Also switch to using `subprocess.run` so we can better see errors --- manual/sphinx/conf.py | 29 ++++++++++++++--------------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 3aa6e94dc3..6b997ec111 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -82,22 +82,21 @@ def __getattr__(cls, name): pwd = "/".join(os.getcwd().split("/")[:-2]) os.system("git submodule update --init --recursive ../../externalpackages/fmt") cmake = ( - "cmake . -DBOUT_USE_FFTW=ON" - + " -DBOUT_USE_LAPACK=OFF" - + " -DBOUT_ENABLE_PYTHON=ON" - + " -DBOUT_UPDATE_GIT_SUBMODULE=OFF" - + " -DBOUT_TESTS=OFF" - + " -DBOUT_ALLOW_INSOURCE_BUILD=ON" - + f" -DPython3_ROOT_DIR={pydir}" - + f" -Dmpark_variant_DIR={pwd}/externalpackages/mpark.variant/" - + f" -Dfmt_DIR={pwd}/externalpackages/fmt/" + "cmake . " + " -B build" + " -DBOUT_USE_FFTW=ON" + " -DBOUT_USE_LAPACK=OFF" + " -DBOUT_ENABLE_PYTHON=ON" + " -DBOUT_UPDATE_GIT_SUBMODULE=OFF" + " -DBOUT_TESTS=OFF" + " -DBOUT_ALLOW_INSOURCE_BUILD=ON" + f" -DPython3_ROOT_DIR={pydir}" + f" -Dmpark_variant_DIR={pwd}/externalpackages/mpark.variant/" + f" -Dfmt_DIR={pwd}/externalpackages/fmt/" ) - # os.system("mkdir ../../build") - os.system("echo " + cmake) - x = os.system("cd ../.. ;" + cmake) - assert x == 0 - x = os.system("cd ../.. ; make -j 2 -f Makefile") - assert x == 0 + subprocess.run(f"echo {cmake}", shell=True) + subprocess.run(f"cd ../../; {cmake}", shell=True, check=True) + subprocess.run("cd ../.. ; cmake --build build -j 2", shell=True, check=True) # readthedocs currently runs out of memory if we actually dare to try to do this From 6ccdca8fd2877dee1bbc04bd09b682f92fee9739 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 5 Jan 2026 17:23:41 +0000 Subject: [PATCH 133/461] Partial revert of d0cd5f0aee Seems to cause MPI problems? --- include/bout/physicsmodel.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index 81f17a39f0..e6d34daef9 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -432,12 +432,12 @@ private: auto bout_monitor = bout::utils::make_unique(); \ solver->addMonitor(bout_monitor.get(), Solver::BACK); \ solver->solve(); \ - BoutFinalise(); \ - return 0; \ } catch (const BoutException& e) { \ output.write("Error encountered: {}\n", e.what()); \ MPI_Abort(BoutComm::get(), 1); \ } \ + BoutFinalise(); \ + return 0; \ } /// Macro to replace solver->add, passing variable name From d8b360fee146fd8837330943a3fa20523b8f4974 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:19:22 +0000 Subject: [PATCH 134/461] Remove some more unused headers --- include/bout/deriv_store.hxx | 1 + include/bout/msg_stack.hxx | 6 ++---- src/field/field2d.cxx | 2 -- src/solver/impls/adams_bashforth/adams_bashforth.cxx | 3 --- src/solver/impls/euler/euler.cxx | 1 - src/sys/derivs.cxx | 1 - 6 files changed, 3 insertions(+), 11 deletions(-) diff --git a/include/bout/deriv_store.hxx b/include/bout/deriv_store.hxx index 0e03d6a32e..b2b7928c2e 100644 --- a/include/bout/deriv_store.hxx +++ b/include/bout/deriv_store.hxx @@ -29,6 +29,7 @@ #ifndef __DERIV_STORE_HXX__ #define __DERIV_STORE_HXX__ +#include #include #include #include diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 9e218e9efb..1abb26d2c7 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -31,11 +31,9 @@ class MsgStack; #include "bout/build_defines.hxx" -#include "bout/unused.hxx" - #include "fmt/core.h" -#include +#include #include #include #include @@ -82,7 +80,7 @@ public: } void pop() {} - void pop(int UNUSED(id)) {} + void pop(int [[maybe_unused]] id) {} void clear() {} void dump() {} diff --git a/src/field/field2d.cxx b/src/field/field2d.cxx index 01f2d8c4a6..a046078585 100644 --- a/src/field/field2d.cxx +++ b/src/field/field2d.cxx @@ -36,8 +36,6 @@ #include // for mesh #include #include -#include -#include #include diff --git a/src/solver/impls/adams_bashforth/adams_bashforth.cxx b/src/solver/impls/adams_bashforth/adams_bashforth.cxx index 72748fd52e..51fff4fe6a 100644 --- a/src/solver/impls/adams_bashforth/adams_bashforth.cxx +++ b/src/solver/impls/adams_bashforth/adams_bashforth.cxx @@ -5,9 +5,6 @@ #include #include #include -#include - -#include namespace { BoutReal lagrange_at_position_denominator(const std::deque& grid, diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 2a9e5813f2..a3911e78d6 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -5,7 +5,6 @@ #include #include #include -#include EulerSolver::EulerSolver(Options* options) : Solver(options), mxstep((*options)["mxstep"] diff --git a/src/sys/derivs.cxx b/src/sys/derivs.cxx index 13be388e97..aab75b8f19 100644 --- a/src/sys/derivs.cxx +++ b/src/sys/derivs.cxx @@ -45,7 +45,6 @@ #include #include #include -#include #include #include From 5306125a4a9bb834888e1949ddf8f1cbfd7d6bdf Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:19:34 +0000 Subject: [PATCH 135/461] tests: Add functions to anon namespace --- tests/integrated/test-backtrace/boutexcept.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/tests/integrated/test-backtrace/boutexcept.cxx b/tests/integrated/test-backtrace/boutexcept.cxx index 70bf480862..6017ecd8f9 100644 --- a/tests/integrated/test-backtrace/boutexcept.cxx +++ b/tests/integrated/test-backtrace/boutexcept.cxx @@ -1,8 +1,10 @@ #include "bout/boutexception.hxx" +namespace { void troublemaker() { throw BoutException("test"); } void f() { troublemaker(); } void e() { f(); } +} int main() { e(); From 68fab1b2e338c0631e15372b2f1255642d693807 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:19:45 +0000 Subject: [PATCH 136/461] Don't use `char[]` for constant --- src/bout++.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/bout++.cxx b/src/bout++.cxx index ba49a208be..d6888874c6 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -27,7 +27,7 @@ #include "bout/build_config.hxx" -const char DEFAULT_DIR[] = "data"; +static constexpr auto DEFAULT_DIR = "data"; #define GLOBALORIGIN From 563ab24f4a30ba18b85f9a25d28ab4f3a78f637d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:22:59 +0000 Subject: [PATCH 137/461] Fix use of reserved identifier --- include/bout/scorepwrapper.hxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bout/scorepwrapper.hxx b/include/bout/scorepwrapper.hxx index 40c2232040..8b37b2ff61 100644 --- a/include/bout/scorepwrapper.hxx +++ b/include/bout/scorepwrapper.hxx @@ -20,9 +20,9 @@ /// we need to say if we support this or not by defining BOUT_HAS_PRETTY_FUNCTION (to be /// implemented in configure) #if BOUT_HAS_PRETTY_FUNCTION -#define __thefunc__ __PRETTY_FUNCTION__ +#define _thefunc_ __PRETTY_FUNCTION__ #else -#define __thefunc__ __func__ +#define _thefunc_ __func__ #endif /// Instrument a function with scorep @@ -31,7 +31,7 @@ /// If we don't have scorep support then just define a null function #if BOUT_HAS_SCOREP #define SCOREP_BASE_CALL(...) \ - SCOREP_USER_REGION(__thefunc__, SCOREP_USER_REGION_TYPE_FUNCTION) + SCOREP_USER_REGION(_thefunc_, SCOREP_USER_REGION_TYPE_FUNCTION) #else #define SCOREP_BASE_CALL(...) #endif From 1a9210cbc6a846caaef63bc2cb562a65bff349e5 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 14:45:16 +0000 Subject: [PATCH 138/461] docs: Update example backtrace --- manual/sphinx/developer_docs/debugging.rst | 97 +++++++++++++--------- src/sys/boutexception.cxx | 2 +- src/sys/msg_stack.cxx | 2 +- 3 files changed, 58 insertions(+), 43 deletions(-) diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index fc8d80d9fc..4e8bd0feab 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -69,14 +69,15 @@ help find where an error occurred. For example, given this snippet:: we would see something like the following output: .. code:: text - - ====== Back trace ====== + + ====== Exception thrown ====== + Something went wrong + + === Additional information === -> 4. Inner-most scope on line 58 of '/path/to/model.cxx' -> 2. Middle scope on line 53 of '/path/to/model.cxx' -> 1. Outer-most scope on line 51 of '/path/to/model.cxx' - ====== Exception thrown ====== - Something went wrong The third ``TRACE`` message doesn't appear in the output because we've left its scope and it's no longer relevant. @@ -97,44 +98,58 @@ the ``fmt`` syntax also used by the loggers:: Backtrace ========= -Lastly, BOUT++ can also automatically print a backtrace in the event -of a crash. This is a compile-time option in the BOUT++ library -(``-DBOUT_ENABLE_BACKTRACE=ON``, the default, requires the -``addr2line`` program to be installed), and debug symbols to be turned -on (``-DCMAKE_BUILD_TYPE=Debug`` or ``=RelWithDebInfo``) in BOUT++ -_and_ the physics model. If debug symbols are only present in part, the -backtrace will be missing names for the other part. - -The output looks something like this: +Lastly, BOUT++ can also automatically print a backtrace in the event of a +crash. This is very useful to include if you ever need to report a bug to the +developers! The output looks something like this: .. code:: text ... - Error encountered - ====== Exception path ====== - [bt] #10 ./backtrace() [0x40a27e] - _start at /home/abuild/rpmbuild/BUILD/glibc-2.33/csu/../sysdeps/x86_64/start.S:122 - [bt] #9 /lib64/libc.so.6(__libc_start_main+0xd5) [0x7fecbfa28b25] - __libc_start_main at /usr/src/debug/glibc-2.33-4.1.x86_64/csu/../csu/libc-start.c:332 - [bt] #8 ./backtrace() [0x40a467] - main at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:32 (discriminator 9) - [bt] #7 /path/to/BOUT-dev/build/libbout++.so(_ZN6Solver8setModelEP12PhysicsModel+0xb5) [0x7fecc0ca2e93] - Solver::setModel(PhysicsModel*) at /path/to/BOUT-dev/build/../src/solver/solver.cxx:94 - [bt] #6 /path/to/BOUT-dev/build/libbout++.so(_ZN12PhysicsModel10initialiseEP6Solver+0xc0) [0x7fecc0cad594] - PhysicsModel::initialise(Solver*) at /path/to/BOUT-dev/build/../include/bout/physicsmodel.hxx:93 (discriminator 5) - [bt] #5 ./backtrace() [0x40a986] - Backtrace::init(bool) at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:27 - [bt] #4 ./backtrace() [0x40a3cf] - f3() at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:19 - [bt] #3 ./backtrace() [0x40a3be] - f2(int) at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:15 - [bt] #2 ./backtrace() [0x40a386] - f1() at /path/to/BOUT-dev/build/../examples/backtrace/backtrace.cxx:13 (discriminator 2) - [bt] #1 ./backtrace(_ZN13BoutExceptionC1IA19_cJEEERKT_DpRKT0_+0xba) [0x40ae16] - BoutException::BoutException(char const (&) [19]) at /path/to/BOUT-dev/build/../include/bout/../boutexception.hxx:28 (discriminator 2) - - -This output tends to be much harder to read than the message stack -from ``TRACE`` macros, but the advantage is that it doesn't require -any modifications to the code to use, and can give you more precise -location information. + Error encountered: Stack trace (most recent call first): + #0 (filtered) + #1 (filtered) + #2 (filtered) + #3 in BoutMesh::createCommunicators() + at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:709:64 + 707: } + 708: // Unconditional exception for demo purposes + > 709: throw BoutException("Single null outer SOL not correct\n"); + ^ + 710: MPI_Group_free(&group); + 711: } + #4 in BoutMesh::load() + at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:575:22 + 573: /// Communicator + 574: + > 575: createCommunicators(); + ^ + 576: output_debug << "Got communicators" << endl; + #5 in BoutInitialise(int&, char**&) + at BOUT-dev/src/bout++.cxx:201:28 + 199: bout::globals::mesh = Mesh::create(); + 200: // Load from sources. Required for Field initialisation + > 201: bout::globals::mesh->load(); + ^ + 202: + 203: // time_report options are used in BoutFinalise, i.e. after we + #6 in main + at BOUT-dev/examples/elm-pb/elm_pb.cxx:2161:1 + 2159: }; + 2160: + > 2161: BOUTMAIN(ELMpb); + ^ + #7 (filtered) + #8 (filtered) + #9 (filtered) + + ====== Exception thrown ====== + Single null outer SOL not correct + + +Debug symbols are required to get the filename/line number and code snippets. If +they are missing in either BOUT++ or the physics model, only the function name +and signature will be included for that part. + +Including debug symbols is a configure time ``CMake`` option, set either: +``-DCMAKE_BUILD_TYPE=Debug`` or ``-DCMAKE_BUILD_TYPE=RelWithDebInfo`` (the +default). diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index 931720d537..02b6ef3391 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -72,7 +72,7 @@ std::string BoutException::getBacktrace() const { formatter.format(generate_trace()), message); if (msg_stack.size() > 0) { - return fmt::format("{}\n\nAdditional information:\n{}", backtrace_message, + return fmt::format("{}\n\n{}", backtrace_message, msg_stack.getDump()); } return backtrace_message; diff --git a/src/sys/msg_stack.cxx b/src/sys/msg_stack.cxx index 81eb6c4b0f..3dbd7c2797 100644 --- a/src/sys/msg_stack.cxx +++ b/src/sys/msg_stack.cxx @@ -91,7 +91,7 @@ void MsgStack::dump() { } std::string MsgStack::getDump() { - std::string res = "====== Back trace ======\n"; + std::string res = "=== Additional information ===\n"; for (int i = position - 1; i >= 0; i--) { if (stack[i] != "") { res += " -> "; From 0285b949d124d9896f47c5e6b7ed570c084a9144 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 7 Jan 2026 16:38:45 +0000 Subject: [PATCH 139/461] Remove `Mesh::smoothSeparatrix` Closes #3239 --- examples/dalf3/dalf3.cxx | 8 ------- include/bout/mesh.hxx | 3 --- src/mesh/impls/bout/boutmesh.cxx | 41 -------------------------------- src/mesh/impls/bout/boutmesh.hxx | 2 -- 4 files changed, 54 deletions(-) diff --git a/examples/dalf3/dalf3.cxx b/examples/dalf3/dalf3.cxx index 46a9a4286d..b27ae97113 100644 --- a/examples/dalf3/dalf3.cxx +++ b/examples/dalf3/dalf3.cxx @@ -71,8 +71,6 @@ class DALF3 : public PhysicsModel { BoutReal viscosity, hyper_viscosity; - bool smooth_separatrix; - FieldGroup comms; std::unique_ptr phiSolver{nullptr}; // Laplacian solver in X-Z @@ -132,7 +130,6 @@ class DALF3 : public PhysicsModel { viscosity = options["viscosity"].withDefault(-1.0); hyper_viscosity = options["hyper_viscosity"].withDefault(-1.0); viscosity_par = options["viscosity_par"].withDefault(-1.0); - smooth_separatrix = options["smooth_separatrix"].withDefault(false); filter_z = options["filter_z"].withDefault(false); @@ -466,11 +463,6 @@ class DALF3 : public PhysicsModel { ddt(Pe) = -bracket(phi, Pet, bm) + Pet * (Kappa(phi - Pe) + B0 * Grad_parP(jpar - Vpar) / B0); - if (smooth_separatrix) { - // Experimental smoothing across separatrix - ddt(Vort) += mesh->smoothSeparatrix(Vort); - } - if (filter_z) { ddt(Pe) = filter(ddt(Pe), 1); } diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 2e27bed777..da8ac51127 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -505,9 +505,6 @@ public: virtual void addBoundaryPar(std::shared_ptr UNUSED(bndry), BoundaryParType UNUSED(type)) {} - /// Branch-cut special handling (experimental) - virtual Field3D smoothSeparatrix(const Field3D& f) { return f; } - virtual BoutReal GlobalX(int jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(int jy) const = 0; ///< Continuous Y index (0 -> 1) virtual BoutReal GlobalX(BoutReal jx) const = 0; ///< Continuous X index between 0 and 1 diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 8917fa3753..11ea2f5c89 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -3115,47 +3115,6 @@ void BoutMesh::addBoundaryPar(std::shared_ptr bndry, par_boundary[static_cast(BoundaryParType::all)].push_back(bndry); } -Field3D BoutMesh::smoothSeparatrix(const Field3D& f) { - Field3D result{emptyFrom(f)}; - if ((ixseps_inner > 0) && (ixseps_inner < nx - 1)) { - if (XPROC(ixseps_inner) == PE_XIND) { - int x = getLocalXIndex(ixseps_inner); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x - 1, y, z)); - } - } - } - if (XPROC(ixseps_inner - 1) == PE_XIND) { - int x = getLocalXIndex(ixseps_inner - 1); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x + 1, y, z)); - } - } - } - } - if ((ixseps_outer > 0) && (ixseps_outer < nx - 1) && (ixseps_outer != ixseps_inner)) { - if (XPROC(ixseps_outer) == PE_XIND) { - int x = getLocalXIndex(ixseps_outer); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x - 1, y, z)); - } - } - } - if (XPROC(ixseps_outer - 1) == PE_XIND) { - int x = getLocalXIndex(ixseps_outer - 1); - for (int y = 0; y < LocalNy; y++) { - for (int z = 0; z < LocalNz; z++) { - result(x, y, z) = 0.5 * (f(x, y, z) + f(x + 1, y, z)); - } - } - } - } - return result; -} - BoutReal BoutMesh::GlobalX(int jx) const { if (symmetricGlobalX) { // With this definition the boundary sits dx/2 away form the first/last inner points diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index a22e0e4473..876edab1da 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -167,8 +167,6 @@ public: BoundaryParType type) override; std::set getPossibleBoundaries() const override; - Field3D smoothSeparatrix(const Field3D& f) override; - int getNx() const { return nx; } int getNy() const { return ny; } From 0386f676712594aea5c4df308804b39139a5a884 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 7 Jan 2026 17:09:12 +0000 Subject: [PATCH 140/461] Use `zstart/zend` in more loops --- examples/elm-pb/elm_pb.cxx | 18 +-- .../iterator-offsets/iterator-offsets.cxx | 4 +- examples/performance/iterator/iterator.cxx | 4 +- include/bout/fv_ops.hxx | 2 +- manual/sphinx/developer_docs/data_types.rst | 6 +- manual/sphinx/user_docs/boundary_options.rst | 2 +- .../laplace/impls/naulin/naulin_laplace.cxx | 4 +- .../laplace/impls/petsc/petsc_laplace.cxx | 40 +++--- src/invert/laplace/impls/spt/spt.cxx | 12 +- .../laplacexz/impls/petsc/laplacexz-petsc.cxx | 50 +++---- src/mesh/boundary_standard.cxx | 136 +++++++++--------- src/mesh/coordinates.cxx | 6 +- src/mesh/difops.cxx | 4 +- src/mesh/fv_ops.cxx | 16 +-- src/solver/impls/imex-bdf2/imex-bdf2.cxx | 20 +-- src/solver/impls/petsc/petsc.cxx | 6 +- src/solver/impls/snes/snes.cxx | 10 +- tests/integrated/test-invpar/test_invpar.cxx | 2 +- .../test_multigrid_laplace.cxx | 10 +- .../test_naulin_laplace.cxx | 10 +- .../test-petsc_laplace/test_petsc_laplace.cxx | 54 +++---- .../test_petsc_laplace_MAST_grid.cxx | 66 ++++----- 22 files changed, 241 insertions(+), 241 deletions(-) diff --git a/examples/elm-pb/elm_pb.cxx b/examples/elm-pb/elm_pb.cxx index fe18a756ae..57f0ec0778 100644 --- a/examples/elm-pb/elm_pb.cxx +++ b/examples/elm-pb/elm_pb.cxx @@ -1366,7 +1366,7 @@ class ELMpb : public PhysicsModel { // Calculate a single phi boundary value for all Y slices BoutReal philocal = 0.0; for (int j = mesh->ystart; j <= mesh->yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { philocal += phi(mesh->xstart, j, k); } } @@ -1379,7 +1379,7 @@ class ELMpb : public PhysicsModel { for (int j = mesh->ystart; j <= mesh->yend; j++) { if (!phi_core_averagey) { phivalue = 0.0; // Calculate phi boundary for each Y index separately - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { phivalue += phi(mesh->xstart, j, k); } phivalue /= mesh->LocalNz; // Average in Z of point next to boundary @@ -1393,7 +1393,7 @@ class ELMpb : public PhysicsModel { BoutReal const newvalue = weight * oldvalue + (1. - weight) * phivalue; // Set phi at the boundary to this value - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { phi(mesh->xstart - 1, j, k) = 2. * newvalue - phi(mesh->xstart, j, k); phi(mesh->xstart - 2, j, k) = phi(mesh->xstart - 1, j, k); } @@ -1403,7 +1403,7 @@ class ELMpb : public PhysicsModel { if (mesh->lastX()) { for (int j = mesh->ystart; j <= mesh->yend; j++) { BoutReal phivalue = 0.0; - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { phivalue += phi(mesh->xend, j, k); } phivalue /= mesh->LocalNz; // Average in Z of point next to boundary @@ -1416,7 +1416,7 @@ class ELMpb : public PhysicsModel { BoutReal const newvalue = weight * oldvalue + (1. - weight) * phivalue; // Set phi at the boundary to this value - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { phi(mesh->xend + 1, j, k) = 2. * newvalue - phi(mesh->xend, j, k); phi(mesh->xend + 2, j, k) = phi(mesh->xend + 1, j, k); } @@ -1441,7 +1441,7 @@ class ELMpb : public PhysicsModel { if (mesh->firstX()) { for (int j = mesh->ystart; j <= mesh->yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Average phi + Pi at the boundary, and set the boundary cell // to this value. The phi solver will then put the value back // onto the cell mid-point @@ -1453,7 +1453,7 @@ class ELMpb : public PhysicsModel { if (mesh->lastX()) { for (int j = mesh->ystart; j <= mesh->yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { phi_shift(mesh->xend + 1, j, k) = 0.5 * (phi_shift(mesh->xend + 1, j, k) + phi_shift(mesh->xend, j, k)); } @@ -1513,7 +1513,7 @@ class ELMpb : public PhysicsModel { if (mesh->firstX()) { for (int i = mesh->xstart - 2; i >= 0; --i) { for (int j = mesh->ystart; j <= mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { phi(i, j, k) = phi(i + 1, j, k); } } @@ -1523,7 +1523,7 @@ class ELMpb : public PhysicsModel { if (mesh->lastX()) { for (int i = mesh->xend + 2; i < mesh->LocalNx; ++i) { for (int j = mesh->ystart; j <= mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { phi(i, j, k) = phi(i - 1, j, k); } } diff --git a/examples/performance/iterator-offsets/iterator-offsets.cxx b/examples/performance/iterator-offsets/iterator-offsets.cxx index 455df38ea5..48012f1793 100644 --- a/examples/performance/iterator-offsets/iterator-offsets.cxx +++ b/examples/performance/iterator-offsets/iterator-offsets.cxx @@ -65,7 +65,7 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK( "Nested loop", for (int i = 0; i < mesh->LocalNx; ++i) { for (int j = mesh->ystart; j < mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = (a(i, j + 1, k) - a(i, j - 1, k)); } } @@ -76,7 +76,7 @@ int main(int argc, char** argv) { BOUT_OMP_PERF(parallel for) for(int i=0;iLocalNx;++i) { for (int j = mesh->ystart; j < mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = (a(i, j + 1, k) - a(i, j - 1, k)); } } diff --git a/examples/performance/iterator/iterator.cxx b/examples/performance/iterator/iterator.cxx index 7a29b00298..e1fc59d067 100644 --- a/examples/performance/iterator/iterator.cxx +++ b/examples/performance/iterator/iterator.cxx @@ -77,7 +77,7 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK( "Nested loop", for (int i = 0; i < mesh->LocalNx; ++i) { for (int j = 0; j < mesh->LocalNy; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = a(i, j, k) + b(i, j, k); } } @@ -88,7 +88,7 @@ int main(int argc, char** argv) { BOUT_OMP_PERF(parallel for) for(int i=0;iLocalNx;++i) { for (int j = 0; j < mesh->LocalNy; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { result(i, j, k) = a(i, j, k) + b(i, j, k); } } diff --git a/include/bout/fv_ops.hxx b/include/bout/fv_ops.hxx index 94007a57a2..df61c6aa24 100644 --- a/include/bout/fv_ops.hxx +++ b/include/bout/fv_ops.hxx @@ -258,7 +258,7 @@ const Field3D Div_par(const Field3D& f_in, const Field3D& v_in, BoutReal flux_factor_lm = common_factor / (coord->dy(i, j - 1) * coord->J(i, j - 1)); #endif - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { #if BOUT_USE_METRIC_3D // For right cell boundaries BoutReal common_factor = diff --git a/manual/sphinx/developer_docs/data_types.rst b/manual/sphinx/developer_docs/data_types.rst index fa8e9e6ea6..f9411dcb39 100644 --- a/manual/sphinx/developer_docs/data_types.rst +++ b/manual/sphinx/developer_docs/data_types.rst @@ -255,9 +255,9 @@ parallelise and vectorise. Some tuning of this is possible, see below for details. It replaces the C-style triple-nested loop:: Field3D f(0.0); - for (int i = mesh->xstart; i < mesh->xend; ++i) { - for (int j = mesh->ystart; j < mesh->yend; ++j) { - for (int k = 0; k < mesh->LocalNz; ++k) { + for (int i = mesh->xstart; i <= mesh->xend; ++i) { + for (int j = mesh->ystart; j <= mesh->yend; ++j) { + for (int k = mesh->zstart; k <= mesh->zend; ++k) { f(i,j,k) = a(i,j,k) + b(i,j,k) } } diff --git a/manual/sphinx/user_docs/boundary_options.rst b/manual/sphinx/user_docs/boundary_options.rst index a3cdf0078b..8493049516 100644 --- a/manual/sphinx/user_docs/boundary_options.rst +++ b/manual/sphinx/user_docs/boundary_options.rst @@ -575,7 +575,7 @@ and implemented in ``boundary_standard.cxx`` void BoundaryNeumann::apply(Field3D &f) { for(bndry->first(); !bndry->isDone(); bndry->next()) - for(int z=0;zLocalNz;z++) + for(int z= mesh->zstart; z <= mesh->zend;z++) f[bndry->x][bndry->y][z] = f[bndry->x - bndry->bx][bndry->y - bndry->by][z]; } diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.cxx b/src/invert/laplace/impls/naulin/naulin_laplace.cxx index e6f68d850d..f61dc93908 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.cxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.cxx @@ -402,7 +402,7 @@ void LaplaceNaulin::copy_x_boundaries(Field3D& x, const Field3D& x0, Mesh* local if (localmesh->firstX()) { for (int i = localmesh->xstart - 1; i >= 0; --i) { for (int j = localmesh->ystart; j <= localmesh->yend; ++j) { - for (int k = 0; k < localmesh->LocalNz; ++k) { + for (int k = localmesh->zstart; k <= localmesh->zend; ++k) { x(i, j, k) = x0(i, j, k); } } @@ -411,7 +411,7 @@ void LaplaceNaulin::copy_x_boundaries(Field3D& x, const Field3D& x0, Mesh* local if (localmesh->lastX()) { for (int i = localmesh->xend + 1; i < localmesh->LocalNx; ++i) { for (int j = localmesh->ystart; j <= localmesh->yend; ++j) { - for (int k = 0; k < localmesh->LocalNz; ++k) { + for (int k = localmesh->zstart; k <= localmesh->zend; ++k) { x(i, j, k) = x0(i, j, k); } } diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 5f417d4faa..4170c88e80 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -145,7 +145,7 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, if (fourth_order) { // first and last 2*localmesh-LocalNz entries are the edge x-values that (may) have 'off-diagonal' components (i.e. on another processor) if (localmesh->firstX() && localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 15; d_nnz[localN - 1 - i] = 15; o_nnz[i] = 0; @@ -158,7 +158,7 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, o_nnz[localN - 1 - i] = 0; } } else if (localmesh->firstX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 15; d_nnz[localN - 1 - i] = 15; o_nnz[i] = 0; @@ -171,7 +171,7 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, o_nnz[localN - 1 - i] = 5; } } else if (localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 15; d_nnz[localN - 1 - i] = 15; o_nnz[i] = 10; @@ -184,7 +184,7 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, o_nnz[localN - 1 - i] = 0; } } else { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 15; d_nnz[localN - 1 - i] = 15; o_nnz[i] = 10; @@ -215,28 +215,28 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, } else { // first and last localmesh->LocalNz entries are the edge x-values that (may) have 'off-diagonal' components (i.e. on another processor) if (localmesh->firstX() && localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 6; d_nnz[localN - 1 - i] = 6; o_nnz[i] = 0; o_nnz[localN - 1 - i] = 0; } } else if (localmesh->firstX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 6; d_nnz[localN - 1 - i] = 6; o_nnz[i] = 0; o_nnz[localN - 1 - i] = 3; } } else if (localmesh->lastX()) { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 6; d_nnz[localN - 1 - i] = 6; o_nnz[i] = 3; o_nnz[localN - 1 - i] = 0; } } else { - for (int i = 0; i < localmesh->LocalNz; i++) { + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { d_nnz[i] = 6; d_nnz[localN - 1 - i] = 6; o_nnz[i] = 3; @@ -377,7 +377,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the values for the inner boundary region if (localmesh->firstX()) { for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; // Value of element to be set in the matrix // If Neumann Boundary Conditions are set. if (isInnerBoundaryFlagSet(INVERT_AC_GRAD)) { @@ -461,7 +461,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the values for the main domain for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // NOTE: Only A0 is the A from setCoefA () BoutReal A0, A1, A2, A3, A4, A5; A0 = A(x, y, z); @@ -639,7 +639,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the values for the outer boundary region if (localmesh->lastX()) { for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Set Diagonal Values to 1 PetscScalar val = 1; Element(i, x, z, 0, 0, val, MatA); @@ -829,7 +829,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the inner boundary values if (localmesh->firstX()) { for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 0; VecGetValues(xs, 1, &i, &val); sol[x][z] = val; @@ -840,7 +840,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the main domain values for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 0; VecGetValues(xs, 1, &i, &val); sol[x][z] = val; @@ -851,7 +851,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the outer boundary values if (localmesh->lastX()) { for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 0; VecGetValues(xs, 1, &i, &val); sol[x][z] = val; @@ -1076,7 +1076,7 @@ void LaplacePetsc::vecToField(Vec xs, FieldPerp& f) { int i = Istart; if (localmesh->firstX()) { for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &i, &val); f[x][z] = val; @@ -1086,7 +1086,7 @@ void LaplacePetsc::vecToField(Vec xs, FieldPerp& f) { } for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &i, &val); f[x][z] = val; @@ -1096,7 +1096,7 @@ void LaplacePetsc::vecToField(Vec xs, FieldPerp& f) { if (localmesh->lastX()) { for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &i, &val); f[x][z] = val; @@ -1113,7 +1113,7 @@ void LaplacePetsc::fieldToVec(const FieldPerp& f, Vec bs) { int i = Istart; if (localmesh->firstX()) { for (int x = 0; x < localmesh->xstart; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = f[x][z]; VecSetValues(bs, 1, &i, &val, INSERT_VALUES); i++; // Increment row in Petsc matrix @@ -1122,7 +1122,7 @@ void LaplacePetsc::fieldToVec(const FieldPerp& f, Vec bs) { } for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = f[x][z]; VecSetValues(bs, 1, &i, &val, INSERT_VALUES); i++; // Increment row in Petsc matrix @@ -1131,7 +1131,7 @@ void LaplacePetsc::fieldToVec(const FieldPerp& f, Vec bs) { if (localmesh->lastX()) { for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = f[x][z]; VecSetValues(bs, 1, &i, &val, INSERT_VALUES); i++; // Increment row in Petsc matrix diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index 2e4c844c94..e39ca7e89f 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -95,7 +95,7 @@ FieldPerp LaplaceSPT::solve(const FieldPerp& b, const FieldPerp& x0) { if (isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) { // Copy x0 inner boundary into bs for (int ix = 0; ix < xbndry; ix++) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs[ix][iz] = x0[ix][iz]; } } @@ -103,7 +103,7 @@ FieldPerp LaplaceSPT::solve(const FieldPerp& b, const FieldPerp& x0) { if (isOuterBoundaryFlagSetOnLastX(INVERT_SET)) { // Copy x0 outer boundary into bs for (int ix = localmesh->LocalNx - 1; ix >= localmesh->LocalNx - xbndry; ix--) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs[ix][iz] = x0[ix][iz]; } } @@ -181,7 +181,7 @@ Field3D LaplaceSPT::solve(const Field3D& b, const Field3D& x0) { // Copy x0 inner boundary into bs for (int ix = 0; ix < xbndry; ix++) { for (int iy = 0; iy < localmesh->LocalNy; iy++) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs(ix, iy, iz) = x0(ix, iy, iz); } } @@ -191,7 +191,7 @@ Field3D LaplaceSPT::solve(const Field3D& b, const Field3D& x0) { // Copy x0 outer boundary into bs for (int ix = localmesh->LocalNx - 1; ix >= localmesh->LocalNx - xbndry; ix--) { for (int iy = 0; iy < localmesh->LocalNy; iy++) { - for (int iz = 0; iz < localmesh->LocalNz; iz++) { + for (int iz = localmesh->zstart; iz <= localmesh->zend; iz++) { bs(ix, iy, iz) = x0(ix, iy, iz); } } @@ -519,7 +519,7 @@ void LaplaceSPT::finish(SPT_data& data, FieldPerp& x) { if (!localmesh->firstX()) { // Set left boundary to zero (Prevent unassigned values in corners) for (int ix = 0; ix < localmesh->xstart; ix++) { - for (int kz = 0; kz < localmesh->LocalNz; kz++) { + for (int kz = localmesh->zstart; kz <= localmesh->zend; kz++) { x(ix, kz) = 0.0; } } @@ -527,7 +527,7 @@ void LaplaceSPT::finish(SPT_data& data, FieldPerp& x) { if (!localmesh->lastX()) { // Same for right boundary for (int ix = localmesh->xend + 1; ix < localmesh->LocalNx; ix++) { - for (int kz = 0; kz < localmesh->LocalNz; kz++) { + for (int kz = localmesh->zstart; kz <= localmesh->zend; kz++) { x(ix, kz) = 0.0; } } diff --git a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx index 769e797352..c61db141b8 100644 --- a/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx +++ b/src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx @@ -202,25 +202,25 @@ LaplaceXZpetsc::LaplaceXZpetsc(Mesh* m, Options* opt, const CELL_LOC loc) // X boundaries if (localmesh->firstX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { d_nnz[z] = 2; } } else { // One point on another processor - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { d_nnz[z] -= 1; o_nnz[z] += 1; } } if (localmesh->lastX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { int ind = localN - (localmesh->LocalNz) + z; d_nnz[ind] = 2; } } else { // One point on another processor - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { int ind = localN - (localmesh->LocalNz) + z; d_nnz[ind] -= 1; o_nnz[ind] += 1; @@ -335,7 +335,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { /* NOTE: Sign of the elements are opposite of what one might expect, * see note about BC in LaplaceXZ constructor for more details */ - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -347,7 +347,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (inner_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -359,7 +359,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (inner_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -374,7 +374,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { /* NOTE: Sign of the elements are opposite of what one might expect, * see note about BC in LaplaceXZ constructor for more details */ - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 0.5; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -393,7 +393,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { Coordinates* coords = localmesh->getCoordinates(location); for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // stencil entries PetscScalar c, xm, xp, zm, zp; // Diagonal entries @@ -655,7 +655,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { if (localmesh->lastX()) { if (outer_boundary_flags & INVERT_AC_GRAD) { // Neumann 0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -667,7 +667,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (outer_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -679,7 +679,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { } } else if (outer_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = 1.0; MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); @@ -693,7 +693,7 @@ void LaplaceXZpetsc::setCoefs(const Field3D& Ain, const Field3D& Bin) { //Default: Dirichlet on outer X boundary PetscScalar val = 0.5; - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { MatSetValues(it.MatA, 1, &row, 1, &row, &val, INSERT_VALUES); int col = row - (localmesh->LocalNz); // -1 in X @@ -804,7 +804,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { if (localmesh->firstX()) { if (inner_boundary_flags & INVERT_AC_GRAD) { // Neumann 0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -816,7 +816,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (inner_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -828,7 +828,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (inner_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -840,7 +840,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else { // Default: Neumann on inner x boundary - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xstart - 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -855,7 +855,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { // Set the inner points for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val = x0(x, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -869,7 +869,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { if (localmesh->lastX()) { if (outer_boundary_flags & INVERT_AC_GRAD) { // Neumann 0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -882,7 +882,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (outer_boundary_flags & INVERT_SET) { // Setting BC from x0 - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -895,7 +895,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else if (outer_boundary_flags & INVERT_RHS) { // Setting BC from b - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -908,7 +908,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } } else { //Default: Dirichlet on outer X boundary - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { // Setting the initial guess x0 PetscScalar val = x0(localmesh->xend + 1, y, z); VecSetValues(xs, 1, &ind, &val, INSERT_VALUES); @@ -952,7 +952,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { ind = Istart; // Inner X boundary if (localmesh->firstX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &ind, &val); for (int x = localmesh->xstart - 1; x >= 0; --x) { @@ -963,7 +963,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { } for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &ind, &val); result(x, y, z) = val; @@ -973,7 +973,7 @@ Field3D LaplaceXZpetsc::solve(const Field3D& bin, const Field3D& x0in) { // Outer X boundary if (localmesh->lastX()) { - for (int z = 0; z < localmesh->LocalNz; z++) { + for (int z = localmesh->zstart; z <= localmesh->zend; z++) { PetscScalar val; VecGetValues(xs, 1, &ind, &val); for (int x = localmesh->xend + 1; x < localmesh->LocalNx; ++x) { diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index 8f2c7df5ec..c3fd5f8f2e 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -325,7 +325,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -346,7 +346,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->bx < 0) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -368,7 +368,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->by != 0) { // y boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -394,7 +394,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // Upper y boundary boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -415,7 +415,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->by < 0) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -436,7 +436,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { if (bndry->bx != 0) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -469,7 +469,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { * (mesh->GlobalY(bndry->y) // In the guard cell + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate( Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); @@ -513,7 +513,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { const int yi = bndry->y + (i * bndry->by); const auto xnorm = mesh->GlobalX(xi); const auto ynorm = mesh->GlobalY(yi) * TWOPI; - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate( Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); @@ -528,7 +528,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { } else { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -572,7 +572,7 @@ void BoundaryDirichlet::apply(Field3D& f, BoutReal t) { // Set any other guard cells using the values on the cells int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -596,7 +596,7 @@ void BoundaryDirichlet::apply_ddt(Field3D& f) { Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -827,7 +827,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -848,7 +848,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { if (bndry->bx < 0) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -870,7 +870,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { // y boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -898,7 +898,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { // Upper y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -919,7 +919,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { if (bndry->by < 0) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -940,7 +940,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { if (bndry->bx != 0) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -974,7 +974,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { * (mesh->GlobalY(bndry->y) // In the guard cell + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate( Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); @@ -1000,7 +1000,7 @@ void BoundaryDirichlet_O3::apply(Field3D& f, BoutReal t) { } else { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1038,7 +1038,7 @@ void BoundaryDirichlet_O3::apply_ddt(Field3D& f) { bndry->first(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -1284,7 +1284,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1306,7 +1306,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { if (bndry->bx < 0) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1329,7 +1329,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // y boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1358,7 +1358,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // Outer y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1381,7 +1381,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { if (bndry->by < 0) { // Inner y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1405,7 +1405,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1440,7 +1440,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { * (mesh->GlobalY(bndry->y) // In the guard cell + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate( Context(bndry, zk, loc, t, mesh).set("x", xnorm, "y", ynorm)); @@ -1468,7 +1468,7 @@ void BoundaryDirichlet_O4::apply(Field3D& f, BoutReal t) { } else { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); } @@ -1505,7 +1505,7 @@ void BoundaryDirichlet_O4::apply_ddt(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -1547,7 +1547,7 @@ void BoundaryDirichlet_4thOrder::apply(Field3D& f) { // Set (at 4th order) the value at the mid-point between the guard cell and the grid // cell to be val for (bndry->first(); !bndry->isDone(); bndry->next1d()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = 128. / 35. * val - 4. * f(bndry->x - bndry->bx, bndry->y - bndry->by, z) + 2. * f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, z) @@ -1574,7 +1574,7 @@ void BoundaryDirichlet_4thOrder::apply_ddt(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -1661,7 +1661,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Loop over all elements and set equal to the next point in for (bndry->first(); !bndry->isDone(); bndry->next1d()) { #if BOUT_USE_METRIC_3D - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { #else int z = 0; #endif @@ -1680,7 +1680,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // because derivative values don't exist in boundary region // NOTE: should be fixed to interpolate to boundary line #if not(BOUT_USE_METRIC_3D) - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { #endif BoutReal xshift = g12shift * dfdy(bndry->x - bndry->bx, bndry->y, z) + g13shift * dfdz(bndry->x - bndry->bx, bndry->y, z); @@ -1968,7 +1968,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->bx > 0) { // Outer x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dx(bndry->x, bndry->y, zk); @@ -1997,7 +1997,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->bx < 0) { // Inner x boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dx(bndry->x - bndry->bx, bndry->y, zk); @@ -2029,7 +2029,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + bndry->by * metric->dy(bndry->x, bndry->y); #endif - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { #if BOUT_USE_METRIC_3D BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); @@ -2053,7 +2053,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by > 0) { // Outer y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dy(bndry->x, bndry->y, zk); @@ -2081,7 +2081,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by < 0) { // Inner y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)) * metric->dy(bndry->x, bndry->y - bndry->by, zk); @@ -2115,7 +2115,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); #endif - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { #if BOUT_USE_METRIC_3D BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); @@ -2148,7 +2148,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { * (mesh->GlobalY(bndry->y) // In the guard cell + mesh->GlobalY(bndry->y - bndry->by)); // the grid cell - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); if (fg) { @@ -2170,13 +2170,13 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } else { for (; !bndry->isDone(); bndry->next1d()) { #if BOUT_USE_METRIC_3D - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y, zk) + bndry->by * metric->dy(bndry->x, bndry->y, zk); #else BoutReal delta = bndry->bx * metric->dx(bndry->x, bndry->y) + bndry->by * metric->dy(bndry->x, bndry->y); - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { #endif if (fg) { val = fg->generate(Context(bndry, zk, loc, t, mesh)); @@ -2205,7 +2205,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -2303,7 +2303,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { } else { Coordinates* coords = f.getCoordinates(); for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { BoutReal delta = bndry->bx * coords->dx(bndry->x, bndry->y, zk) + bndry->by * coords->dy(bndry->x, bndry->y, zk); if (fg) { @@ -2339,7 +2339,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -2398,7 +2398,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // grid cell to be val This sets the value of the co-ordinate derivative, i.e. DDX/DDY // not Grad_par/Grad_perp.x for (bndry->first(); !bndry->isDone(); bndry->next1d()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { BoutReal delta = -(bndry->bx * metric->dx(bndry->x, bndry->y, z) + bndry->by * metric->dy(bndry->x, bndry->y, z)); f(bndry->x, bndry->y, z) = @@ -2431,7 +2431,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -2469,7 +2469,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Coordinates* metric = f.getCoordinates(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = f(bndry->x - bndry->bx, bndry->y - bndry->by, z) * sqrt(metric->g_22(bndry->x, bndry->y, z) @@ -2536,7 +2536,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); if (fabs(bval) < 1.e-12) { for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = gval / aval; } } @@ -2546,7 +2546,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { sign = -1.; } for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = f(bndry->x - bndry->bx, bndry->y - bndry->by, z) + sign * (gval - aval * f(bndry->x - bndry->bx, bndry->y - bndry->by, z)) @@ -2570,7 +2570,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(bndry->x, bndry->y, z) = 2. * f(bndry->x - bndry->bx, bndry->y - bndry->by, z) - f(bndry->x - 2 * bndry->bx, bndry->y - 2 * bndry->by, z); @@ -3191,7 +3191,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3205,7 +3205,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3220,7 +3220,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3236,7 +3236,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by > 0) { // Upper y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3250,7 +3250,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3264,7 +3264,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3279,7 +3279,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3303,7 +3303,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -3453,7 +3453,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3468,7 +3468,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Inner x boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3484,7 +3484,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3501,7 +3501,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { if (bndry->by > 0) { // Upper y boundary for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3516,7 +3516,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Lower y boundary. Set one point inwards for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = -1; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3531,7 +3531,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // x boundaries for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3547,7 +3547,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { // Standard (non-staggered) case for (; !bndry->isDone(); bndry->next1d()) { - for (int zk = 0; zk < mesh->LocalNz; zk++) { + for (int zk = mesh->zstart; zk <= mesh->zend; zk++) { for (int i = 0; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; @@ -3572,7 +3572,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { ASSERT1(mesh == f.getMesh()); Field3D* dt = f.timeDeriv(); for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { (*dt)(bndry->x, bndry->y, z) = 0.; // Set time derivative to zero } } @@ -3632,7 +3632,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { op->apply(g); // Set time-derivatives for (bndry->first(); !bndry->isDone(); bndry->next()) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { ddt(f)(bndry->x, bndry->y, z) = r * (g(bndry->x, bndry->y, z) - f(bndry->x, bndry->y, z)); } diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 3dfee6a553..986707a79e 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -225,7 +225,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, ASSERT1(bndry->bx == 0 or localmesh->xstart > 1); ASSERT1(bndry->by == 0 or localmesh->ystart > 1); // note that either bx or by is >0 here - for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + for (int zi = localmesh->zstart; zi <= localmesh->zend; ++zi) { result(bndry->x, bndry->y, zi) = (9. * (f(bndry->x - bndry->bx, bndry->y - bndry->by, zi) @@ -256,7 +256,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, for (int i = extrap_start; i < bndry->width; i++) { int xi = bndry->x + i * bndry->bx; int yi = bndry->y + i * bndry->by; - for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + for (int zi = localmesh->zstart; zi <= localmesh->zend; ++zi) { result(xi, yi, zi) = 3.0 * result(xi - bndry->bx, yi - bndry->by, zi) - 3.0 * result(xi - 2 * bndry->bx, yi - 2 * bndry->by, zi) @@ -266,7 +266,7 @@ Field3D interpolateAndExtrapolate(const Field3D& f_, CELL_LOC location, } else { // not enough grid points to extrapolate, set equal to last grid point for (int i = extrap_start; i < bndry->width; i++) { - for (int zi = 0; zi < localmesh->LocalNz; ++zi) { + for (int zi = localmesh->zstart; zi <= localmesh->zend; ++zi) { result(bndry->x + i * bndry->bx, bndry->y + i * bndry->by, zi) = result(bndry->x - bndry->bx, bndry->y - bndry->by, zi); } diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 42fa4d6ca5..64b072e5ec 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -779,7 +779,7 @@ Field3D bracket(const Field3D& f, const Field2D& g, BRACKET_METHOD method, for (int jy = mesh->ystart; jy <= mesh->yend; jy++) { const BoutReal partialFactor = 1.0 / (12 * metric->dz(jx, jy)); const BoutReal spacingFactor = partialFactor / metric->dx(jx, jy); - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { const int jzp = jz + 1 < ncz ? jz + 1 : 0; // Above is alternative to const int jzp = (jz + 1) % ncz; const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; @@ -904,7 +904,7 @@ Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, int ncz = mesh->LocalNz; for (int y = mesh->ystart; y <= mesh->yend; y++) { for (int x = 1; x <= mesh->LocalNx - 2; x++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int zm = (z - 1 + ncz) % ncz; int zp = (z + 1) % ncz; diff --git a/src/mesh/fv_ops.cxx b/src/mesh/fv_ops.cxx index fe5422b4d1..723e1d45da 100644 --- a/src/mesh/fv_ops.cxx +++ b/src/mesh/fv_ops.cxx @@ -48,7 +48,7 @@ Field3D Div_a_Grad_perp(const Field3D& a, const Field3D& f) { for (int i = xs; i <= xe; i++) { for (int j = mesh->ystart; j <= mesh->yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Calculate flux from i to i+1 BoutReal fout = 0.5 * (a(i, j, k) + a(i + 1, j, k)) @@ -282,7 +282,7 @@ const Field3D D4DY4(const Field3D& d_in, const Field3D& f_in) { mesh->yend; for (int j = ystart; j <= yend; j++) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { BoutReal dy3 = SQ(coord->dy(i, j, k)) * coord->dy(i, j, k); // 3rd derivative at upper boundary @@ -329,7 +329,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { if (j != mesh->yend || !has_upper_boundary) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Right boundary common factors const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) @@ -353,7 +353,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { // At a domain boundary // Use a one-sided difference formula - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { // Right boundary common factors const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) @@ -383,7 +383,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { // Calculate the fluxes if (j != mesh->ystart || !has_lower_boundary) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) * (coord->J(i, j, k) + coord->J(i, j - 1, k)); @@ -402,7 +402,7 @@ const Field3D D4DY4_Index(const Field3D& f_in, bool bndry_flux) { } } else { // On a domain (Y) boundary - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { const BoutReal common_factor = 0.25 * (coord->dy(i, j, k) + coord->dy(i, j + 1, k)) * (coord->J(i, j, k) + coord->J(i, j - 1, k)); @@ -463,7 +463,7 @@ void communicateFluxes(Field3D& f) { mesh->wait(xin); // Add to cells for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(2, y, z) += f(0, y, z); } } @@ -472,7 +472,7 @@ void communicateFluxes(Field3D& f) { mesh->wait(xout); // Add to cells for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { f(mesh->LocalNx - 3, y, z) += f(mesh->LocalNx - 1, y, z); } } diff --git a/src/solver/impls/imex-bdf2/imex-bdf2.cxx b/src/solver/impls/imex-bdf2/imex-bdf2.cxx index 07188110ab..5ad70a9b8b 100644 --- a/src/solver/impls/imex-bdf2/imex-bdf2.cxx +++ b/src/solver/impls/imex-bdf2/imex-bdf2.cxx @@ -331,7 +331,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { if (mesh->firstX()) { // Lower X boundary for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xstart, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -350,7 +350,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } else { // On another processor for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xstart, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -373,7 +373,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { if (mesh->lastX()) { // Upper X boundary for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xend, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -392,7 +392,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } else { // On another processor for (int y = mesh->ystart; y <= mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int localIndex = ROUND(index(mesh->xend, y, z)); ASSERT2((localIndex >= 0) && (localIndex < localN)); if (z == 0) { @@ -559,7 +559,7 @@ void IMEXBDF2::constructSNES(SNES* snesIn) { } // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int ind = ROUND(index(x, y, z)); @@ -1420,7 +1420,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { if (mesh->firstX() && !mesh->periodicX) { for (int jx = 0; jx < mesh->xstart; ++jx) { for (int jy = mesh->ystart; jy <= mesh->yend; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(jx, jy, jz, u); ++u; } @@ -1432,7 +1432,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { if (mesh->lastX() && !mesh->periodicX) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; ++jx) { for (int jy = mesh->ystart; jy <= mesh->yend; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(jx, jy, jz, u); ++u; } @@ -1442,7 +1442,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { // Lower Y for (RangeIterator xi = mesh->iterateBndryLowerY(); !xi.isDone(); ++xi) { for (int jy = 0; jy < mesh->ystart; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(*xi, jy, jz, u); ++u; } @@ -1452,7 +1452,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { // Upper Y for (RangeIterator xi = mesh->iterateBndryUpperY(); !xi.isDone(); ++xi) { for (int jy = mesh->yend + 1; jy < mesh->LocalNy; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(*xi, jy, jz, u); ++u; } @@ -1463,7 +1463,7 @@ void IMEXBDF2::loopVars(BoutReal* u) { // Bulk of points for (int jx = mesh->xstart; jx <= mesh->xend; ++jx) { for (int jy = mesh->ystart; jy <= mesh->yend; ++jy) { - for (int jz = 0; jz < mesh->LocalNz; ++jz) { + for (int jz = mesh->zstart; jz <= mesh->zend; ++jz) { op.run(jx, jy, jz, u); ++u; } diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index d0535f069d..81f1cc7951 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -660,7 +660,7 @@ int PetscSolver::init() { } } // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { const int ind = ROUND(index(x, y, z)) - Istart; for (int i = 0; i < n3d; i++) { @@ -768,7 +768,7 @@ int PetscSolver::init() { } } // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int const ind = ROUND(index(x, y, z)); for (int i = 0; i < n3d; i++) { @@ -792,7 +792,7 @@ int PetscSolver::init() { || (yi >= mesh->LocalNy)) { continue; } - for (int zi = 0; zi < mesh->LocalNz; ++zi) { + for (int zi = mesh->zstart; zi <= mesh->zend; ++zi) { int ind2 = ROUND(index(xi, yi, zi)); if (ind2 < 0) { continue; // Boundary point diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 311bc371fe..446903dd4f 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -200,7 +200,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { } } // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { const int ind = ROUND(index(x, y, z)) - Istart; for (int i = 0; i < n3d; i++) { @@ -305,7 +305,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { } } // 3D fields - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { int ind = ROUND(index(x, y, z)); for (int i = 0; i < n3d; i++) { @@ -329,7 +329,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { || (yi >= mesh->LocalNy)) { continue; } - for (int zi = 0; zi < mesh->LocalNz; ++zi) { + for (int zi = mesh->zstart; zi <= mesh->zend; ++zi) { int ind2 = ROUND(index(xi, yi, zi)); if (ind2 < 0) { continue; // Boundary point @@ -1331,7 +1331,7 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { } // Field3D quantities evolved together within a cell - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { count = 0; residual = 0.0; for (const auto& f : f3d) { @@ -1380,7 +1380,7 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { // Field3D quantities evolved together within a cell if (!f3d.empty()) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { auto i3d = mesh->ind2Dto3D(i2d, jz); BoutReal residual = 0.0; diff --git a/tests/integrated/test-invpar/test_invpar.cxx b/tests/integrated/test-invpar/test_invpar.cxx index 350437d66b..99b4e99c5c 100644 --- a/tests/integrated/test-invpar/test_invpar.cxx +++ b/tests/integrated/test-invpar/test_invpar.cxx @@ -47,7 +47,7 @@ int test(const std::string& acoef, const std::string& bcoef, const std::string& local_ystart = mesh->ystart + 1; } for (int y = local_ystart; y < mesh->yend; y++) { - for (int z = 0; z < mesh->LocalNz; z++) { + for (int z = mesh->zstart; z <= mesh->zend; z++) { output.write("result: [{:d},{:d}] : {:e}, {:e}, {:e}\n", y, z, input(mesh->xstart, y, z), result(mesh->xstart, y, z), deriv(mesh->xstart, y, z)); diff --git a/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx b/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx index 2b03f78049..a3e128bfdc 100644 --- a/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx +++ b/tests/integrated/test-multigrid_laplace/test_multigrid_laplace.cxx @@ -173,14 +173,14 @@ int main(int argc, char** argv) { // make field to pass in boundary conditions Field3D x0 = 0.; if (mesh->firstX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xstart - 1, mesh->ystart, k) = 0.5 * (f3(mesh->xstart - 1, mesh->ystart, k) + f3(mesh->xstart, mesh->ystart, k)); } } if (mesh->lastX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xend + 1, mesh->ystart, k) = 0.5 * (f3(mesh->xend + 1, mesh->ystart, k) + f3(mesh->xend, mesh->ystart, k)); } @@ -238,7 +238,7 @@ int main(int argc, char** argv) { // make field to pass in boundary conditions x0 = 0.; if (mesh->firstX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xstart - 1, mesh->ystart, k) = (f4(mesh->xstart, mesh->ystart, k) - f4(mesh->xstart - 1, mesh->ystart, k)) / mesh->getCoordinates()->dx(mesh->xstart, mesh->ystart, k) @@ -246,7 +246,7 @@ int main(int argc, char** argv) { } } if (mesh->lastX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xend + 1, mesh->ystart, k) = (f4(mesh->xend + 1, mesh->ystart, k) - f4(mesh->xend, mesh->ystart, k)) / mesh->getCoordinates()->dx(mesh->xend, mesh->ystart, k) @@ -311,7 +311,7 @@ BoutReal max_error_at_ystart(const Field3D& error) { BoutReal local_max_error = error(mesh->xstart, mesh->ystart, 0); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { if (local_max_error < error(jx, mesh->ystart, jz)) { local_max_error = error(jx, mesh->ystart, jz); } diff --git a/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx b/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx index 07a403e2e2..e9af4e5b36 100644 --- a/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx +++ b/tests/integrated/test-naulin-laplace/test_naulin_laplace.cxx @@ -175,14 +175,14 @@ int main(int argc, char** argv) { // make field to pass in boundary conditions Field3D x0 = 0.; if (mesh->firstX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xstart - 1, mesh->ystart, k) = 0.5 * (f3(mesh->xstart - 1, mesh->ystart, k) + f3(mesh->xstart, mesh->ystart, k)); } } if (mesh->lastX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xend + 1, mesh->ystart, k) = 0.5 * (f3(mesh->xend + 1, mesh->ystart, k) + f3(mesh->xend, mesh->ystart, k)); } @@ -242,7 +242,7 @@ int main(int argc, char** argv) { // make field to pass in boundary conditions x0 = 0.; if (mesh->firstX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xstart - 1, mesh->ystart, k) = (f4(mesh->xstart, mesh->ystart, k) - f4(mesh->xstart - 1, mesh->ystart, k)) / mesh->getCoordinates()->dx(mesh->xstart, mesh->ystart, k) @@ -250,7 +250,7 @@ int main(int argc, char** argv) { } } if (mesh->lastX()) { - for (int k = 0; k < mesh->LocalNz; k++) { + for (int k = mesh->zstart; k <= mesh->zend; k++) { x0(mesh->xend + 1, mesh->ystart, k) = (f4(mesh->xend + 1, mesh->ystart, k) - f4(mesh->xend, mesh->ystart, k)) / mesh->getCoordinates()->dx(mesh->xend, mesh->ystart, k) @@ -315,7 +315,7 @@ BoutReal max_error_at_ystart(const Field3D& error) { BoutReal local_max_error = error(mesh->xstart, mesh->ystart, 0); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { if (local_max_error < error(jx, mesh->ystart, jz)) { local_max_error = error(jx, mesh->ystart, jz); } diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index 1e3cdde310..c42c55d8d6 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -222,7 +222,7 @@ BoutReal max_error_at_ystart(const Field3D& error) { BoutReal local_max_error = error(mesh->xstart, mesh->ystart, 0); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { if (local_max_error < error(jx, mesh->ystart, jz)) { local_max_error = error(jx, mesh->ystart, jz); } @@ -241,7 +241,7 @@ void apply_flat_boundary(Field3D& bcoef) { if (mesh.firstX()) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { bcoef(jx, jy, jz) = bcoef(jx + 1, jy, jz); } } @@ -250,7 +250,7 @@ void apply_flat_boundary(Field3D& bcoef) { if (mesh.lastX()) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { bcoef(jx, jy, jz) = bcoef(jx - 1, jy, jz); } } @@ -270,7 +270,7 @@ Field3D generate_f1(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. @@ -289,7 +289,7 @@ Field3D generate_f1(const Mesh& mesh) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. @@ -308,7 +308,7 @@ Field3D generate_f1(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. @@ -340,7 +340,7 @@ Field3D generate_d1(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + 0.2 * exp(-50. * pow(x - p, 2) / 4.) * sin(2. * PI * (z - q) * 3.); @@ -351,7 +351,7 @@ Field3D generate_d1(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + 0.2 * exp(-50. * pow(x - p, 2) / 4.) * sin(2. * PI * (z - q) * 3.); @@ -363,7 +363,7 @@ Field3D generate_d1(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + 0.2 * exp(-50. * pow(x - p, 2) / 4.) * sin(2. * PI * (z - q) * 3.); @@ -386,7 +386,7 @@ Field3D generate_c1(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + 0.15 * exp(-50. * pow(x - p, 2) * 2.) * sin(2. * PI * (z - q) * 2.); @@ -397,7 +397,7 @@ Field3D generate_c1(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + 0.15 * exp(-50. * pow(x - p, 2) * 2.) * sin(2. * PI * (z - q) * 2.); @@ -409,7 +409,7 @@ Field3D generate_c1(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + 0.15 * exp(-50. * pow(x - p, 2) * 2.) * sin(2. * PI * (z - q) * 2.); @@ -433,7 +433,7 @@ Field3D generate_a1(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = -1. + 0.1 * exp(-50. * pow(x - p, 2) * 2.5) * sin(2. * PI * (z - q) * 7.); @@ -444,7 +444,7 @@ Field3D generate_a1(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = -1. + 0.1 * exp(-50. * pow(x - p, 2) * 2.5) * sin(2. * PI * (z - q) * 7.); @@ -456,7 +456,7 @@ Field3D generate_a1(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = -1. + 0.1 * exp(-50. * pow(x - p, 2) * 2.5) * sin(2. * PI * (z - q) * 7.); @@ -479,7 +479,7 @@ Field3D generate_f5(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; //make the gradients zero at both x-boundaries result(jx, jy, jz) = @@ -496,7 +496,7 @@ Field3D generate_f5(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. @@ -515,7 +515,7 @@ Field3D generate_f5(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. @@ -545,7 +545,7 @@ Field3D generate_d5(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + p * cos(2. * PI * x) * sin(2. * PI * (z - q) * 3.); } @@ -555,7 +555,7 @@ Field3D generate_d5(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + p * cos(2. * PI * x) * sin(2. * PI * (z - q) * 3.); } @@ -566,7 +566,7 @@ Field3D generate_d5(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + p * cos(2. * PI * x) * sin(2. * PI * (z - q) * 3.); } @@ -589,7 +589,7 @@ Field3D generate_c5(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + p * cos(2. * PI * x * 5) * sin(2. * PI * (z - q) * 2.); } @@ -599,7 +599,7 @@ Field3D generate_c5(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + p * cos(2. * PI * x * 5) * sin(2. * PI * (z - q) * 2.); @@ -611,7 +611,7 @@ Field3D generate_c5(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = 1. + p * cos(2. * PI * x * 5) * sin(2. * PI * (z - q) * 2.); @@ -633,7 +633,7 @@ Field3D generate_a5(const Mesh& mesh) { for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); @@ -644,7 +644,7 @@ Field3D generate_a5(const Mesh& mesh) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); @@ -656,7 +656,7 @@ Field3D generate_a5(const Mesh& mesh) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = 0; jz < mesh.LocalNz; jz++) { + for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx b/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx index 7646b915a7..945d5c6443 100644 --- a/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx +++ b/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx @@ -63,7 +63,7 @@ int main(int argc, char** argv) { f1.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; f1(jx, jy, jz) = @@ -84,7 +84,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; f1(jx, jy, jz) = @@ -106,7 +106,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; f1(jx, jy, jz) = @@ -131,7 +131,7 @@ int main(int argc, char** argv) { d1.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; d1(jx, jy, jz) = @@ -143,7 +143,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; d1(jx, jy, jz) = @@ -157,7 +157,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; d1(jx, jy, jz) = @@ -174,7 +174,7 @@ int main(int argc, char** argv) { c1.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; c1(jx, jy, jz) = 1. @@ -186,7 +186,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; c1(jx, jy, jz) = 1. @@ -199,7 +199,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; c1(jx, jy, jz) = 1. @@ -215,7 +215,7 @@ int main(int argc, char** argv) { a1.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; a1(jx, jy, jz) = @@ -226,7 +226,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; a1(jx, jy, jz) = @@ -238,7 +238,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; a1(jx, jy, jz) = @@ -256,7 +256,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b1(jx, jy, jz) = b1(jx + 1, jy, jz); } } @@ -265,7 +265,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b1(jx, jy, jz) = b1(jx - 1, jy, jz); } } @@ -371,7 +371,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b3(jx, jy, jz) = b3(jx + 1, jy, jz); } } @@ -380,7 +380,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b3(jx, jy, jz) = b3(jx - 1, jy, jz); } } @@ -468,7 +468,7 @@ int main(int argc, char** argv) { f5.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; f5(jx, jy, jz) = @@ -489,7 +489,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; f5(jx, jy, jz) = @@ -511,7 +511,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; f5(jx, jy, jz) = @@ -536,7 +536,7 @@ int main(int argc, char** argv) { d5.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; d5(jx, jy, jz) = @@ -547,7 +547,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; d5(jx, jy, jz) = @@ -559,7 +559,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; d5(jx, jy, jz) = @@ -574,7 +574,7 @@ int main(int argc, char** argv) { c5.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; c5(jx, jy, jz) = @@ -585,7 +585,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; c5(jx, jy, jz) = @@ -597,7 +597,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; c5(jx, jy, jz) = @@ -612,7 +612,7 @@ int main(int argc, char** argv) { a5.allocate(); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; a5(jx, jy, jz) = -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); @@ -622,7 +622,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; a5(jx, jy, jz) = @@ -634,7 +634,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { BoutReal x = BoutReal(mesh->getGlobalXIndex(jx) - mesh->xstart) / nx; BoutReal z = BoutReal(jz) / nz; a5(jx, jy, jz) = @@ -650,7 +650,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b5(jx, jy, jz) = b5(jx + 1, jy, jz); } } @@ -659,7 +659,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b5(jx, jy, jz) = b5(jx - 1, jy, jz); } } @@ -762,7 +762,7 @@ int main(int argc, char** argv) { if (mesh->firstX()) { for (int jx = mesh->xstart - 1; jx >= 0; jx--) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b7(jx, jy, jz) = b7(jx + 1, jy, jz); } } @@ -771,7 +771,7 @@ int main(int argc, char** argv) { if (mesh->lastX()) { for (int jx = mesh->xend + 1; jx < mesh->LocalNx; jx++) { for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { b7(jx, jy, jz) = b7(jx - 1, jy, jz); } } @@ -864,7 +864,7 @@ BoutReal max_error_at_ystart(const Field3D& error) { BoutReal local_max_error = error(mesh->xstart, mesh->ystart, 0); for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jz = 0; jz < mesh->LocalNz; jz++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { if (local_max_error < error(jx, mesh->ystart, jz)) { local_max_error = error(jx, mesh->ystart, jz); } From 2dba4b436b518a2f9e32dc0fc749984b7ea01093 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 7 Jan 2026 17:22:53 +0000 Subject: [PATCH 141/461] Remove `BRACKET_ARAKAWA_OLD` --- examples/performance/bracket/bracket.cxx | 12 --- include/bout/difops.hxx | 2 - manual/sphinx/user_docs/python_boutpp.rst | 2 +- src/mesh/difops.cxx | 97 ----------------------- tests/MMS/spatial/advection/runtest | 1 - tools/pylib/_boutpp_build/other_enums.hxx | 2 +- tools/pylib/_boutpp_build/scan_enums.py | 2 - 7 files changed, 2 insertions(+), 116 deletions(-) diff --git a/examples/performance/bracket/bracket.cxx b/examples/performance/bracket/bracket.cxx index 50012510ff..0ef9e241fb 100644 --- a/examples/performance/bracket/bracket.cxx +++ b/examples/performance/bracket/bracket.cxx @@ -68,9 +68,6 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK("Bracket [2D,3D] ARAKAWA", result = bracket(a, c, BRACKET_ARAKAWA);); - ITERATOR_TEST_BLOCK("Bracket [2D,3D] ARAKAWA_OLD", - result = bracket(a, c, BRACKET_ARAKAWA_OLD);); - ITERATOR_TEST_BLOCK("Bracket [2D,3D] SIMPLE", result = bracket(a, c, BRACKET_SIMPLE);); @@ -81,21 +78,12 @@ int main(int argc, char** argv) { ITERATOR_TEST_BLOCK("Bracket [3D,3D] ARAKAWA", result = bracket(a, b, BRACKET_ARAKAWA);); - ITERATOR_TEST_BLOCK("Bracket [3D,3D] ARAKAWA_OLD", - result = bracket(a, b, BRACKET_ARAKAWA_OLD);); - ITERATOR_TEST_BLOCK("Bracket [3D,3D] SIMPLE", result = bracket(a, b, BRACKET_SIMPLE);); ITERATOR_TEST_BLOCK("Bracket [3D,3D] DEFAULT", result = bracket(a, b, BRACKET_STD);); } - // Uncomment below for a "correctness" check - // Field3D resNew = bracket(a, b, BRACKET_ARAKAWA); mesh->communicate(resNew); - // Field3D resOld = bracket(a, b, BRACKET_ARAKAWA_OLD); mesh->communicate(resOld); - // time_output << "Max abs diff is - // "<LocalNz; - BOUT_OMP_PERF(parallel for) - for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jy = mesh->ystart; jy <= mesh->yend; jy++) { - const BoutReal partialFactor = 1.0 / (12 * metric->dz(jx, jy)); - const BoutReal spacingFactor = partialFactor / metric->dx(jx, jy); - for (int jz = 0; jz < mesh->LocalNz; jz++) { - const int jzp = jz + 1 < ncz ? jz + 1 : 0; - // Above is alternative to const int jzp = (jz + 1) % ncz; - const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; - // Above is alternative to const int jzmTmp = (jz - 1 + ncz) % ncz; - - // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) - BoutReal Jpp = - ((f(jx, jy, jzp) - f(jx, jy, jzm)) * (g(jx + 1, jy) - g(jx - 1, jy)) - - (f(jx + 1, jy, jz) - f(jx - 1, jy, jz)) * (g(jx, jy) - g(jx, jy))); - - // J+x - BoutReal Jpx = (g(jx + 1, jy) * (f(jx + 1, jy, jzp) - f(jx + 1, jy, jzm)) - - g(jx - 1, jy) * (f(jx - 1, jy, jzp) - f(jx - 1, jy, jzm)) - - g(jx, jy) * (f(jx + 1, jy, jzp) - f(jx - 1, jy, jzp)) - + g(jx, jy) * (f(jx + 1, jy, jzm) - f(jx - 1, jy, jzm))); - - // Jx+ - BoutReal Jxp = (g(jx + 1, jy) * (f(jx, jy, jzp) - f(jx + 1, jy, jz)) - - g(jx - 1, jy) * (f(jx - 1, jy, jz) - f(jx, jy, jzm)) - - g(jx - 1, jy) * (f(jx, jy, jzp) - f(jx - 1, jy, jz)) - + g(jx + 1, jy) * (f(jx + 1, jy, jz) - f(jx, jy, jzm))); - - result(jx, jy, jz) = (Jpp + Jpx + Jxp) * spacingFactor; - } - } - } -#else - throw BoutException("BRACKET_ARAKAWA_OLD not valid with 3D metrics yet."); -#endif - break; - } case BRACKET_SIMPLE: { // Use a subset of terms for comparison to BOUT-06 result = VDDX(DDZ(f, outloc), g, outloc); @@ -1090,63 +1050,6 @@ Field3D bracket(const Field3D& f, const Field3D& g, BRACKET_METHOD method, } break; } - case BRACKET_ARAKAWA_OLD: { - // Arakawa scheme for perpendicular flow - - const int ncz = mesh->LocalNz; - - // We need to discard const qualifier in order to manipulate - // storage array directly - Field3D f_temp = f; - Field3D g_temp = g; - - BOUT_OMP_PERF(parallel for) - for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - for (int jy = mesh->ystart; jy <= mesh->yend; jy++) { -#if not(BOUT_USE_METRIC_3D) - const BoutReal spacingFactor = - 1.0 / (12 * metric->dz(jx, jy) * metric->dx(jx, jy)); -#endif - const BoutReal* Fxm = f_temp(jx - 1, jy); - const BoutReal* Fx = f_temp(jx, jy); - const BoutReal* Fxp = f_temp(jx + 1, jy); - const BoutReal* Gxm = g_temp(jx - 1, jy); - const BoutReal* Gx = g_temp(jx, jy); - const BoutReal* Gxp = g_temp(jx + 1, jy); - for (int jz = 0; jz < mesh->LocalNz; jz++) { -#if BOUT_USE_METRIC_3D - const BoutReal spacingFactor = - 1.0 / (12 * metric->dz(jx, jy, jz) * metric->dx(jx, jy, jz)); -#endif - const int jzp = jz + 1 < ncz ? jz + 1 : 0; - // Above is alternative to const int jzp = (jz + 1) % ncz; - const int jzm = jz - 1 >= 0 ? jz - 1 : ncz - 1; - // Above is alternative to const int jzm = (jz - 1 + ncz) % ncz; - - // J++ = DDZ(f)*DDX(g) - DDX(f)*DDZ(g) - // NOLINTNEXTLINE - BoutReal Jpp = ((Fx[jzp] - Fx[jzm]) * (Gxp[jz] - Gxm[jz]) - - (Fxp[jz] - Fxm[jz]) * (Gx[jzp] - Gx[jzm])); - - // J+x - // NOLINTNEXTLINE - BoutReal Jpx = - (Gxp[jz] * (Fxp[jzp] - Fxp[jzm]) - Gxm[jz] * (Fxm[jzp] - Fxm[jzm]) - - Gx[jzp] * (Fxp[jzp] - Fxm[jzp]) + Gx[jzm] * (Fxp[jzm] - Fxm[jzm])); - - // Jx+ - // NOLINTNEXTLINE - BoutReal Jxp = - (Gxp[jzp] * (Fx[jzp] - Fxp[jz]) - Gxm[jzm] * (Fxm[jz] - Fx[jzm]) - - Gxm[jzp] * (Fx[jzp] - Fxm[jz]) + Gxp[jzm] * (Fxp[jz] - Fx[jzm])); - - result(jx, jy, jz) = (Jpp + Jpx + Jxp) * spacingFactor; - } - } - } - - break; - } case BRACKET_SIMPLE: { // Use a subset of terms for comparison to BOUT-06 result = VDDX(DDZ(f, outloc), g, outloc) + VDDZ(-DDX(f, outloc), g, outloc); diff --git a/tests/MMS/spatial/advection/runtest b/tests/MMS/spatial/advection/runtest index 9114664b47..fa06c478ee 100755 --- a/tests/MMS/spatial/advection/runtest +++ b/tests/MMS/spatial/advection/runtest @@ -43,7 +43,6 @@ build_and_log("MMS steady-state advection test") # List of options to be passed for each test options = [ ("method=2", "Arakawa", "-^", 2), - ("method=4", "Arakawa-old", "-.", 2), ( "method=1 mesh:ddx:upwind=u1 mesh:ddz:upwind=u1", "SIMPLE: 1st order upwind", diff --git a/tools/pylib/_boutpp_build/other_enums.hxx b/tools/pylib/_boutpp_build/other_enums.hxx index 30480a1007..a1da5ded7e 100644 --- a/tools/pylib/_boutpp_build/other_enums.hxx +++ b/tools/pylib/_boutpp_build/other_enums.hxx @@ -1,4 +1,4 @@ #ifdef YOU_SHOULDNT_READ_THIS #error YOU_SHOULDNT_BE_HERE -enum class BRACKET_METHOD { standard, simple, arakawa, ctu, arakawa_old }; +enum class BRACKET_METHOD { standard, simple, arakawa, ctu }; #endif diff --git a/tools/pylib/_boutpp_build/scan_enums.py b/tools/pylib/_boutpp_build/scan_enums.py index 837380c828..a403a92625 100644 --- a/tools/pylib/_boutpp_build/scan_enums.py +++ b/tools/pylib/_boutpp_build/scan_enums.py @@ -78,8 +78,6 @@ def __str__(self): "ARAKAWA": "arakawa", "BRACKET_CTU": "ctu", "CTU": "ctu", - "BRACKET_ARAKAWA_OLD": "arakawa_old", - "ARAKAWA_OLD": "arakawa_old", } enums["CELL_LOC"].extra = { "CELL_DEFAULT": "deflt", From f9c066a59e71dbc63e439bd0ecb4b86536921032 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Wed, 7 Jan 2026 17:30:04 +0000 Subject: [PATCH 142/461] Apply clang-format changes --- include/bout/difops.hxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index a11975d89b..169403a945 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -271,11 +271,11 @@ Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, * Poisson bracket methods */ enum class BRACKET_METHOD { - standard, ///< Use b0xGrad_dot_Grad - simple, ///< Keep only terms in X-Z - arakawa, ///< Arakawa method in X-Z (optimised) - ctu, ///< Corner Transport Upwind (CTU) method. Explicit method only, needs the - /// timestep from the solver + standard, ///< Use b0xGrad_dot_Grad + simple, ///< Keep only terms in X-Z + arakawa, ///< Arakawa method in X-Z (optimised) + ctu, ///< Corner Transport Upwind (CTU) method. Explicit method only, needs the + /// timestep from the solver }; constexpr BRACKET_METHOD BRACKET_STD = BRACKET_METHOD::standard; constexpr BRACKET_METHOD BRACKET_SIMPLE = BRACKET_METHOD::simple; From 0b48cc8852b88e7aeacee03ef57d5ef494f75b4d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 8 Jan 2026 09:54:34 +0000 Subject: [PATCH 143/461] Fix outdated comment on `arakawa` enum Co-authored-by: David Bold --- include/bout/difops.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index 169403a945..8b502002f2 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -273,7 +273,7 @@ Field3D b0xGrad_dot_Grad(const Field3D& phi, const Field3D& A, enum class BRACKET_METHOD { standard, ///< Use b0xGrad_dot_Grad simple, ///< Keep only terms in X-Z - arakawa, ///< Arakawa method in X-Z (optimised) + arakawa, ///< Arakawa method in X-Z ctu, ///< Corner Transport Upwind (CTU) method. Explicit method only, needs the /// timestep from the solver }; From 4ac87c13a12b4fda73c9b5bcf6a5606138656fe0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 8 Jan 2026 15:17:57 +0000 Subject: [PATCH 144/461] docs: Expand on using `cpptrace` and the backtrace --- manual/sphinx/developer_docs/debugging.rst | 125 +++++++++++---------- manual/sphinx/user_docs/installing.rst | 13 ++- 2 files changed, 74 insertions(+), 64 deletions(-) diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index 4e8bd0feab..f16d0b6a3b 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -34,10 +34,74 @@ To enable the ``output_debug`` messages, configure BOUT++ with a configure BOUT++ with ``-DENABLE_OUTPUT_DEBUG``. When running BOUT++ add a ``-v -v`` flag to see ``output_debug`` messages. +Backtrace +========= + +BOUT++ can also automatically print a backtrace in the event of a crash. This is +very useful to include if you ever need to report a bug to the developers! The +output looks something like this: + +.. code:: text + + ... + Error encountered: Stack trace (most recent call first): + #0 (filtered) + #1 (filtered) + #2 (filtered) + #3 in BoutMesh::createCommunicators() + at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:709:64 + 707: } + 708: // Unconditional exception for demo purposes + > 709: throw BoutException("Single null outer SOL not correct\n"); + ^ + 710: MPI_Group_free(&group); + 711: } + #4 in BoutMesh::load() + at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:575:22 + 573: /// Communicator + 574: + > 575: createCommunicators(); + ^ + 576: output_debug << "Got communicators" << endl; + #5 in BoutInitialise(int&, char**&) + at BOUT-dev/src/bout++.cxx:201:28 + 199: bout::globals::mesh = Mesh::create(); + 200: // Load from sources. Required for Field initialisation + > 201: bout::globals::mesh->load(); + ^ + 202: + 203: // time_report options are used in BoutFinalise, i.e. after we + #6 in main + at BOUT-dev/examples/elm-pb/elm_pb.cxx:2161:1 + 2159: }; + 2160: + > 2161: BOUTMAIN(ELMpb); + ^ + #7 (filtered) + #8 (filtered) + #9 (filtered) + + ====== Exception thrown ====== + Single null outer SOL not correct + + +Debug symbols are required to get the filename/line number and code snippets. If +they are missing in either BOUT++ or the physics model, only the function name +and signature will be included for that part. + +Including debug symbols is a configure time ``CMake`` option, set either: +``-DCMAKE_BUILD_TYPE=Debug`` or ``-DCMAKE_BUILD_TYPE=RelWithDebInfo`` (the +default). + +The formatting of this backtrace is controlled in +`BoutException::getBacktrace()` using the `cpptrace +`_ library. + + Message Stack ============= -The second utility BOUT++ has to help debugging is the message stack using the +Another utility BOUT++ has to help debugging is the message stack using the `TRACE` macro. This is very useful for when a bug only occurs after a long time of running, and/or only occasionally. The ``TRACE`` macro can simply be dropped in anywhere in the code:: @@ -94,62 +158,3 @@ If you need to capture runtime information in the message, you can use the ``fmt`` syntax also used by the loggers:: TRACE("Value of i={}, some arbitrary {}", i, "string"); - -Backtrace -========= - -Lastly, BOUT++ can also automatically print a backtrace in the event of a -crash. This is very useful to include if you ever need to report a bug to the -developers! The output looks something like this: - -.. code:: text - - ... - Error encountered: Stack trace (most recent call first): - #0 (filtered) - #1 (filtered) - #2 (filtered) - #3 in BoutMesh::createCommunicators() - at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:709:64 - 707: } - 708: // Unconditional exception for demo purposes - > 709: throw BoutException("Single null outer SOL not correct\n"); - ^ - 710: MPI_Group_free(&group); - 711: } - #4 in BoutMesh::load() - at BOUT-dev/src/mesh/impls/bout/boutmesh.cxx:575:22 - 573: /// Communicator - 574: - > 575: createCommunicators(); - ^ - 576: output_debug << "Got communicators" << endl; - #5 in BoutInitialise(int&, char**&) - at BOUT-dev/src/bout++.cxx:201:28 - 199: bout::globals::mesh = Mesh::create(); - 200: // Load from sources. Required for Field initialisation - > 201: bout::globals::mesh->load(); - ^ - 202: - 203: // time_report options are used in BoutFinalise, i.e. after we - #6 in main - at BOUT-dev/examples/elm-pb/elm_pb.cxx:2161:1 - 2159: }; - 2160: - > 2161: BOUTMAIN(ELMpb); - ^ - #7 (filtered) - #8 (filtered) - #9 (filtered) - - ====== Exception thrown ====== - Single null outer SOL not correct - - -Debug symbols are required to get the filename/line number and code snippets. If -they are missing in either BOUT++ or the physics model, only the function name -and signature will be included for that part. - -Including debug symbols is a configure time ``CMake`` option, set either: -``-DCMAKE_BUILD_TYPE=Debug`` or ``-DCMAKE_BUILD_TYPE=RelWithDebInfo`` (the -default). diff --git a/manual/sphinx/user_docs/installing.rst b/manual/sphinx/user_docs/installing.rst index 10f5d9b9f1..2c4b8e847d 100644 --- a/manual/sphinx/user_docs/installing.rst +++ b/manual/sphinx/user_docs/installing.rst @@ -332,10 +332,13 @@ The default build configuration options try to be sensible for new users and developers, but there are a few you probably want to set manually for production runs or for debugging: -* ``CMAKE_BUILD_TYPE``: The default is ``RelWithDebInfo``, which - builds an optimised executable with debug symbols included. Change - this to ``Release`` to remove the debug symbols, or ``Debug`` for an - unoptimised build, but better debug experience +* ``CMAKE_BUILD_TYPE``: The default is ``RelWithDebInfo``, which builds an + optimised executable with debug symbols included. This is generally the most + useful, except for developers, who may wish to use ``Debug`` for an + unoptimised build, but better debug experience. There are a couple of other + choices (``Release`` and ``MinSizeRel``) which also produce optimised + executables, but without debug symbols, which is only really useful for + producing smaller binaries. * ``CHECK``: This sets the level of internal runtime checking done in the BOUT++ library, and ranges from 0 to 4 (inclusive). By default, @@ -377,6 +380,8 @@ For ADIOS2, use ``-DBOUT_DOWNLOAD_ADIOS2=ON``. This will download and configure `ADIOS2 `_, enabling BOUT++ to read and write this high-performance parallel file format. +For cpptrace, use ``-DBOUT_DOWNLOAD_CPPTRACE=on`` (the default). + Bundled Dependencies ~~~~~~~~~~~~~~~~~~~~ From 73e585768078fb32364d8801c30c36e46f6807f8 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Thu, 8 Jan 2026 15:18:45 +0000 Subject: [PATCH 145/461] Apply clang-format changes --- src/sys/boutexception.cxx | 3 +-- tests/integrated/test-backtrace/boutexcept.cxx | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index 02b6ef3391..b5ea01d231 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -72,8 +72,7 @@ std::string BoutException::getBacktrace() const { formatter.format(generate_trace()), message); if (msg_stack.size() > 0) { - return fmt::format("{}\n\n{}", backtrace_message, - msg_stack.getDump()); + return fmt::format("{}\n\n{}", backtrace_message, msg_stack.getDump()); } return backtrace_message; } diff --git a/tests/integrated/test-backtrace/boutexcept.cxx b/tests/integrated/test-backtrace/boutexcept.cxx index 6017ecd8f9..6ebe2d05c4 100644 --- a/tests/integrated/test-backtrace/boutexcept.cxx +++ b/tests/integrated/test-backtrace/boutexcept.cxx @@ -4,7 +4,7 @@ namespace { void troublemaker() { throw BoutException("test"); } void f() { troublemaker(); } void e() { f(); } -} +} // namespace int main() { e(); From 8edb351b1f75382ae014f770676c239ee92e74be Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 8 Jan 2026 16:01:24 +0000 Subject: [PATCH 146/461] tests: Fix exception test inlining functions --- tests/integrated/test-backtrace/boutexcept.cxx | 6 ++++-- tests/integrated/test-backtrace/runtest | 4 ++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/tests/integrated/test-backtrace/boutexcept.cxx b/tests/integrated/test-backtrace/boutexcept.cxx index 6ebe2d05c4..3019c690a9 100644 --- a/tests/integrated/test-backtrace/boutexcept.cxx +++ b/tests/integrated/test-backtrace/boutexcept.cxx @@ -1,10 +1,12 @@ #include "bout/boutexception.hxx" -namespace { +// Can't use anonymous namespace, or the compiler will inline everything, +// defeating the point of this test. +// NOLINTBEGIN(misc-use-internal-linkage) void troublemaker() { throw BoutException("test"); } void f() { troublemaker(); } void e() { f(); } -} // namespace +// NOLINTEND(misc-use-internal-linkage) int main() { e(); diff --git a/tests/integrated/test-backtrace/runtest b/tests/integrated/test-backtrace/runtest index 4010873b56..06497ec65a 100755 --- a/tests/integrated/test-backtrace/runtest +++ b/tests/integrated/test-backtrace/runtest @@ -21,13 +21,13 @@ _, output = shell("BOUT_SHOW_BACKTRACE=0 ./boutexcept", pipe=True) if "troublemaker" in output: success = False - print("Fail: detected offending function name in output when not expected") + print(f"Fail: detected offending function name in output when not expected:\n{output}") _, output = shell("./boutexcept", pipe=True) if "troublemaker" not in output: success = False - print("Fail: did not detect offending function name in output when expected") + print(f"Fail: did not detect offending function name in output when expected:\n{output}") if success: print("=> All BoutException backtrace tests passed") From 6b03b6c1c214f476cf2280ddcfe69a336b324df4 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Thu, 8 Jan 2026 16:02:30 +0000 Subject: [PATCH 147/461] Apply black changes --- tests/integrated/test-backtrace/runtest | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/integrated/test-backtrace/runtest b/tests/integrated/test-backtrace/runtest index 06497ec65a..c4020e6d99 100755 --- a/tests/integrated/test-backtrace/runtest +++ b/tests/integrated/test-backtrace/runtest @@ -21,13 +21,17 @@ _, output = shell("BOUT_SHOW_BACKTRACE=0 ./boutexcept", pipe=True) if "troublemaker" in output: success = False - print(f"Fail: detected offending function name in output when not expected:\n{output}") + print( + f"Fail: detected offending function name in output when not expected:\n{output}" + ) _, output = shell("./boutexcept", pipe=True) if "troublemaker" not in output: success = False - print(f"Fail: did not detect offending function name in output when expected:\n{output}") + print( + f"Fail: did not detect offending function name in output when expected:\n{output}" + ) if success: print("=> All BoutException backtrace tests passed") From 86e3c1c576a2bd3cd7a513db976fbffce7de7e3a Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 8 Jan 2026 16:24:17 +0000 Subject: [PATCH 148/461] docs: Fix typo in use of `OptionsIO` Closes #3227 --- manual/sphinx/user_docs/bout_options.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/sphinx/user_docs/bout_options.rst b/manual/sphinx/user_docs/bout_options.rst index 85a8a17d59..926c201c16 100644 --- a/manual/sphinx/user_docs/bout_options.rst +++ b/manual/sphinx/user_docs/bout_options.rst @@ -950,7 +950,7 @@ automatically set the ``"time_dimension"`` attribute:: data["field"] = Field3D(3.0); // Append data to file - bout::OptionsIO({{"file", "time.nc"}, {"append", true}})->write(data); + bout::OptionsIO::create({{"file", "time.nc"}, {"append", true}})->write(data); .. note:: By default, `bout::OptionsIO::write` will only write variables with a ``"time_dimension"`` of ``"t"``. You can write From 9855b5f21cb8e4b19d5c430033e57d0aa239bbe8 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 13:50:02 +0000 Subject: [PATCH 149/461] Add `cpptrace` as git submodule --- .gitmodules | 3 ++ cmake/SetupBOUTThirdParty.cmake | 28 +++++++++++-------- externalpackages/cpptrace | 1 + manual/sphinx/user_docs/installing.rst | 38 ++++++++++++++++++-------- 4 files changed, 47 insertions(+), 23 deletions(-) create mode 160000 externalpackages/cpptrace diff --git a/.gitmodules b/.gitmodules index 4b33b3b615..2bdbd2a57b 100644 --- a/.gitmodules +++ b/.gitmodules @@ -13,3 +13,6 @@ [submodule "externalpackages/boutdata"] path = externalpackages/boutdata url = https://github.com/boutproject/boutdata.git +[submodule "externalpackages/cpptrace"] + path = externalpackages/cpptrace + url = https://github.com/ZedThree/cpptrace.git diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 2c5b9a1cfb..52b7468012 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -370,18 +370,24 @@ endif() message(STATUS "UUID_SYSTEM_GENERATOR: ${BOUT_USE_UUID_SYSTEM_GENERATOR}") set(BOUT_HAS_UUID_SYSTEM_GENERATOR ${BOUT_USE_UUID_SYSTEM_GENERATOR}) -option(BOUT_DOWNLOAD_CPPTRACE "Download cpptrace for backtrace support" ON) -if (BOUT_DOWNLOAD_CPPTRACE) - include(FetchContent) +cmake_dependent_option(BOUT_USE_SYSTEM_CPPTRACE "Use external installation of cpptrace" OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/cpptrace/CMakeLists.txt" ON) + +if(BOUT_USE_SYSTEM_CPPTRACE) + message(STATUS "Using external cpptrace") + find_package(cpptrace REQUIRED) + get_target_property(CPPTRACE_INCLUDE_PATH cpptrace::cpptrace INTERFACE_INCLUDE_DIRECTORIES) +else() + message(STATUS "Using cpptrace submodule") + bout_update_submodules() + # Need a fork with some fixes for CMake set(CPPTRACE_LIBDWARF_REPO "https://github.com/ZedThree/libdwarf-lite.git" CACHE STRING "" FORCE) set(CPPTRACE_LIBDWARF_TAG "ebe10a39afd56b8247de633bfe17666ad50ab95e" CACHE STRING "" FORCE) - FetchContent_Declare( - cpptrace - GIT_REPOSITORY https://github.com/ZedThree/cpptrace.git - GIT_TAG "027f9aee2d34dbe1c98f26224e1fbe1654cb4aae" - ) - FetchContent_MakeAvailable(cpptrace) -else() - find_package(cpptrace REQUIRED) + add_subdirectory(externalpackages/cpptrace) + if(NOT TARGET cpptrace::cpptrace) + message(FATAL_ERROR "cpptrace not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?") + endif() + set(CPPTRACE_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/cpptrace/include") endif() +set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${CPPTRACE_INCLUDE_PATH}") target_link_libraries(bout++ PUBLIC cpptrace::cpptrace) diff --git a/externalpackages/cpptrace b/externalpackages/cpptrace new file mode 160000 index 0000000000..027f9aee2d --- /dev/null +++ b/externalpackages/cpptrace @@ -0,0 +1 @@ +Subproject commit 027f9aee2d34dbe1c98f26224e1fbe1654cb4aae diff --git a/manual/sphinx/user_docs/installing.rst b/manual/sphinx/user_docs/installing.rst index 2c4b8e847d..a493f932c4 100644 --- a/manual/sphinx/user_docs/installing.rst +++ b/manual/sphinx/user_docs/installing.rst @@ -380,21 +380,35 @@ For ADIOS2, use ``-DBOUT_DOWNLOAD_ADIOS2=ON``. This will download and configure `ADIOS2 `_, enabling BOUT++ to read and write this high-performance parallel file format. -For cpptrace, use ``-DBOUT_DOWNLOAD_CPPTRACE=on`` (the default). - Bundled Dependencies ~~~~~~~~~~~~~~~~~~~~ -BOUT++ bundles some dependencies, currently `mpark.variant -`_, `fmt `_ and -`googletest `_. If you wish to -use an existing installation of ``mpark.variant``, you can set -``-DBOUT_USE_SYSTEM_MPARK_VARIANT=ON``, and supply the installation -path using ``mpark_variant_ROOT`` via the command line or environment -variable if it is installed in a non standard loction. Similarly for -``fmt``, using ``-DBOUT_USE_SYSTEM_FMT=ON`` and ``fmt_ROOT`` -respectively. To turn off both, you can set -``-DBOUT_USE_GIT_SUBMODULE=OFF``. +BOUT++ bundles some dependencies, currently: + +- `mpark.variant `_ +- `fmt `_ +- `cpptrace `_ +- ``googletest `_ (for unit tests) + +Aside from ``googletest``, the others are required dependencies and can either +be built as part of the BOUT++ build, or provided externally. If you wish to use +existing installations of some of these, set the following flags: + ++--------------------+-----------------------------------+------------------------+ +| Name | Flag for external installation | Library path | ++====================+===================================+========================+ +| ``mpark.variant`` | ``BOUT_USE_SYSTEM_MPARK_VARIANT`` | ``mpark_variant_ROOT`` | ++--------------------+-----------------------------------+------------------------+ +| ``fmt`` | ``BOUT_USE_SYSTEM_FMT`` | ``fmt_ROOT`` | ++--------------------+-----------------------------------+------------------------+ +| ``cpptrace`` | ``BOUT_USE_SYSTEM_CPPTRACE`` | ``cpptrace_ROOT`` | ++--------------------+-----------------------------------+------------------------+ + +You can also set ``-DBOUT_USE_GIT_SUBMODULE=OFF`` to not use any of the bundled +versions. + +If the libraries are in non-standard locations, you may also need to supply the +relevant library path flags. The recommended way to use ``googletest`` is to compile it at the same time as your project, therefore there is no option to use an external From 7fa1c5f3ff790f4a641c0f54d443d924e93ae48f Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 17:12:06 +0000 Subject: [PATCH 150/461] boutpp: Add `cpptrace` to sdist --- tools/pylib/_boutpp_build/backend.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tools/pylib/_boutpp_build/backend.py b/tools/pylib/_boutpp_build/backend.py index 31a4694d10..6455c203ad 100755 --- a/tools/pylib/_boutpp_build/backend.py +++ b/tools/pylib/_boutpp_build/backend.py @@ -176,7 +176,7 @@ def build_sdist(sdist_directory, config_settings=None): print(config_settings, sdist_directory) enable_gz = True enable_xz = False - external = {"fmt", "mpark.variant"} + external = {"fmt", "mpark.variant", "cpptrace"} if config_settings is not None: global useLocalVersion, pkgname for k, v in config_settings.items(): From 68904f93c08637dd2394f93bcfb42d62c2bc3848 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 17:12:26 +0000 Subject: [PATCH 151/461] docs: Init all submodules --- manual/sphinx/conf.py | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 6b997ec111..5c6b7ef1d5 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -76,11 +76,7 @@ def __getattr__(cls, name): pydir = "/".join(python.split("/")[:-2]) os.system("which clang-format") os.system("which clang-format-6.0") - os.system( - "git submodule update --init --recursive ../../externalpackages/mpark.variant" - ) - pwd = "/".join(os.getcwd().split("/")[:-2]) - os.system("git submodule update --init --recursive ../../externalpackages/fmt") + subprocess.run("git submodule update --init --recursive", shell=True) cmake = ( "cmake . " " -B build" @@ -91,8 +87,6 @@ def __getattr__(cls, name): " -DBOUT_TESTS=OFF" " -DBOUT_ALLOW_INSOURCE_BUILD=ON" f" -DPython3_ROOT_DIR={pydir}" - f" -Dmpark_variant_DIR={pwd}/externalpackages/mpark.variant/" - f" -Dfmt_DIR={pwd}/externalpackages/fmt/" ) subprocess.run(f"echo {cmake}", shell=True) subprocess.run(f"cd ../../; {cmake}", shell=True, check=True) From 4a09719018050984656ed31309a3910b7ca73ca8 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 17:31:07 +0000 Subject: [PATCH 152/461] cmake: Update cpptrace include dir for install --- CMakeLists.txt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7864705519..a5ca6e8717 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -818,6 +818,9 @@ endif() if (NOT BOUT_USE_SYSTEM_FMT) set(FMT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") endif() +if (NOT BOUT_USE_SYSTEM_CPPTRACE) + set(CPPTRACE_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") +endif() set(BOUT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") # We don't need the build include path any more string(REPLACE "-I${CMAKE_CURRENT_BINARY_DIR}/include" "" CONFIG_CFLAGS "${CONFIG_CFLAGS}") From 5374e241a50e569964e9869006ffe9981f2c39e4 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Tue, 13 Jan 2026 00:12:37 +0000 Subject: [PATCH 153/461] Bump externalpackages/boutdata from `7164a89` to `962d5a5` Bumps [externalpackages/boutdata](https://github.com/boutproject/boutdata) from `7164a89` to `962d5a5`. - [Release notes](https://github.com/boutproject/boutdata/releases) - [Commits](https://github.com/boutproject/boutdata/compare/7164a89c16dba1049d42e1715506b988c0af5926...962d5a58b8e6133a2977c3e44f1d4751a11d2b1c) --- updated-dependencies: - dependency-name: externalpackages/boutdata dependency-version: 962d5a58b8e6133a2977c3e44f1d4751a11d2b1c dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- externalpackages/boutdata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externalpackages/boutdata b/externalpackages/boutdata index 7164a89c16..962d5a58b8 160000 --- a/externalpackages/boutdata +++ b/externalpackages/boutdata @@ -1 +1 @@ -Subproject commit 7164a89c16dba1049d42e1715506b988c0af5926 +Subproject commit 962d5a58b8e6133a2977c3e44f1d4751a11d2b1c From 74acd738baae630c9ce05b425cbf1862965db072 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Thu, 15 Jan 2026 15:54:46 -0800 Subject: [PATCH 154/461] snes and petsc solvers: MPI_Abort on exceptions Catch exceptions and call MPI_Abort rather than trying to continue. There is no way to recover and synchronise processors unless all processors threw an exception at the same point. If only one processor throws an exception then the others will wait indefinitely on the next MPI communication or collective operation. --- include/bout/boutcomm.hxx | 6 ++++-- src/solver/impls/petsc/petsc.cxx | 12 +++++------- src/solver/impls/snes/snes.cxx | 12 ++++++++++-- src/sys/boutcomm.cxx | 4 ++++ 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/include/bout/boutcomm.hxx b/include/bout/boutcomm.hxx index 68c7559fee..29bff53087 100644 --- a/include/bout/boutcomm.hxx +++ b/include/bout/boutcomm.hxx @@ -4,9 +4,9 @@ * * ************************************************************************** -* Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu +* Copyright 2010 - 2026 BOUT++ contributors * -* Contact: Ben Dudson, bd512@york.ac.uk +* Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -49,6 +49,8 @@ public: static int rank(); ///< Rank: my processor number static int size(); ///< Size: number of processors + static void abort(int errorcode); ///< MPI abort + // Setting options void setComm(MPI_Comm c); diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index 81f1cc7951..834a60ba19 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -2,7 +2,7 @@ * Interface to PETSc solver * ************************************************************************** - * Copyright 2010 - 2025 BOUT++ contributors + * Copyright 2010 - 2026 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -942,12 +942,10 @@ PetscErrorCode PetscSolver::rhs(BoutReal t, Vec udata, Vec dudata, bool linear) } catch (BoutException& e) { // Simulation might fail, e.g. negative densities // if timestep too large - output_warn.write("WARNING: BoutException thrown: {}\n", e.what()); - - // Tell SNES that the input was out of domain - SNESSetFunctionDomainError(snes); - // Note: Returning non-zero error here leaves vectors in locked state - return 0; + output_error.write("BoutException thrown: {}\n", e.what()); + // There is no way to recover and synchronise MPI ranks + // unless they all threw an exception at the same point. + BoutComm::abort(1); } // Save derivatives to PETSc diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 446903dd4f..564ff6f33b 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -1108,7 +1108,10 @@ int SNESSolver::run() { run_rhs(simtime); } catch (BoutException& e) { output_error.write("ERROR: BoutException thrown: {}\n", e.what()); - return 1; + // Abort simulation. There is no way to recover and + // synchronise unless all processors throw exceptions + // together + BoutComm::abort(1); } // Copy derivatives back @@ -1262,6 +1265,9 @@ int SNESSolver::run() { run_rhs(target); // Run RHS to calculate auxilliary variables } catch (BoutException& e) { output_error.write("ERROR: BoutException thrown: {}\n", e.what()); + // Abort simulation. There is no way to recover unless + // all processors throw an exception at the same point. + BoutComm::abort(1); } if (call_monitors(target, s, getNumberOutputSteps()) != 0) { @@ -1538,7 +1544,9 @@ PetscErrorCode SNESSolver::rhs_function(Vec x, Vec f, bool linear) { // Simulation might fail, e.g. negative densities // if timestep too large output_warn.write("WARNING: BoutException thrown: {}\n", e.what()); - return 2; + // Abort simulation. Unless all processors threw an exception + // at the same point, there is no way to synchronise again. + BoutComm::abort(2); } // Copy derivatives back diff --git a/src/sys/boutcomm.cxx b/src/sys/boutcomm.cxx index 2d78a24076..d85a64501e 100644 --- a/src/sys/boutcomm.cxx +++ b/src/sys/boutcomm.cxx @@ -58,6 +58,10 @@ int BoutComm::size() { return NPES; } +void BoutComm::abort(int errorcode) { + MPI_Abort(get(), errorcode); +} + BoutComm* BoutComm::getInstance() { if (instance == nullptr) { // Create the singleton object From 5f0645294ca49db6a934871103e0586de00c3fdf Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Fri, 16 Jan 2026 00:01:54 +0000 Subject: [PATCH 155/461] Apply clang-format changes --- src/sys/boutcomm.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/sys/boutcomm.cxx b/src/sys/boutcomm.cxx index d85a64501e..358f2346f9 100644 --- a/src/sys/boutcomm.cxx +++ b/src/sys/boutcomm.cxx @@ -58,9 +58,7 @@ int BoutComm::size() { return NPES; } -void BoutComm::abort(int errorcode) { - MPI_Abort(get(), errorcode); -} +void BoutComm::abort(int errorcode) { MPI_Abort(get(), errorcode); } BoutComm* BoutComm::getInstance() { if (instance == nullptr) { From 52299eaf8a6e76a88bbe5329aae67af9b2382634 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 10:25:41 +0000 Subject: [PATCH 156/461] docs: Fix building `boutpp` docs on readthedocs --- manual/sphinx/conf.py | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 5c6b7ef1d5..6f2e12f349 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -78,8 +78,8 @@ def __getattr__(cls, name): os.system("which clang-format-6.0") subprocess.run("git submodule update --init --recursive", shell=True) cmake = ( - "cmake . " - " -B build" + "cmake -S ../.. " + " -B bout_build" " -DBOUT_USE_FFTW=ON" " -DBOUT_USE_LAPACK=OFF" " -DBOUT_ENABLE_PYTHON=ON" @@ -89,8 +89,12 @@ def __getattr__(cls, name): f" -DPython3_ROOT_DIR={pydir}" ) subprocess.run(f"echo {cmake}", shell=True) - subprocess.run(f"cd ../../; {cmake}", shell=True, check=True) - subprocess.run("cd ../.. ; cmake --build build -j 2", shell=True, check=True) + subprocess.run(f"{cmake}", shell=True, check=True) + subprocess.run("cmake --build bout_build", shell=True, check=True) + + # Add the build directory to sys.path so that sphinx picks up the built + # Python modules + sys.path.append("bout_build/tools/pylib") # readthedocs currently runs out of memory if we actually dare to try to do this From 60546f61ecd25c825862e08a4f18c568c56b89e4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 10:26:16 +0000 Subject: [PATCH 157/461] docs: Exclude CMake-built dependencies from docs --- manual/doxygen/Doxyfile | 3 ++- manual/doxygen/Doxyfile_readthedocs | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/manual/doxygen/Doxyfile b/manual/doxygen/Doxyfile index 82cfa5a8a4..973c5fce58 100644 --- a/manual/doxygen/Doxyfile +++ b/manual/doxygen/Doxyfile @@ -840,7 +840,8 @@ EXCLUDE = ../../examples \ ../../manual/ \ ../../bin/ \ ../../externalpackages/googletest/ \ - ../../tests/ + ../../tests/ \ + ../../build/_deps/ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/manual/doxygen/Doxyfile_readthedocs b/manual/doxygen/Doxyfile_readthedocs index 0ddca11de0..08de92fc79 100644 --- a/manual/doxygen/Doxyfile_readthedocs +++ b/manual/doxygen/Doxyfile_readthedocs @@ -790,7 +790,8 @@ EXCLUDE = ../../examples \ ../../manual/ \ ../../bin/ \ ../../externalpackages/googletest/ \ - ../../tests/ + ../../tests/ \ + ../sphinx/bout_build/_deps/ # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded From f9cb4d8a4c9b31ced214ed7a19d1a7fd9db948c9 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 10:26:37 +0000 Subject: [PATCH 158/461] docs: Use builtin path to Python to set location for CMake --- manual/sphinx/conf.py | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 6f2e12f349..98b0f60d06 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -72,8 +72,6 @@ def __getattr__(cls, name): sys.modules.update((mod_name, Mock()) for mod_name in MOCK_MODULES) print(os.environ) print(sys.argv) - python = sys.argv[0] - pydir = "/".join(python.split("/")[:-2]) os.system("which clang-format") os.system("which clang-format-6.0") subprocess.run("git submodule update --init --recursive", shell=True) @@ -86,7 +84,7 @@ def __getattr__(cls, name): " -DBOUT_UPDATE_GIT_SUBMODULE=OFF" " -DBOUT_TESTS=OFF" " -DBOUT_ALLOW_INSOURCE_BUILD=ON" - f" -DPython3_ROOT_DIR={pydir}" + f" -DPython3_ROOT_DIR={sys.exec_prefix}" ) subprocess.run(f"echo {cmake}", shell=True) subprocess.run(f"{cmake}", shell=True, check=True) From 3252e41690bc0b97b27feb3193220b8ca4e24638 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 10:27:36 +0000 Subject: [PATCH 159/461] docs: Remove outdated comment --- manual/sphinx/conf.py | 1 - 1 file changed, 1 deletion(-) diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 98b0f60d06..6046173aef 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -95,7 +95,6 @@ def __getattr__(cls, name): sys.path.append("bout_build/tools/pylib") -# readthedocs currently runs out of memory if we actually dare to try to do this if has_breathe: # Run doxygen to generate the XML sources if on_readthedocs: From 876fe2c0e77788eae2f58082a80ba7fe42228605 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Mon, 19 Jan 2026 10:28:55 +0000 Subject: [PATCH 160/461] Apply black changes --- bin/bout-add-mod-path | 12 +++---- bin/bout-pylib-cmd-to-bin | 31 ++++++------------- bin/bout-v5-factory-upgrader.py | 21 +++---------- bin/bout-v5-format-upgrader.py | 1 - bin/bout-v5-header-upgrader.py | 7 ++--- bin/bout-v5-input-file-upgrader.py | 6 ++-- bin/bout-v5-macro-upgrader.py | 6 ++-- bin/bout-v5-physics-model-upgrader.py | 13 +++----- bin/bout-v5-xzinterpolation-upgrader.py | 12 ++----- bin/update_version_number_in_files.py | 6 ++-- src/field/gen_fieldops.py | 1 - tests/MMS/GBS/runtest-slab2d | 1 - tests/MMS/GBS/runtest-slab3d | 1 - tests/MMS/advection/runtest | 1 - tests/MMS/diffusion/runtest | 1 - tests/MMS/diffusion2/Z/plot_error.py | 1 + tests/MMS/diffusion2/runtest | 1 - tests/MMS/fieldalign/runtest.broken | 1 - tests/MMS/hw/runtest | 1 - tests/MMS/laplace/runtest | 1 - tests/MMS/spatial/diffusion/runtest | 1 - tests/MMS/wave-1d-y/runtest | 1 - tests/MMS/wave-1d/runtest | 1 - tests/integrated/test-command-args/runtest | 1 - tests/integrated/test-cyclic/runtest | 1 - tests/integrated/test-griddata/runtest | 1 - tests/integrated/test-gyro/runtest | 1 - tests/integrated/test-invpar/runtest | 1 - tests/integrated/test-laplace/runtest | 1 - .../integrated/test-multigrid_laplace/runtest | 1 - .../runtest_multiple_grids | 1 - .../test-multigrid_laplace/runtest_unsheared | 1 - tests/integrated/test-naulin-laplace/runtest | 1 - .../runtest_multiple_grids | 1 - .../test-naulin-laplace/runtest_unsheared | 1 - tests/integrated/test-petsc_laplace/runtest | 1 - .../test-petsc_laplace_MAST-grid/runtest | 1 - tests/integrated/test-region-iterator/runtest | 1 - tests/integrated/test-restarting/runtest | 1 - tests/integrated/test-smooth/runtest | 1 - tests/integrated/test-squash/runtest | 1 - tests/integrated/test-yupdown/runtest | 1 - tools/pylib/_boutpp_build/backend.py | 18 ++++------- 43 files changed, 41 insertions(+), 124 deletions(-) diff --git a/bin/bout-add-mod-path b/bin/bout-add-mod-path index 9faf1be3de..ee7ea66d25 100755 --- a/bin/bout-add-mod-path +++ b/bin/bout-add-mod-path @@ -104,8 +104,7 @@ def create_mod(modulepath, name, top, build): else: prereq = "" with open(filename, "w") as f: - f.write( - f"""#%Module 1.0 + f.write(f"""#%Module 1.0 # # BOUT++ module for use with 'environment-modules' package # Created by bout-add-mod-path v0.9 @@ -119,17 +118,14 @@ setenv BOUT_TOP {top} prepend-path PATH {top}/bin prepend-path PYTHONPATH {top}/tools/pylib prepend-path IDL_PATH +{top}/tools/idllib:'' -""" - ) +""") if build != top: - f.write( - f"""#%Module 1.0 + f.write(f"""#%Module 1.0 setenv BOUT_BUILD {build} prepend-path PATH {build}/bin prepend-path LD_LIBRARY_PATH {build}/lib prepend-path PYTHONPATH {build}/tools/pylib -""" - ) +""") print(f"created `{filename}`") diff --git a/bin/bout-pylib-cmd-to-bin b/bin/bout-pylib-cmd-to-bin index 8f88a5dbf4..8d6dbed7ae 100755 --- a/bin/bout-pylib-cmd-to-bin +++ b/bin/bout-pylib-cmd-to-bin @@ -126,8 +126,7 @@ def createwrapper(mod, func_name, func, name): out += end f.write(out) - fprint( - """#!/usr/bin/env python3 + fprint("""#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK import argparse @@ -136,8 +135,7 @@ try: except ImportError: argcomplete=None -""" - ) +""") doc = True para = False ret = False @@ -183,19 +181,16 @@ except ImportError: arg_help[curarg].append(esc(blas)) # Print functions that are needed if "str_to_slice" in arg_type.values(): - fprint( - """ + fprint(""" def str_to_slice(sstr): args=[] for s in sstr.split(','): args.append(int(s)) print(args) return slice(*args) -""" - ) +""") if "str_to_bool" in arg_type.values(): - fprint( - """ + fprint(""" def str_to_bool(sstr): low=sstr.lower() # no or false @@ -206,8 +201,7 @@ def str_to_bool(sstr): return True else: raise ArgumentTypeError("Cannot parse %s to bool type"%sstr) -""" - ) +""") # Create the parser fprint("parser = argparse.ArgumentParser(%s)" % (esc(docs))) spec = inspect.signature(func) @@ -247,24 +241,19 @@ def str_to_bool(sstr): pre = "\n " fprint(")") - fprint( - """ + fprint(""" if argcomplete: argcomplete.autocomplete(parser) -# late import for faster auto-complete""" - ) +# late import for faster auto-complete""") fprint("from %s import %s" % (mod, func_name)) - fprint( - """ + fprint(""" args = parser.parse_args() # Call the function %s, using command line arguments %s(**args.__dict__) -""" - % (func_name, func_name) - ) +""" % (func_name, func_name)) # alternative, but I think 0o755 is easier to read # import stat # os.chmod(filename,stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH) diff --git a/bin/bout-v5-factory-upgrader.py b/bin/bout-v5-factory-upgrader.py index 29fc07db30..ee24572a94 100755 --- a/bin/bout-v5-factory-upgrader.py +++ b/bin/bout-v5-factory-upgrader.py @@ -5,7 +5,6 @@ import difflib import re - # Dictionary of factory methods that may need updating factories = { "Interpolation": { @@ -62,9 +61,7 @@ def find_factory_calls(factory, source): \s*=\s* {factory_name}:: .*{create_method}.* - """.format( - **factory - ), + """.format(**factory), source, re.VERBOSE, ) @@ -75,9 +72,7 @@ def find_type_pointers(factory, source): r""" \b{type_name}\s*\*\s* # Type name and pointer ([\w_]+)\s*; # Variable name - """.format( - **factory - ), + """.format(**factory), source, re.VERBOSE, ) @@ -107,9 +102,7 @@ def fix_declarations(factory, variables, source): (.*?)(class\s*)? # optional "class" keyword \b({type_name})\s*\*\s* # Type-pointer ({variable_name})\s*; # Variable - """.format( - type_name=factory["type_name"], variable_name=variable - ), + """.format(type_name=factory["type_name"], variable_name=variable), r"\1std::unique_ptr<\3> \4{nullptr};", source, flags=re.VERBOSE, @@ -123,9 +116,7 @@ def fix_declarations(factory, variables, source): ({variable_name})\s* # Variable =\s* # Assignment from factory ({factory_name}::.*{create_method}.*); - """.format( - variable_name=variable, **factory - ), + """.format(variable_name=variable, **factory), r"\1auto \4 = \5;", source, flags=re.VERBOSE, @@ -139,9 +130,7 @@ def fix_declarations(factory, variables, source): ({variable_name})\s* # Variable =\s* # Assignment (0|nullptr|NULL); - """.format( - variable_name=variable, **factory - ), + """.format(variable_name=variable, **factory), r"\1std::unique_ptr<\2> \3{nullptr};", source, flags=re.VERBOSE, diff --git a/bin/bout-v5-format-upgrader.py b/bin/bout-v5-format-upgrader.py index 7c7d13ac7f..a534ca240a 100755 --- a/bin/bout-v5-format-upgrader.py +++ b/bin/bout-v5-format-upgrader.py @@ -5,7 +5,6 @@ import difflib import re - format_replacements = { "c": "c", "d": "d", diff --git a/bin/bout-v5-header-upgrader.py b/bin/bout-v5-header-upgrader.py index 49a8fbcbe4..77794ab920 100755 --- a/bin/bout-v5-header-upgrader.py +++ b/bin/bout-v5-header-upgrader.py @@ -9,7 +9,6 @@ from typing import List from subprocess import run - header_shim_sentinel = "// BOUT++ header shim" header_warning = f"""\ @@ -122,8 +121,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix deprecated header locations for BOUT++ v4 -> v5 All BOUT++ headers are now under ``include/bout`` and @@ -142,8 +140,7 @@ def create_patch(filename, original, modified): If you have staged changes, this tool will not work, so to avoid committing undesired or unrelated changes. - """ - ), + """), ) parser.add_argument( diff --git a/bin/bout-v5-input-file-upgrader.py b/bin/bout-v5-input-file-upgrader.py index e2940ff58a..ea979005d5 100755 --- a/bin/bout-v5-input-file-upgrader.py +++ b/bin/bout-v5-input-file-upgrader.py @@ -271,8 +271,7 @@ def possibly_apply_patch(patch, options_file, quiet=False, force=False): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix input files for BOUT++ v5+ Please note that this will only fix input options in sections with @@ -300,8 +299,7 @@ def possibly_apply_patch(patch, options_file, quiet=False, force=False): Files that change in this way will have the "canonicalisation" patch presented first. If you choose not to apply this patch, the "upgrade - fixer" patch will still include it.""" - ), + fixer" patch will still include it."""), ) parser.add_argument("files", action="store", nargs="+", help="Input files") diff --git a/bin/bout-v5-macro-upgrader.py b/bin/bout-v5-macro-upgrader.py index 11b4926255..d644fed9e8 100755 --- a/bin/bout-v5-macro-upgrader.py +++ b/bin/bout-v5-macro-upgrader.py @@ -342,8 +342,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix macro defines for BOUT++ v4 -> v5 Please note that this is only slightly better than dumb text replacement. It @@ -359,8 +358,7 @@ def create_patch(filename, original, modified): still replace them in strings or comments. Please check the diff output carefully! - """ - ), + """), ) parser.add_argument("files", action="store", nargs="+", help="Input files") diff --git a/bin/bout-v5-physics-model-upgrader.py b/bin/bout-v5-physics-model-upgrader.py index 26fb8ef6e0..260d1d59ee 100755 --- a/bin/bout-v5-physics-model-upgrader.py +++ b/bin/bout-v5-physics-model-upgrader.py @@ -8,7 +8,6 @@ import textwrap import warnings - PHYSICS_MODEL_INCLUDE = '#include "bout/physicsmodel.hxx"' PHYSICS_MODEL_SKELETON = """ @@ -213,13 +212,11 @@ def fix_bout_constrain(source, error_on_warning): "\n ".join(["{}:{}".format(i, source_lines[i]) for i in line_range]) ) - message = textwrap.dedent( - """\ + message = textwrap.dedent("""\ Some uses of `bout_constrain` remain, but we could not automatically convert them to use `Solver::constraint`. Please fix them before continuing: - """ - ) + """) message += " " + "\n ".join(lines_context) if error_on_warning: @@ -389,8 +386,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Upgrade legacy physics models to use the PhysicsModel class This will do the bare minimum required to compile, and @@ -403,8 +399,7 @@ def create_patch(filename, original, modified): By default, this will use the file name stripped of file extensions as the name of the new class. Use '--name=' to give a different name. - """ - ), + """), ) parser.add_argument("files", action="store", nargs="+", help="Files to fix") diff --git a/bin/bout-v5-xzinterpolation-upgrader.py b/bin/bout-v5-xzinterpolation-upgrader.py index 37c79e0de8..e70c3c54ae 100755 --- a/bin/bout-v5-xzinterpolation-upgrader.py +++ b/bin/bout-v5-xzinterpolation-upgrader.py @@ -54,9 +54,7 @@ def fix_header_includes(old_header, new_header, source): (<|") ({header}) # Header name (>|") - """.format( - header=old_header - ), + """.format(header=old_header), r"\1\2{header}\4".format(header=new_header), source, flags=re.VERBOSE, @@ -67,9 +65,7 @@ def fix_interpolations(old_interpolation, new_interpolation, source): return re.sub( r""" \b{}\b - """.format( - old_interpolation - ), + """.format(old_interpolation), r"{}".format(new_interpolation), source, flags=re.VERBOSE, @@ -120,9 +116,7 @@ def fix_factories(old_factory, new_factory, source): return re.sub( r""" \b{}\b - """.format( - old_factory - ), + """.format(old_factory), r"{}".format(new_factory), source, flags=re.VERBOSE, diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index ec9a31bc32..3e9758a8c1 100755 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -158,8 +158,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Update the software version number to the specified version, to be given in the form major.minor.patch, e.g. 5.10.3 @@ -172,8 +171,7 @@ def create_patch(filename, original, modified): the 'minor' version number of the provided version will be incremented by 1, e.g. 5.10.3 -> 5.11.3 - """ - ), + """), ) parser.add_argument( diff --git a/src/field/gen_fieldops.py b/src/field/gen_fieldops.py index 29631ff7aa..3e07d6fec4 100755 --- a/src/field/gen_fieldops.py +++ b/src/field/gen_fieldops.py @@ -11,7 +11,6 @@ """ - from __future__ import print_function from builtins import object diff --git a/tests/MMS/GBS/runtest-slab2d b/tests/MMS/GBS/runtest-slab2d index 97c7e11459..221c34e815 100755 --- a/tests/MMS/GBS/runtest-slab2d +++ b/tests/MMS/GBS/runtest-slab2d @@ -11,7 +11,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("MMS test") diff --git a/tests/MMS/GBS/runtest-slab3d b/tests/MMS/GBS/runtest-slab3d index f193583e47..45acd53af7 100755 --- a/tests/MMS/GBS/runtest-slab3d +++ b/tests/MMS/GBS/runtest-slab3d @@ -14,7 +14,6 @@ from numpy import sqrt, max, abs, mean, array, log, concatenate import pickle - build_and_log("MMS test") # List of NX values to use diff --git a/tests/MMS/advection/runtest b/tests/MMS/advection/runtest index 1290a4c247..9a0a691287 100755 --- a/tests/MMS/advection/runtest +++ b/tests/MMS/advection/runtest @@ -20,7 +20,6 @@ import pickle import time - if __name__ == "__main__": build_and_log("MMS advection") diff --git a/tests/MMS/diffusion/runtest b/tests/MMS/diffusion/runtest index a25b3519a6..ef6b335f70 100755 --- a/tests/MMS/diffusion/runtest +++ b/tests/MMS/diffusion/runtest @@ -15,7 +15,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log - build_and_log("MMS diffusion test") # List of NX values to use diff --git a/tests/MMS/diffusion2/Z/plot_error.py b/tests/MMS/diffusion2/Z/plot_error.py index 5c4e1164c1..4b4b666758 100644 --- a/tests/MMS/diffusion2/Z/plot_error.py +++ b/tests/MMS/diffusion2/Z/plot_error.py @@ -4,6 +4,7 @@ @author: yolen """ + import boutdata import matplotlib.pyplot as plt diff --git a/tests/MMS/diffusion2/runtest b/tests/MMS/diffusion2/runtest index 546be3b2c1..d1b8072fc8 100755 --- a/tests/MMS/diffusion2/runtest +++ b/tests/MMS/diffusion2/runtest @@ -20,7 +20,6 @@ from numpy import sqrt, max, abs, mean, array, log from os.path import join - build_and_log("MMS diffusion test") # List of input directories diff --git a/tests/MMS/fieldalign/runtest.broken b/tests/MMS/fieldalign/runtest.broken index e09fbeccd5..bfb2023c1d 100755 --- a/tests/MMS/fieldalign/runtest.broken +++ b/tests/MMS/fieldalign/runtest.broken @@ -18,7 +18,6 @@ from os.path import join import time - build_and_log("MMS test") # nxlist = [256, 128, 64, 32, 16, 8] # do in reverse order to save disk space diff --git a/tests/MMS/hw/runtest b/tests/MMS/hw/runtest index b6abadc3b9..62740fcbbd 100755 --- a/tests/MMS/hw/runtest +++ b/tests/MMS/hw/runtest @@ -15,7 +15,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("MMS test") # List of NX values to use diff --git a/tests/MMS/laplace/runtest b/tests/MMS/laplace/runtest index 98eb62da87..80c8bf08af 100755 --- a/tests/MMS/laplace/runtest +++ b/tests/MMS/laplace/runtest @@ -14,7 +14,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("MMS test") # List of NX values to use diff --git a/tests/MMS/spatial/diffusion/runtest b/tests/MMS/spatial/diffusion/runtest index 000cf8f09b..8d4e759d59 100755 --- a/tests/MMS/spatial/diffusion/runtest +++ b/tests/MMS/spatial/diffusion/runtest @@ -22,7 +22,6 @@ from os.path import join import matplotlib.pyplot as plt - build_and_log("MMS diffusion test") # List of input directories diff --git a/tests/MMS/wave-1d-y/runtest b/tests/MMS/wave-1d-y/runtest index 9a4de5a3bb..332d6864b0 100755 --- a/tests/MMS/wave-1d-y/runtest +++ b/tests/MMS/wave-1d-y/runtest @@ -20,7 +20,6 @@ from sys import stdout from numpy import sqrt, max, abs, mean, array, log, concatenate, pi - build_and_log("Making MMS wave test") # List of NX values to use diff --git a/tests/MMS/wave-1d/runtest b/tests/MMS/wave-1d/runtest index f13fb7bd11..1adb7c7d6d 100755 --- a/tests/MMS/wave-1d/runtest +++ b/tests/MMS/wave-1d/runtest @@ -15,7 +15,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("Making MMS wave test") # List of NX values to use diff --git a/tests/integrated/test-command-args/runtest b/tests/integrated/test-command-args/runtest index 39939bb105..a8dc3f020c 100755 --- a/tests/integrated/test-command-args/runtest +++ b/tests/integrated/test-command-args/runtest @@ -7,7 +7,6 @@ import shutil import unittest import platform - OUTPUT_FILE = "stdout.log" if platform.system() == "FreeBSD" else "stderr.log" diff --git a/tests/integrated/test-cyclic/runtest b/tests/integrated/test-cyclic/runtest index 5e31da6239..c68f9b033e 100755 --- a/tests/integrated/test-cyclic/runtest +++ b/tests/integrated/test-cyclic/runtest @@ -17,7 +17,6 @@ from boututils.run_wrapper import build_and_log, shell, launch from boutdata.collect import collect from sys import stdout, exit - build_and_log("Cyclic Reduction test") flags = ["", "nsys=2", "nsys=5 periodic", "nsys=7 n=10"] diff --git a/tests/integrated/test-griddata/runtest b/tests/integrated/test-griddata/runtest index 1c57bf5be7..6aef955023 100755 --- a/tests/integrated/test-griddata/runtest +++ b/tests/integrated/test-griddata/runtest @@ -13,7 +13,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("griddata test") for nproc in [1]: diff --git a/tests/integrated/test-gyro/runtest b/tests/integrated/test-gyro/runtest index 52ab0de4ab..7a0209fa57 100755 --- a/tests/integrated/test-gyro/runtest +++ b/tests/integrated/test-gyro/runtest @@ -25,7 +25,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("Gyro-average inversion test") # Read benchmark values diff --git a/tests/integrated/test-invpar/runtest b/tests/integrated/test-invpar/runtest index de7f028528..53f6015fe9 100755 --- a/tests/integrated/test-invpar/runtest +++ b/tests/integrated/test-invpar/runtest @@ -12,7 +12,6 @@ from boututils.run_wrapper import build_and_log, shell, launch from boutdata.collect import collect from sys import stdout, exit - build_and_log("parallel inversion test") flags_src = [ diff --git a/tests/integrated/test-laplace/runtest b/tests/integrated/test-laplace/runtest index e54e46d0d8..d1e9c3d1c9 100755 --- a/tests/integrated/test-laplace/runtest +++ b/tests/integrated/test-laplace/runtest @@ -41,7 +41,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("Laplacian inversion test") # Read benchmark values diff --git a/tests/integrated/test-multigrid_laplace/runtest b/tests/integrated/test-multigrid_laplace/runtest index 4a7455f80b..f325185c46 100755 --- a/tests/integrated/test-multigrid_laplace/runtest +++ b/tests/integrated/test-multigrid_laplace/runtest @@ -22,7 +22,6 @@ from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("multigrid Laplacian inversion test") print("Running multigrid Laplacian inversion test") diff --git a/tests/integrated/test-multigrid_laplace/runtest_multiple_grids b/tests/integrated/test-multigrid_laplace/runtest_multiple_grids index 6817120b13..739d15f7d4 100755 --- a/tests/integrated/test-multigrid_laplace/runtest_multiple_grids +++ b/tests/integrated/test-multigrid_laplace/runtest_multiple_grids @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Multigrid Laplacian inversion test") print("Running multigrid Laplacian inversion test") diff --git a/tests/integrated/test-multigrid_laplace/runtest_unsheared b/tests/integrated/test-multigrid_laplace/runtest_unsheared index cda68f2167..d0dca3a808 100755 --- a/tests/integrated/test-multigrid_laplace/runtest_unsheared +++ b/tests/integrated/test-multigrid_laplace/runtest_unsheared @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Making multigrid Laplacian inversion test") print("Running multigrid Laplacian inversion test") diff --git a/tests/integrated/test-naulin-laplace/runtest b/tests/integrated/test-naulin-laplace/runtest index f972eab6cc..145257c23b 100755 --- a/tests/integrated/test-naulin-laplace/runtest +++ b/tests/integrated/test-naulin-laplace/runtest @@ -22,7 +22,6 @@ from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("LaplaceNaulin inversion test") print("Running LaplaceNaulin inversion test") diff --git a/tests/integrated/test-naulin-laplace/runtest_multiple_grids b/tests/integrated/test-naulin-laplace/runtest_multiple_grids index c0281c3a4e..f56a80677e 100755 --- a/tests/integrated/test-naulin-laplace/runtest_multiple_grids +++ b/tests/integrated/test-naulin-laplace/runtest_multiple_grids @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Making LaplaceNaulin inversion test") print("Running LaplaceNaulin inversion test") diff --git a/tests/integrated/test-naulin-laplace/runtest_unsheared b/tests/integrated/test-naulin-laplace/runtest_unsheared index 8f47f33026..a845a9d92f 100755 --- a/tests/integrated/test-naulin-laplace/runtest_unsheared +++ b/tests/integrated/test-naulin-laplace/runtest_unsheared @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("LaplaceNaulin inversion test") print("Running LaplaceNaulin inversion test") diff --git a/tests/integrated/test-petsc_laplace/runtest b/tests/integrated/test-petsc_laplace/runtest index ac248c4ce7..83e1006338 100755 --- a/tests/integrated/test-petsc_laplace/runtest +++ b/tests/integrated/test-petsc_laplace/runtest @@ -30,7 +30,6 @@ from boutdata.collect import collect # import numpy as np from sys import stdout, exit - build_and_log("PETSc Laplacian inversion test") print("Running PETSc Laplacian inversion test") diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/runtest b/tests/integrated/test-petsc_laplace_MAST-grid/runtest index 5a4cbf875a..3be39949c3 100755 --- a/tests/integrated/test-petsc_laplace_MAST-grid/runtest +++ b/tests/integrated/test-petsc_laplace_MAST-grid/runtest @@ -28,7 +28,6 @@ from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from sys import stdout, exit - build_and_log( "PETSc Laplacian inversion test with non-identity metric (taken from grid for MAST SOL)" ) diff --git a/tests/integrated/test-region-iterator/runtest b/tests/integrated/test-region-iterator/runtest index e5825285de..2277ef15e9 100755 --- a/tests/integrated/test-region-iterator/runtest +++ b/tests/integrated/test-region-iterator/runtest @@ -17,7 +17,6 @@ from boututils.run_wrapper import build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Region Iterator test") flags = [""] diff --git a/tests/integrated/test-restarting/runtest b/tests/integrated/test-restarting/runtest index 262aa818b6..daf19ebac6 100755 --- a/tests/integrated/test-restarting/runtest +++ b/tests/integrated/test-restarting/runtest @@ -5,7 +5,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("restart test") # Run once for 10 timesteps diff --git a/tests/integrated/test-smooth/runtest b/tests/integrated/test-smooth/runtest index bd7296341a..91ae793164 100755 --- a/tests/integrated/test-smooth/runtest +++ b/tests/integrated/test-smooth/runtest @@ -16,7 +16,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("smoothing operator test") # Read benchmark values diff --git a/tests/integrated/test-squash/runtest b/tests/integrated/test-squash/runtest index 2f35e00589..8bf358ee8e 100755 --- a/tests/integrated/test-squash/runtest +++ b/tests/integrated/test-squash/runtest @@ -9,7 +9,6 @@ import argparse import re import os.path - # requires: all_tests # requires: netcdf # cores: 4 diff --git a/tests/integrated/test-yupdown/runtest b/tests/integrated/test-yupdown/runtest index 34fcd36496..13d7365b53 100755 --- a/tests/integrated/test-yupdown/runtest +++ b/tests/integrated/test-yupdown/runtest @@ -6,7 +6,6 @@ from sys import exit from numpy import max, abs - build_and_log("parallel slices test") failed = False diff --git a/tools/pylib/_boutpp_build/backend.py b/tools/pylib/_boutpp_build/backend.py index 6455c203ad..9e305b3048 100755 --- a/tools/pylib/_boutpp_build/backend.py +++ b/tools/pylib/_boutpp_build/backend.py @@ -212,12 +212,10 @@ def build_sdist(sdist_directory, config_settings=None): run(f"rm {tmp}") with open(tmp, "w") as f: - f.write( - f"""Metadata-Version: 2.1 + f.write(f"""Metadata-Version: 2.1 Name: {pkgname} Version: {getversion()} -""" - ) +""") with open("LICENSE") as src: pre = "License: " for l in src: @@ -269,23 +267,19 @@ def prepare_metadata_for_build_wheel( distinfo = f"{metadata_directory}/{thisdir}" mkdir_p(distinfo) with open(f"{distinfo}/METADATA", "w") as f: - f.write( - f"""Metadata-Version: 2.1 + f.write(f"""Metadata-Version: 2.1 Name: {pkgname} Version: {getversion()} License-File: COPYING -""" - ) +""") run(f"cp LICENSE {distinfo}/COPYING") run(f"cp LICENSE.GPL {distinfo}/COPYING.GPL") with open(f"{distinfo}/WHEEL", "w") as f: - f.write( - f"""Wheel-Version: 1.0 + f.write(f"""Wheel-Version: 1.0 Generator: boutpp_custom_build_wheel ({getversion()}) Root-Is-Purelib: false Tag: {gettag()} -""" - ) +""") if record: with open(f"{distinfo}/RECORD", "w") as f: From 79d6ed0375f7d5d5cdf21f9b019503ef75e2a6f1 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 10:32:57 +0000 Subject: [PATCH 161/461] cmake: Bump netcdf-cxx4 `FetchContent` version --- cmake/SetupBOUTThirdParty.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 1256fbbeba..704b3b6a5f 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -164,8 +164,8 @@ if (BOUT_USE_NETCDF) include(FetchContent) FetchContent_Declare( netcdf-cxx4 - GIT_REPOSITORY https://github.com/ZedThree/netcdf-cxx4 - GIT_TAG "ad3e50953190615cb69dcc8a4652f9a88a8499cf" + GIT_REPOSITORY https://github.com/Unidata/netcdf-cxx4 + GIT_TAG "a43d6d4d415d407712c246faca553bd951730dc1" ) # Don't build the netcdf tests, they have lots of warnings set(NCXX_ENABLE_TESTS OFF CACHE BOOL "" FORCE) From e2f03beb97e0b3adc3694c2c111f24b4d0aa2d88 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Mon, 19 Jan 2026 10:34:26 +0000 Subject: [PATCH 162/461] Apply black changes --- bin/bout-add-mod-path | 12 +++---- bin/bout-pylib-cmd-to-bin | 31 ++++++------------- bin/bout-v5-factory-upgrader.py | 21 +++---------- bin/bout-v5-format-upgrader.py | 1 - bin/bout-v5-header-upgrader.py | 7 ++--- bin/bout-v5-input-file-upgrader.py | 6 ++-- bin/bout-v5-macro-upgrader.py | 6 ++-- bin/bout-v5-physics-model-upgrader.py | 13 +++----- bin/bout-v5-xzinterpolation-upgrader.py | 12 ++----- bin/update_version_number_in_files.py | 6 ++-- src/field/gen_fieldops.py | 1 - tests/MMS/GBS/runtest-slab2d | 1 - tests/MMS/GBS/runtest-slab3d | 1 - tests/MMS/advection/runtest | 1 - tests/MMS/diffusion/runtest | 1 - tests/MMS/diffusion2/Z/plot_error.py | 1 + tests/MMS/diffusion2/runtest | 1 - tests/MMS/fieldalign/runtest.broken | 1 - tests/MMS/hw/runtest | 1 - tests/MMS/laplace/runtest | 1 - tests/MMS/spatial/diffusion/runtest | 1 - tests/MMS/wave-1d-y/runtest | 1 - tests/MMS/wave-1d/runtest | 1 - tests/integrated/test-command-args/runtest | 1 - tests/integrated/test-cyclic/runtest | 1 - tests/integrated/test-griddata/runtest | 1 - tests/integrated/test-gyro/runtest | 1 - tests/integrated/test-invpar/runtest | 1 - tests/integrated/test-laplace/runtest | 1 - .../integrated/test-multigrid_laplace/runtest | 1 - .../runtest_multiple_grids | 1 - .../test-multigrid_laplace/runtest_unsheared | 1 - tests/integrated/test-naulin-laplace/runtest | 1 - .../runtest_multiple_grids | 1 - .../test-naulin-laplace/runtest_unsheared | 1 - tests/integrated/test-petsc_laplace/runtest | 1 - .../test-petsc_laplace_MAST-grid/runtest | 1 - tests/integrated/test-region-iterator/runtest | 1 - tests/integrated/test-restarting/runtest | 1 - tests/integrated/test-smooth/runtest | 1 - tests/integrated/test-squash/runtest | 1 - tests/integrated/test-yupdown/runtest | 1 - tools/pylib/_boutpp_build/backend.py | 18 ++++------- 43 files changed, 41 insertions(+), 124 deletions(-) diff --git a/bin/bout-add-mod-path b/bin/bout-add-mod-path index 9faf1be3de..ee7ea66d25 100755 --- a/bin/bout-add-mod-path +++ b/bin/bout-add-mod-path @@ -104,8 +104,7 @@ def create_mod(modulepath, name, top, build): else: prereq = "" with open(filename, "w") as f: - f.write( - f"""#%Module 1.0 + f.write(f"""#%Module 1.0 # # BOUT++ module for use with 'environment-modules' package # Created by bout-add-mod-path v0.9 @@ -119,17 +118,14 @@ setenv BOUT_TOP {top} prepend-path PATH {top}/bin prepend-path PYTHONPATH {top}/tools/pylib prepend-path IDL_PATH +{top}/tools/idllib:'' -""" - ) +""") if build != top: - f.write( - f"""#%Module 1.0 + f.write(f"""#%Module 1.0 setenv BOUT_BUILD {build} prepend-path PATH {build}/bin prepend-path LD_LIBRARY_PATH {build}/lib prepend-path PYTHONPATH {build}/tools/pylib -""" - ) +""") print(f"created `{filename}`") diff --git a/bin/bout-pylib-cmd-to-bin b/bin/bout-pylib-cmd-to-bin index 8f88a5dbf4..8d6dbed7ae 100755 --- a/bin/bout-pylib-cmd-to-bin +++ b/bin/bout-pylib-cmd-to-bin @@ -126,8 +126,7 @@ def createwrapper(mod, func_name, func, name): out += end f.write(out) - fprint( - """#!/usr/bin/env python3 + fprint("""#!/usr/bin/env python3 # PYTHON_ARGCOMPLETE_OK import argparse @@ -136,8 +135,7 @@ try: except ImportError: argcomplete=None -""" - ) +""") doc = True para = False ret = False @@ -183,19 +181,16 @@ except ImportError: arg_help[curarg].append(esc(blas)) # Print functions that are needed if "str_to_slice" in arg_type.values(): - fprint( - """ + fprint(""" def str_to_slice(sstr): args=[] for s in sstr.split(','): args.append(int(s)) print(args) return slice(*args) -""" - ) +""") if "str_to_bool" in arg_type.values(): - fprint( - """ + fprint(""" def str_to_bool(sstr): low=sstr.lower() # no or false @@ -206,8 +201,7 @@ def str_to_bool(sstr): return True else: raise ArgumentTypeError("Cannot parse %s to bool type"%sstr) -""" - ) +""") # Create the parser fprint("parser = argparse.ArgumentParser(%s)" % (esc(docs))) spec = inspect.signature(func) @@ -247,24 +241,19 @@ def str_to_bool(sstr): pre = "\n " fprint(")") - fprint( - """ + fprint(""" if argcomplete: argcomplete.autocomplete(parser) -# late import for faster auto-complete""" - ) +# late import for faster auto-complete""") fprint("from %s import %s" % (mod, func_name)) - fprint( - """ + fprint(""" args = parser.parse_args() # Call the function %s, using command line arguments %s(**args.__dict__) -""" - % (func_name, func_name) - ) +""" % (func_name, func_name)) # alternative, but I think 0o755 is easier to read # import stat # os.chmod(filename,stat.S_IRWXU|stat.S_IRGRP|stat.S_IXGRP|stat.S_IROTH|stat.S_IXOTH) diff --git a/bin/bout-v5-factory-upgrader.py b/bin/bout-v5-factory-upgrader.py index 29fc07db30..ee24572a94 100755 --- a/bin/bout-v5-factory-upgrader.py +++ b/bin/bout-v5-factory-upgrader.py @@ -5,7 +5,6 @@ import difflib import re - # Dictionary of factory methods that may need updating factories = { "Interpolation": { @@ -62,9 +61,7 @@ def find_factory_calls(factory, source): \s*=\s* {factory_name}:: .*{create_method}.* - """.format( - **factory - ), + """.format(**factory), source, re.VERBOSE, ) @@ -75,9 +72,7 @@ def find_type_pointers(factory, source): r""" \b{type_name}\s*\*\s* # Type name and pointer ([\w_]+)\s*; # Variable name - """.format( - **factory - ), + """.format(**factory), source, re.VERBOSE, ) @@ -107,9 +102,7 @@ def fix_declarations(factory, variables, source): (.*?)(class\s*)? # optional "class" keyword \b({type_name})\s*\*\s* # Type-pointer ({variable_name})\s*; # Variable - """.format( - type_name=factory["type_name"], variable_name=variable - ), + """.format(type_name=factory["type_name"], variable_name=variable), r"\1std::unique_ptr<\3> \4{nullptr};", source, flags=re.VERBOSE, @@ -123,9 +116,7 @@ def fix_declarations(factory, variables, source): ({variable_name})\s* # Variable =\s* # Assignment from factory ({factory_name}::.*{create_method}.*); - """.format( - variable_name=variable, **factory - ), + """.format(variable_name=variable, **factory), r"\1auto \4 = \5;", source, flags=re.VERBOSE, @@ -139,9 +130,7 @@ def fix_declarations(factory, variables, source): ({variable_name})\s* # Variable =\s* # Assignment (0|nullptr|NULL); - """.format( - variable_name=variable, **factory - ), + """.format(variable_name=variable, **factory), r"\1std::unique_ptr<\2> \3{nullptr};", source, flags=re.VERBOSE, diff --git a/bin/bout-v5-format-upgrader.py b/bin/bout-v5-format-upgrader.py index 7c7d13ac7f..a534ca240a 100755 --- a/bin/bout-v5-format-upgrader.py +++ b/bin/bout-v5-format-upgrader.py @@ -5,7 +5,6 @@ import difflib import re - format_replacements = { "c": "c", "d": "d", diff --git a/bin/bout-v5-header-upgrader.py b/bin/bout-v5-header-upgrader.py index 49a8fbcbe4..77794ab920 100755 --- a/bin/bout-v5-header-upgrader.py +++ b/bin/bout-v5-header-upgrader.py @@ -9,7 +9,6 @@ from typing import List from subprocess import run - header_shim_sentinel = "// BOUT++ header shim" header_warning = f"""\ @@ -122,8 +121,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix deprecated header locations for BOUT++ v4 -> v5 All BOUT++ headers are now under ``include/bout`` and @@ -142,8 +140,7 @@ def create_patch(filename, original, modified): If you have staged changes, this tool will not work, so to avoid committing undesired or unrelated changes. - """ - ), + """), ) parser.add_argument( diff --git a/bin/bout-v5-input-file-upgrader.py b/bin/bout-v5-input-file-upgrader.py index e2940ff58a..ea979005d5 100755 --- a/bin/bout-v5-input-file-upgrader.py +++ b/bin/bout-v5-input-file-upgrader.py @@ -271,8 +271,7 @@ def possibly_apply_patch(patch, options_file, quiet=False, force=False): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix input files for BOUT++ v5+ Please note that this will only fix input options in sections with @@ -300,8 +299,7 @@ def possibly_apply_patch(patch, options_file, quiet=False, force=False): Files that change in this way will have the "canonicalisation" patch presented first. If you choose not to apply this patch, the "upgrade - fixer" patch will still include it.""" - ), + fixer" patch will still include it."""), ) parser.add_argument("files", action="store", nargs="+", help="Input files") diff --git a/bin/bout-v5-macro-upgrader.py b/bin/bout-v5-macro-upgrader.py index 11b4926255..d644fed9e8 100755 --- a/bin/bout-v5-macro-upgrader.py +++ b/bin/bout-v5-macro-upgrader.py @@ -342,8 +342,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Fix macro defines for BOUT++ v4 -> v5 Please note that this is only slightly better than dumb text replacement. It @@ -359,8 +358,7 @@ def create_patch(filename, original, modified): still replace them in strings or comments. Please check the diff output carefully! - """ - ), + """), ) parser.add_argument("files", action="store", nargs="+", help="Input files") diff --git a/bin/bout-v5-physics-model-upgrader.py b/bin/bout-v5-physics-model-upgrader.py index 26fb8ef6e0..260d1d59ee 100755 --- a/bin/bout-v5-physics-model-upgrader.py +++ b/bin/bout-v5-physics-model-upgrader.py @@ -8,7 +8,6 @@ import textwrap import warnings - PHYSICS_MODEL_INCLUDE = '#include "bout/physicsmodel.hxx"' PHYSICS_MODEL_SKELETON = """ @@ -213,13 +212,11 @@ def fix_bout_constrain(source, error_on_warning): "\n ".join(["{}:{}".format(i, source_lines[i]) for i in line_range]) ) - message = textwrap.dedent( - """\ + message = textwrap.dedent("""\ Some uses of `bout_constrain` remain, but we could not automatically convert them to use `Solver::constraint`. Please fix them before continuing: - """ - ) + """) message += " " + "\n ".join(lines_context) if error_on_warning: @@ -389,8 +386,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Upgrade legacy physics models to use the PhysicsModel class This will do the bare minimum required to compile, and @@ -403,8 +399,7 @@ def create_patch(filename, original, modified): By default, this will use the file name stripped of file extensions as the name of the new class. Use '--name=' to give a different name. - """ - ), + """), ) parser.add_argument("files", action="store", nargs="+", help="Files to fix") diff --git a/bin/bout-v5-xzinterpolation-upgrader.py b/bin/bout-v5-xzinterpolation-upgrader.py index 37c79e0de8..e70c3c54ae 100755 --- a/bin/bout-v5-xzinterpolation-upgrader.py +++ b/bin/bout-v5-xzinterpolation-upgrader.py @@ -54,9 +54,7 @@ def fix_header_includes(old_header, new_header, source): (<|") ({header}) # Header name (>|") - """.format( - header=old_header - ), + """.format(header=old_header), r"\1\2{header}\4".format(header=new_header), source, flags=re.VERBOSE, @@ -67,9 +65,7 @@ def fix_interpolations(old_interpolation, new_interpolation, source): return re.sub( r""" \b{}\b - """.format( - old_interpolation - ), + """.format(old_interpolation), r"{}".format(new_interpolation), source, flags=re.VERBOSE, @@ -120,9 +116,7 @@ def fix_factories(old_factory, new_factory, source): return re.sub( r""" \b{}\b - """.format( - old_factory - ), + """.format(old_factory), r"{}".format(new_factory), source, flags=re.VERBOSE, diff --git a/bin/update_version_number_in_files.py b/bin/update_version_number_in_files.py index ec9a31bc32..3e9758a8c1 100755 --- a/bin/update_version_number_in_files.py +++ b/bin/update_version_number_in_files.py @@ -158,8 +158,7 @@ def create_patch(filename, original, modified): if __name__ == "__main__": parser = argparse.ArgumentParser( formatter_class=argparse.RawDescriptionHelpFormatter, - description=textwrap.dedent( - """\ + description=textwrap.dedent("""\ Update the software version number to the specified version, to be given in the form major.minor.patch, e.g. 5.10.3 @@ -172,8 +171,7 @@ def create_patch(filename, original, modified): the 'minor' version number of the provided version will be incremented by 1, e.g. 5.10.3 -> 5.11.3 - """ - ), + """), ) parser.add_argument( diff --git a/src/field/gen_fieldops.py b/src/field/gen_fieldops.py index 29631ff7aa..3e07d6fec4 100755 --- a/src/field/gen_fieldops.py +++ b/src/field/gen_fieldops.py @@ -11,7 +11,6 @@ """ - from __future__ import print_function from builtins import object diff --git a/tests/MMS/GBS/runtest-slab2d b/tests/MMS/GBS/runtest-slab2d index 97c7e11459..221c34e815 100755 --- a/tests/MMS/GBS/runtest-slab2d +++ b/tests/MMS/GBS/runtest-slab2d @@ -11,7 +11,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("MMS test") diff --git a/tests/MMS/GBS/runtest-slab3d b/tests/MMS/GBS/runtest-slab3d index f193583e47..45acd53af7 100755 --- a/tests/MMS/GBS/runtest-slab3d +++ b/tests/MMS/GBS/runtest-slab3d @@ -14,7 +14,6 @@ from numpy import sqrt, max, abs, mean, array, log, concatenate import pickle - build_and_log("MMS test") # List of NX values to use diff --git a/tests/MMS/advection/runtest b/tests/MMS/advection/runtest index 1290a4c247..9a0a691287 100755 --- a/tests/MMS/advection/runtest +++ b/tests/MMS/advection/runtest @@ -20,7 +20,6 @@ import pickle import time - if __name__ == "__main__": build_and_log("MMS advection") diff --git a/tests/MMS/diffusion/runtest b/tests/MMS/diffusion/runtest index a25b3519a6..ef6b335f70 100755 --- a/tests/MMS/diffusion/runtest +++ b/tests/MMS/diffusion/runtest @@ -15,7 +15,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log - build_and_log("MMS diffusion test") # List of NX values to use diff --git a/tests/MMS/diffusion2/Z/plot_error.py b/tests/MMS/diffusion2/Z/plot_error.py index 5c4e1164c1..4b4b666758 100644 --- a/tests/MMS/diffusion2/Z/plot_error.py +++ b/tests/MMS/diffusion2/Z/plot_error.py @@ -4,6 +4,7 @@ @author: yolen """ + import boutdata import matplotlib.pyplot as plt diff --git a/tests/MMS/diffusion2/runtest b/tests/MMS/diffusion2/runtest index 546be3b2c1..d1b8072fc8 100755 --- a/tests/MMS/diffusion2/runtest +++ b/tests/MMS/diffusion2/runtest @@ -20,7 +20,6 @@ from numpy import sqrt, max, abs, mean, array, log from os.path import join - build_and_log("MMS diffusion test") # List of input directories diff --git a/tests/MMS/fieldalign/runtest.broken b/tests/MMS/fieldalign/runtest.broken index e09fbeccd5..bfb2023c1d 100755 --- a/tests/MMS/fieldalign/runtest.broken +++ b/tests/MMS/fieldalign/runtest.broken @@ -18,7 +18,6 @@ from os.path import join import time - build_and_log("MMS test") # nxlist = [256, 128, 64, 32, 16, 8] # do in reverse order to save disk space diff --git a/tests/MMS/hw/runtest b/tests/MMS/hw/runtest index b6abadc3b9..62740fcbbd 100755 --- a/tests/MMS/hw/runtest +++ b/tests/MMS/hw/runtest @@ -15,7 +15,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("MMS test") # List of NX values to use diff --git a/tests/MMS/laplace/runtest b/tests/MMS/laplace/runtest index 98eb62da87..80c8bf08af 100755 --- a/tests/MMS/laplace/runtest +++ b/tests/MMS/laplace/runtest @@ -14,7 +14,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("MMS test") # List of NX values to use diff --git a/tests/MMS/spatial/diffusion/runtest b/tests/MMS/spatial/diffusion/runtest index 000cf8f09b..8d4e759d59 100755 --- a/tests/MMS/spatial/diffusion/runtest +++ b/tests/MMS/spatial/diffusion/runtest @@ -22,7 +22,6 @@ from os.path import join import matplotlib.pyplot as plt - build_and_log("MMS diffusion test") # List of input directories diff --git a/tests/MMS/wave-1d-y/runtest b/tests/MMS/wave-1d-y/runtest index 9a4de5a3bb..332d6864b0 100755 --- a/tests/MMS/wave-1d-y/runtest +++ b/tests/MMS/wave-1d-y/runtest @@ -20,7 +20,6 @@ from sys import stdout from numpy import sqrt, max, abs, mean, array, log, concatenate, pi - build_and_log("Making MMS wave test") # List of NX values to use diff --git a/tests/MMS/wave-1d/runtest b/tests/MMS/wave-1d/runtest index f13fb7bd11..1adb7c7d6d 100755 --- a/tests/MMS/wave-1d/runtest +++ b/tests/MMS/wave-1d/runtest @@ -15,7 +15,6 @@ from boutdata.collect import collect from numpy import sqrt, max, abs, mean, array, log, concatenate - build_and_log("Making MMS wave test") # List of NX values to use diff --git a/tests/integrated/test-command-args/runtest b/tests/integrated/test-command-args/runtest index 39939bb105..a8dc3f020c 100755 --- a/tests/integrated/test-command-args/runtest +++ b/tests/integrated/test-command-args/runtest @@ -7,7 +7,6 @@ import shutil import unittest import platform - OUTPUT_FILE = "stdout.log" if platform.system() == "FreeBSD" else "stderr.log" diff --git a/tests/integrated/test-cyclic/runtest b/tests/integrated/test-cyclic/runtest index 5e31da6239..c68f9b033e 100755 --- a/tests/integrated/test-cyclic/runtest +++ b/tests/integrated/test-cyclic/runtest @@ -17,7 +17,6 @@ from boututils.run_wrapper import build_and_log, shell, launch from boutdata.collect import collect from sys import stdout, exit - build_and_log("Cyclic Reduction test") flags = ["", "nsys=2", "nsys=5 periodic", "nsys=7 n=10"] diff --git a/tests/integrated/test-griddata/runtest b/tests/integrated/test-griddata/runtest index 1c57bf5be7..6aef955023 100755 --- a/tests/integrated/test-griddata/runtest +++ b/tests/integrated/test-griddata/runtest @@ -13,7 +13,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("griddata test") for nproc in [1]: diff --git a/tests/integrated/test-gyro/runtest b/tests/integrated/test-gyro/runtest index 52ab0de4ab..7a0209fa57 100755 --- a/tests/integrated/test-gyro/runtest +++ b/tests/integrated/test-gyro/runtest @@ -25,7 +25,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("Gyro-average inversion test") # Read benchmark values diff --git a/tests/integrated/test-invpar/runtest b/tests/integrated/test-invpar/runtest index de7f028528..53f6015fe9 100755 --- a/tests/integrated/test-invpar/runtest +++ b/tests/integrated/test-invpar/runtest @@ -12,7 +12,6 @@ from boututils.run_wrapper import build_and_log, shell, launch from boutdata.collect import collect from sys import stdout, exit - build_and_log("parallel inversion test") flags_src = [ diff --git a/tests/integrated/test-laplace/runtest b/tests/integrated/test-laplace/runtest index e54e46d0d8..d1e9c3d1c9 100755 --- a/tests/integrated/test-laplace/runtest +++ b/tests/integrated/test-laplace/runtest @@ -41,7 +41,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("Laplacian inversion test") # Read benchmark values diff --git a/tests/integrated/test-multigrid_laplace/runtest b/tests/integrated/test-multigrid_laplace/runtest index 4a7455f80b..f325185c46 100755 --- a/tests/integrated/test-multigrid_laplace/runtest +++ b/tests/integrated/test-multigrid_laplace/runtest @@ -22,7 +22,6 @@ from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("multigrid Laplacian inversion test") print("Running multigrid Laplacian inversion test") diff --git a/tests/integrated/test-multigrid_laplace/runtest_multiple_grids b/tests/integrated/test-multigrid_laplace/runtest_multiple_grids index 6817120b13..739d15f7d4 100755 --- a/tests/integrated/test-multigrid_laplace/runtest_multiple_grids +++ b/tests/integrated/test-multigrid_laplace/runtest_multiple_grids @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Multigrid Laplacian inversion test") print("Running multigrid Laplacian inversion test") diff --git a/tests/integrated/test-multigrid_laplace/runtest_unsheared b/tests/integrated/test-multigrid_laplace/runtest_unsheared index cda68f2167..d0dca3a808 100755 --- a/tests/integrated/test-multigrid_laplace/runtest_unsheared +++ b/tests/integrated/test-multigrid_laplace/runtest_unsheared @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Making multigrid Laplacian inversion test") print("Running multigrid Laplacian inversion test") diff --git a/tests/integrated/test-naulin-laplace/runtest b/tests/integrated/test-naulin-laplace/runtest index f972eab6cc..145257c23b 100755 --- a/tests/integrated/test-naulin-laplace/runtest +++ b/tests/integrated/test-naulin-laplace/runtest @@ -22,7 +22,6 @@ from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("LaplaceNaulin inversion test") print("Running LaplaceNaulin inversion test") diff --git a/tests/integrated/test-naulin-laplace/runtest_multiple_grids b/tests/integrated/test-naulin-laplace/runtest_multiple_grids index c0281c3a4e..f56a80677e 100755 --- a/tests/integrated/test-naulin-laplace/runtest_multiple_grids +++ b/tests/integrated/test-naulin-laplace/runtest_multiple_grids @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Making LaplaceNaulin inversion test") print("Running LaplaceNaulin inversion test") diff --git a/tests/integrated/test-naulin-laplace/runtest_unsheared b/tests/integrated/test-naulin-laplace/runtest_unsheared index 8f47f33026..a845a9d92f 100755 --- a/tests/integrated/test-naulin-laplace/runtest_unsheared +++ b/tests/integrated/test-naulin-laplace/runtest_unsheared @@ -18,7 +18,6 @@ from boututils.run_wrapper import shell, build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("LaplaceNaulin inversion test") print("Running LaplaceNaulin inversion test") diff --git a/tests/integrated/test-petsc_laplace/runtest b/tests/integrated/test-petsc_laplace/runtest index ac248c4ce7..83e1006338 100755 --- a/tests/integrated/test-petsc_laplace/runtest +++ b/tests/integrated/test-petsc_laplace/runtest @@ -30,7 +30,6 @@ from boutdata.collect import collect # import numpy as np from sys import stdout, exit - build_and_log("PETSc Laplacian inversion test") print("Running PETSc Laplacian inversion test") diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/runtest b/tests/integrated/test-petsc_laplace_MAST-grid/runtest index 5a4cbf875a..3be39949c3 100755 --- a/tests/integrated/test-petsc_laplace_MAST-grid/runtest +++ b/tests/integrated/test-petsc_laplace_MAST-grid/runtest @@ -28,7 +28,6 @@ from boututils.run_wrapper import build_and_log, shell, launch_safe from boutdata.collect import collect from sys import stdout, exit - build_and_log( "PETSc Laplacian inversion test with non-identity metric (taken from grid for MAST SOL)" ) diff --git a/tests/integrated/test-region-iterator/runtest b/tests/integrated/test-region-iterator/runtest index e5825285de..2277ef15e9 100755 --- a/tests/integrated/test-region-iterator/runtest +++ b/tests/integrated/test-region-iterator/runtest @@ -17,7 +17,6 @@ from boututils.run_wrapper import build_and_log, launch_safe from boutdata.collect import collect from sys import exit - build_and_log("Region Iterator test") flags = [""] diff --git a/tests/integrated/test-restarting/runtest b/tests/integrated/test-restarting/runtest index 262aa818b6..daf19ebac6 100755 --- a/tests/integrated/test-restarting/runtest +++ b/tests/integrated/test-restarting/runtest @@ -5,7 +5,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("restart test") # Run once for 10 timesteps diff --git a/tests/integrated/test-smooth/runtest b/tests/integrated/test-smooth/runtest index bd7296341a..91ae793164 100755 --- a/tests/integrated/test-smooth/runtest +++ b/tests/integrated/test-smooth/runtest @@ -16,7 +16,6 @@ from boutdata.collect import collect import numpy as np from sys import stdout, exit - build_and_log("smoothing operator test") # Read benchmark values diff --git a/tests/integrated/test-squash/runtest b/tests/integrated/test-squash/runtest index 2f35e00589..8bf358ee8e 100755 --- a/tests/integrated/test-squash/runtest +++ b/tests/integrated/test-squash/runtest @@ -9,7 +9,6 @@ import argparse import re import os.path - # requires: all_tests # requires: netcdf # cores: 4 diff --git a/tests/integrated/test-yupdown/runtest b/tests/integrated/test-yupdown/runtest index 34fcd36496..13d7365b53 100755 --- a/tests/integrated/test-yupdown/runtest +++ b/tests/integrated/test-yupdown/runtest @@ -6,7 +6,6 @@ from sys import exit from numpy import max, abs - build_and_log("parallel slices test") failed = False diff --git a/tools/pylib/_boutpp_build/backend.py b/tools/pylib/_boutpp_build/backend.py index 31a4694d10..61bb826016 100755 --- a/tools/pylib/_boutpp_build/backend.py +++ b/tools/pylib/_boutpp_build/backend.py @@ -212,12 +212,10 @@ def build_sdist(sdist_directory, config_settings=None): run(f"rm {tmp}") with open(tmp, "w") as f: - f.write( - f"""Metadata-Version: 2.1 + f.write(f"""Metadata-Version: 2.1 Name: {pkgname} Version: {getversion()} -""" - ) +""") with open("LICENSE") as src: pre = "License: " for l in src: @@ -269,23 +267,19 @@ def prepare_metadata_for_build_wheel( distinfo = f"{metadata_directory}/{thisdir}" mkdir_p(distinfo) with open(f"{distinfo}/METADATA", "w") as f: - f.write( - f"""Metadata-Version: 2.1 + f.write(f"""Metadata-Version: 2.1 Name: {pkgname} Version: {getversion()} License-File: COPYING -""" - ) +""") run(f"cp LICENSE {distinfo}/COPYING") run(f"cp LICENSE.GPL {distinfo}/COPYING.GPL") with open(f"{distinfo}/WHEEL", "w") as f: - f.write( - f"""Wheel-Version: 1.0 + f.write(f"""Wheel-Version: 1.0 Generator: boutpp_custom_build_wheel ({getversion()}) Root-Is-Purelib: false Tag: {gettag()} -""" - ) +""") if record: with open(f"{distinfo}/RECORD", "w") as f: From 47e30d5e5667f0a6cc533d43a7ebde0cc190b6b0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 11:54:11 +0000 Subject: [PATCH 163/461] ci: Cache Zenodo data, and retry on http 502, 503 errors Also: - reduce max number of tries to avoid hammering Zenodo - update moved URL --- .github/workflows/tests.yml | 23 ++++++++++++++++++++ tests/integrated/test-fci-mpi/CMakeLists.txt | 2 +- 2 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 314ee196ae..3124a11762 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -181,6 +181,29 @@ jobs: python -m pip install --upgrade pip setuptools python -m pip install -r requirements.txt + - name: Cache Zenodo test data + uses: actions/cache@v4 + with: + path: ./zenodo_data + # Data doesn't depend on matrix job, so this should cache between all jobs + key: zenodo-data + + - name: Download Zenodo test data + run: | + if [[ ! -e zenodo_data/grid.fci.nc ]]; then + echo "Downloading Zenodo data" + mkdir -p zenodo_data + wget \ + https://zenodo.org/records/7614499/files/W7X-conf4-36x8x128.fci.nc?download=1 \ + --output-document=zenodo_data/grid.fci.nc \ + --retry-on-http-error=502,503,504 \ + --tries 3 + else + echo "Using cached Zenodo data" + fi + mkdir -pv build/tests/integrated/test-fci-mpi + cp -v zenodo_data/grid.fci.nc build/tests/integrated/test-fci-mpi + - name: Cache SUNDIALS build uses: actions/cache@v4 with: diff --git a/tests/integrated/test-fci-mpi/CMakeLists.txt b/tests/integrated/test-fci-mpi/CMakeLists.txt index 0dd38487a3..aea0421c0a 100644 --- a/tests/integrated/test-fci-mpi/CMakeLists.txt +++ b/tests/integrated/test-fci-mpi/CMakeLists.txt @@ -3,7 +3,7 @@ bout_add_mms_test(test-fci-mpi USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 6 - DOWNLOAD https://zenodo.org/record/7614499/files/W7X-conf4-36x8x128.fci.nc?download=1 + DOWNLOAD https://zenodo.org/records/7614499/files/W7X-conf4-36x8x128.fci.nc?download=1 DOWNLOAD_NAME grid.fci.nc REQUIRES BOUT_HAS_PETSC ) From 51d2ceb79105055d84ac4f60d1636b0fe073f53d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 13:37:47 +0000 Subject: [PATCH 164/461] ci: Use wget flags from env var This avoids duplicating the download in all jobs --- .github/workflows/tests.yml | 44 ++++++++++++++++++++----------------- cmake/BOUT++functions.cmake | 2 +- 2 files changed, 25 insertions(+), 21 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 3124a11762..e50154e615 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -30,6 +30,7 @@ jobs: OMPI_MCA_rmaps_base_oversubscribe: yes PRTE_MCA_rmaps_default_mapping_policy: ":oversubscribe" MPIRUN: mpiexec -np + BOUT_TEST_DOWNLOAD_FLAGS: --retry-on-http-error=502,503,504 --tries 3 strategy: fail-fast: true matrix: @@ -184,25 +185,9 @@ jobs: - name: Cache Zenodo test data uses: actions/cache@v4 with: - path: ./zenodo_data - # Data doesn't depend on matrix job, so this should cache between all jobs - key: zenodo-data - - - name: Download Zenodo test data - run: | - if [[ ! -e zenodo_data/grid.fci.nc ]]; then - echo "Downloading Zenodo data" - mkdir -p zenodo_data - wget \ - https://zenodo.org/records/7614499/files/W7X-conf4-36x8x128.fci.nc?download=1 \ - --output-document=zenodo_data/grid.fci.nc \ - --retry-on-http-error=502,503,504 \ - --tries 3 - else - echo "Using cached Zenodo data" - fi - mkdir -pv build/tests/integrated/test-fci-mpi - cp -v zenodo_data/grid.fci.nc build/tests/integrated/test-fci-mpi + path: build/tests/integrated/test-fci-mpi/grid.fci.nc + # If we update the test, invalidate the cache + key: zenodo-data-${{ hashFiles('tests/integrated/test-fci-mpi/CMakeLists.txt') }} - name: Cache SUNDIALS build uses: actions/cache@v4 @@ -227,10 +212,20 @@ jobs: # standard_tests timeout-minutes: 120 runs-on: ubuntu-latest + env: + BOUT_TEST_DOWNLOAD_FLAGS: --retry-on-http-error=502,503,504 --tries 3 steps: - uses: actions/checkout@v5 with: submodules: true + + - name: Cache Zenodo test data + uses: actions/cache@v4 + with: + path: build/tests/integrated/test-fci-mpi/grid.fci.nc + # If we update the test, invalidate the cache + key: zenodo-data-${{ hashFiles('tests/integrated/test-fci-mpi/CMakeLists.txt') }} + - name: Build Fedora run: ./.ci_fedora.sh setup openmpi shell: bash @@ -240,11 +235,20 @@ jobs: timeout-minutes: 60 runs-on: ubuntu-latest container: ghcr.io/ggeorgakoudis/boutdev-cuda:latest - + env: + BOUT_TEST_DOWNLOAD_FLAGS: --retry-on-http-error=502,503,504 --tries 3 steps: - uses: actions/checkout@v5 with: submodules: true + + - name: Cache Zenodo test data + uses: actions/cache@v4 + with: + path: build/tests/integrated/test-fci-mpi/grid.fci.nc + # If we update the test, invalidate the cache + key: zenodo-data-${{ hashFiles('tests/integrated/test-fci-mpi/CMakeLists.txt') }} + - name: Build minimal CUDA 12.2 @ GCC9.4.0 @ Ubuntu 20.04 run: | . /spack/share/spack/setup-env.sh diff --git a/cmake/BOUT++functions.cmake b/cmake/BOUT++functions.cmake index 77279dfd4b..074d803fb6 100644 --- a/cmake/BOUT++functions.cmake +++ b/cmake/BOUT++functions.cmake @@ -208,7 +208,7 @@ function(bout_add_integrated_or_mms_test BUILD_CHECK_TARGET TESTNAME) endif() set(output ) add_custom_command(OUTPUT ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} - COMMAND wget ${BOUT_TEST_OPTIONS_DOWNLOAD} -O ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + COMMAND wget ${BOUT_TEST_OPTIONS_DOWNLOAD} -O ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} $ENV{BOUT_TEST_DOWNLOAD_FLAGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Downloading ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME}" ) From bbaeeaf05ea80488d295313eca12ed3dbd22a51f Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 13:39:44 +0000 Subject: [PATCH 165/461] ci: Cache Python packages --- .github/workflows/tests.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e50154e615..47c053744f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -176,6 +176,7 @@ jobs: - uses: actions/setup-python@v6 with: python-version: '3.x' + cache: 'pip' - name: Install pip packages run: | From cb0d3f12061424e81c199272ff6e9f8589be35f0 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 21 Jan 2026 12:12:12 +0100 Subject: [PATCH 166/461] Add backtrace to tracked operations This avoids mostly the need for annotations using BOUT_USE_TRACK. setName could thus be dropped. --- src/field/field3d.cxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index a2ca5aad5e..00cdd8982f 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -33,6 +33,7 @@ #include #include #include +#include #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" @@ -859,6 +860,9 @@ void Field3D::_track(const T& change, std::string operation) { const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; locked->set(outname, change, "tracking"); + + const std::string trace = cpptrace::generate_trace().to_string(); + // Workaround for bug in gcc9.4 #if BOUT_USE_TRACK const std::string changename = change.name; @@ -868,6 +872,7 @@ void Field3D::_track(const T& change, std::string operation) { #if BOUT_USE_TRACK {"rhs.name", changename}, #endif + {"trace", trace}, }); } @@ -885,10 +890,12 @@ void Field3D::_track(const BoutReal& change, std::string operation) { if (locked == nullptr) { return; } + const std::string trace = cpptrace::generate_trace().to_string(); const std::string outname{fmt::format("track_{:s}_{:d}", selfname, tracking_state++)}; locked->set(outname, change, "tracking"); (*locked)[outname].setAttributes({ {"operation", operation}, {"rhs.name", "BoutReal"}, + {"trace", trace}, }); } From f2ed814db206d92c729980d4ad9d317ff3ebcf59 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:07:41 +0000 Subject: [PATCH 167/461] Apply clang-format changes --- src/field/field3d.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 00cdd8982f..c669516399 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -31,9 +31,9 @@ #include #include +#include #include #include -#include #include "bout/parallel_boundary_op.hxx" #include "bout/parallel_boundary_region.hxx" From e3f8923b4692a4d969127147cf4b57b00ea72aab Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 29 Jan 2026 10:25:08 +0100 Subject: [PATCH 168/461] Apply clang-format suggestions --- src/solver/impls/euler/euler.cxx | 4 +++- src/solver/impls/pvode/pvode.cxx | 4 ++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index f33d07f9d3..4b7a1bc029 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -1,6 +1,8 @@ - #include "euler.hxx" +#include +#include + #include "bout/field.hxx" #include "bout/version.hxx" #include diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index a0a0efa377..59f7b3867f 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -64,7 +64,7 @@ namespace { void pvode_load_data_f3d(const std::vector& evolve_bndrys, std::vector& ffs, const BoutReal* udata) { int p = 0; - Mesh* mesh = ffs[0].getMesh(); + const Mesh* mesh = ffs[0].getMesh(); const int nz = mesh->LocalNz; for (const auto& bndry : {true, false}) { for (const auto& i2d : mesh->getRegion2D(bndry ? "RGN_BNDRY" : "RGN_NOBNDRY")) { @@ -366,7 +366,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { if (flag != SUCCESS) { output_error.write("ERROR CVODE step failed, flag = {:d}\n", flag); if (debug_on_failure) { - CVodeMemRec* cv_mem = static_cast(cvode_mem); + const CVodeMemRec* cv_mem = static_cast(cvode_mem); if (!(f2d.empty() and v2d.empty() and v3d.empty())) { output_warn.write("debug_on_failure is currently only supported for Field3Ds"); return -1.0; From 873c94f89117d1af0aa35a7781bdec4f26293ff5 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 29 Jan 2026 10:33:14 +0100 Subject: [PATCH 169/461] Apply clang-format suggestions --- src/solver/impls/euler/euler.cxx | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 4b7a1bc029..a430f0a585 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -1,15 +1,16 @@ #include "euler.hxx" -#include -#include - -#include "bout/field.hxx" -#include "bout/version.hxx" +#include +#include #include #include #include #include +#include +#include +#include + EulerSolver::EulerSolver(Options* options) : Solver(options), mxstep((*options)["mxstep"] .doc("Maximum number of steps between outputs") From 7e5c3112667544350007225f58998a1782214561 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Thu, 29 Jan 2026 09:39:23 +0000 Subject: [PATCH 170/461] Apply clang-format changes --- src/solver/impls/euler/euler.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index a430f0a585..78721d2e8f 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -1,15 +1,15 @@ #include "euler.hxx" -#include -#include #include #include +#include #include #include +#include #include -#include #include +#include EulerSolver::EulerSolver(Options* options) : Solver(options), mxstep((*options)["mxstep"] From 227d5438dfecf5414e4a5064dbbd82f909ce8130 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 29 Jan 2026 11:04:02 +0100 Subject: [PATCH 171/461] CI: Rework formatting workflows All formatting is done in one task. So there will be no more different commits for clang-format and ruff/black. Also adds clang-format to the pipeline. In addition it adds an additional commit, with the hash from the formatting commit added to `.git-blame-ignore-revs`. This tells GitHub (and local git, if you have it configured) to ignore that commit in blames. --- .github/workflows/auto-formatting.yml | 62 +++++++++++++++++++++++++++ .github/workflows/black-fix.yml | 46 -------------------- .github/workflows/clang-format.yml | 33 -------------- 3 files changed, 62 insertions(+), 79 deletions(-) create mode 100644 .github/workflows/auto-formatting.yml delete mode 100644 .github/workflows/black-fix.yml delete mode 100644 .github/workflows/clang-format.yml diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml new file mode 100644 index 0000000000..61a537403f --- /dev/null +++ b/.github/workflows/auto-formatting.yml @@ -0,0 +1,62 @@ +name: format-command + +on: + pull_request: + +jobs: + clang-format: + # Release candidate branches tend to have big PRs which causes all sorts of problems + if: ${{ !endsWith(github.head_ref, '-rc') }} + runs-on: ubuntu-latest + steps: + # Checkout the pull request branch, also include all history + - uses: actions/checkout@v5 + with: + ref: ${{ github.head_ref }} + fetch-depth: 0 + + # This also installs git-clang-format + - name: Install formatters + run: | + sudo apt install clang-format python3-pip python3-setuptools python3-wheel + pip3 install ruff cmake-format + + - name: Version + run: | + python3 --version + $HOME/.local/bin/ruff --version + clang-format --version + $HOME/.local/bin/cmake-format --version + + - name: Run clang-format + id: format + run: + while ! git clang-format origin/${{ github.base_ref }} ; do git add . ; done + + - name: Run ruff + run: | + pwd + ls + $HOME/.local/bin/ruff format . + + - name: Run cmake-format + run: | + pwd + ls + $HOME/.local/bin/cmake-format $(find -name CMakeLists.txt) cmake/*.cmake + + - name: Commit formatting + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "[bot] Apply format changes" + skip_push: true + + - name: Ignore formatting + run: | + msg=$(git rev-list --format=%s --max-count=1 HEAD|tail -n -1) + test "$msg" == "Apply format changes" && git rev-parse HEAD > .git-blame-ignore-revs + + - name: Commit formatting + uses: stefanzweifel/git-auto-commit-action@v5 + with: + commit_message: "[bot] Add last format changes commit to ignore file" diff --git a/.github/workflows/black-fix.yml b/.github/workflows/black-fix.yml deleted file mode 100644 index 4306ee8b43..0000000000 --- a/.github/workflows/black-fix.yml +++ /dev/null @@ -1,46 +0,0 @@ -name: black - -on: - push: - paths: - - '**.py' - - 'bin/**' - - '**/runtest' - - '**/run' - - 'tests/integrated/test_suite' - - 'tests/requirements/*' - - 'tools/tokamak_grids/**' - -defaults: - run: - shell: bash - -jobs: - black: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v6 - with: - ref: ${{ github.head_ref }} - - - name: Install black - run: | - sudo apt update -y - sudo apt -y install python3-pip python3-setuptools python3-wheel - pip3 install black - - - name: Version - run: | - python3 --version - $HOME/.local/bin/black --version - - - name: Run black - run: | - pwd - ls - $HOME/.local/bin/black tests/ tools/ $(grep -EIlr '^#!.*python.*$' bin/ tests/ tools/ src/ | grep -v _boutpp_build) - - - uses: stefanzweifel/git-auto-commit-action@v7 - with: - commit_message: "Apply black changes" diff --git a/.github/workflows/clang-format.yml b/.github/workflows/clang-format.yml deleted file mode 100644 index 678960ba3e..0000000000 --- a/.github/workflows/clang-format.yml +++ /dev/null @@ -1,33 +0,0 @@ -name: clang-format-command - -on: - pull_request: - paths: - - '**.cxx' - - '**.hxx' - -jobs: - clang-format: - # Release candidate branches tend to have big PRs which causes all sorts of problems - if: ${{ !endsWith(github.head_ref, '-rc') }} - runs-on: ubuntu-latest - steps: - # Checkout the pull request branch, also include all history - - uses: actions/checkout@v6 - with: - ref: ${{ github.head_ref }} - fetch-depth: 0 - - # This also installs git-clang-format - - name: Install clang-format - run: sudo apt install clang-format - - - name: Run clang-format - id: format - run: - while ! git clang-format origin/${{ github.base_ref }} ; do git add . ; done - - - name: Commit to the PR branch - uses: stefanzweifel/git-auto-commit-action@v7 - with: - commit_message: "Apply clang-format changes" From 9d509cb3a1ff301ad5c7f1571b0684064a0d26c6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 29 Jan 2026 11:37:24 +0100 Subject: [PATCH 172/461] Do not format matlab files --- .clang-format-ignore | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 .clang-format-ignore diff --git a/.clang-format-ignore b/.clang-format-ignore new file mode 100644 index 0000000000..58566f02b1 --- /dev/null +++ b/.clang-format-ignore @@ -0,0 +1,2 @@ +# ignore matlab files +**/*.m From f1f22c89ed96c3bf8b7d2ab57a469f9802eeee1f Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 29 Jan 2026 11:42:26 +0100 Subject: [PATCH 173/461] CI: Fix pipeline --- .github/workflows/auto-formatting.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml index 61a537403f..38c6e8189f 100644 --- a/.github/workflows/auto-formatting.yml +++ b/.github/workflows/auto-formatting.yml @@ -46,7 +46,7 @@ jobs: $HOME/.local/bin/cmake-format $(find -name CMakeLists.txt) cmake/*.cmake - name: Commit formatting - uses: stefanzweifel/git-auto-commit-action@v5 + uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "[bot] Apply format changes" skip_push: true @@ -54,9 +54,9 @@ jobs: - name: Ignore formatting run: | msg=$(git rev-list --format=%s --max-count=1 HEAD|tail -n -1) - test "$msg" == "Apply format changes" && git rev-parse HEAD > .git-blame-ignore-revs + test "$msg" = "[bot] Apply format changes" && git rev-parse HEAD > .git-blame-ignore-revs || : - name: Commit formatting - uses: stefanzweifel/git-auto-commit-action@v5 + uses: stefanzweifel/git-auto-commit-action@v7 with: commit_message: "[bot] Add last format changes commit to ignore file" From 3e444b2a1adbf10c79457ccc2eabeb436bb771d3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 29 Jan 2026 11:44:22 +0100 Subject: [PATCH 174/461] CI: Ammend to ignore file --- .git-blame-ignore-revs | 1 + .github/workflows/auto-formatting.yml | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4990f2586e..ead5bc3729 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,4 @@ # Clang-format whole repo d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 +# Added by the bot diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml index 38c6e8189f..b3512f02cf 100644 --- a/.github/workflows/auto-formatting.yml +++ b/.github/workflows/auto-formatting.yml @@ -54,7 +54,7 @@ jobs: - name: Ignore formatting run: | msg=$(git rev-list --format=%s --max-count=1 HEAD|tail -n -1) - test "$msg" = "[bot] Apply format changes" && git rev-parse HEAD > .git-blame-ignore-revs || : + test "$msg" = "[bot] Apply format changes" && git rev-parse HEAD >> .git-blame-ignore-revs || : - name: Commit formatting uses: stefanzweifel/git-auto-commit-action@v7 From 4b010b7634aee1045743be80c268d4644522cd29 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Thu, 29 Jan 2026 10:45:02 +0000 Subject: [PATCH 175/461] [bot] Apply format changes --- examples/blob2d/blob_velocity.py | 154 ++++++------ examples/boutpp/simulation.py | 5 +- examples/conduction-snb/fit_temperature.py | 24 +- examples/conduction-snb/sinusoid.py | 30 ++- examples/conduction-snb/step.py | 31 ++- examples/conduction/generate.py | 4 +- examples/eigen-box/eigenvals.py | 35 +-- examples/elm-pb/Python/2dprofile.py | 91 ++++--- examples/elm-pb/Python/analysis.py | 16 +- examples/elm-pb/Python/elm_size.py | 179 +++++++------ examples/elm-pb/Python/fftall.py | 9 +- examples/elm-pb/Python/fftall2.py | 10 +- examples/elm-pb/Python/grate.py | 9 +- examples/elm-pb/Python/grate2.py | 45 ++-- examples/elm-pb/Python/plotcollapse.py | 47 ++-- examples/elm-pb/Python/plotmode.py | 93 ++++--- examples/elm-pb/Python/plotmode2.py | 94 ++++--- examples/elm-pb/Python/plotphase.py | 45 ++-- examples/elm-pb/Python/polslice.py | 26 +- examples/elm-pb/Python/post.py | 142 +++++++---- examples/elm-pb/Python/read_elmsize.py | 46 ++-- examples/elm-pb/Python/showpolslice.py | 36 +-- examples/elm-pb/Python/sprofiles.py | 41 +-- examples/elm-pb/runexample.py | 28 ++- examples/fci-wave/compare-density.py | 45 ++-- examples/finite-volume/diffusion/mms.py | 14 +- examples/finite-volume/fluid/mms.py | 20 +- examples/laplace-petsc3d/plotcheck.py | 16 +- examples/orszag-tang/generate.py | 116 +++++---- .../performance/bracket/scaling_parser.py | 5 +- .../performance/ddx/new_scaling_parser.py | 32 +-- .../performance/ddy/new_scaling_parser.py | 32 +-- .../performance/ddz/new_scaling_parser.py | 32 +-- .../performance/iterator/scaling_parser.py | 5 +- examples/staggered_grid/generate.py | 6 +- examples/subsampling/show.py | 10 +- examples/wave-slab/generate.py | 2 +- .../figure_creators/LaplacianMatrices.ipynb | 236 ++++++++++-------- .../bout_runners_folder_structure.py | 97 +++---- tests/MMS/advection/runtest | 2 +- 40 files changed, 1039 insertions(+), 871 deletions(-) diff --git a/examples/blob2d/blob_velocity.py b/examples/blob2d/blob_velocity.py index d044946320..6de0937d0f 100644 --- a/examples/blob2d/blob_velocity.py +++ b/examples/blob2d/blob_velocity.py @@ -3,96 +3,100 @@ import pickle try: - from past.utils import old_div + from past.utils import old_div except ImportError: - def old_div(a,b): - return a/b - -def blob_velocity(n,**kwargs): - - from boututils import calculus as Calc - # Calculate blob velocity in normalized time and normalized grid spacing - # - # Input: Blob density as a 3D vector in the form n[t,x,z] where t is time and x,z are the perpendicular spatial coordinates - # - # Keywords: - # - # type='peak' -> Calculate velocity of the peak density - # type='COM' -> Calculate centre of mass velocity - # Index=True -> return indices used to create velocity - # - # Default: Peak velocity with no index returning - - size = n.shape - - try: - v_type = kwargs['type'] - except: - v_type = 'peak' #Default to peak velocity calculation - try: - return_index = kwargs['Index'] - except: - return_index = False #Default to no index returning - - - if v_type == 'peak': - x = np.zeros(size[0]) - z = np.zeros(size[0]) - for i in np.arange(size[0]): - nmax,nmin = np.amax((n[i,:,:])),np.amin((n[i,:,:])) - xpos,zpos = np.where(n[i,:,:]==nmax) - x[i] = xpos[0] - z[i] = zpos[0] - - if v_type == 'COM': - x = np.zeros(size[0]) - z = np.zeros(size[0]) - for i in np.arange(size[0]): - data = n[i,:,:] - n[0,0,0] #use corner cell rather than nmin - ntot = np.sum(data[:,:]) - - z[i] = old_div(np.sum(np.sum(data[:,:],axis=0)*(np.arange(size[2]))),ntot) - x[i] = old_div(np.sum(np.sum(data[:,:],axis=1)*(np.arange(size[1]))),ntot) - - vx = Calc.deriv(x) - vz = Calc.deriv(z) - - if return_index: - return vx,vz,x,z - else: - return vx,vz - - - -data='data' + def old_div(a, b): + return a / b + + +def blob_velocity(n, **kwargs): + from boututils import calculus as Calc + # Calculate blob velocity in normalized time and normalized grid spacing + # + # Input: Blob density as a 3D vector in the form n[t,x,z] where t is time and x,z are the perpendicular spatial coordinates + # + # Keywords: + # + # type='peak' -> Calculate velocity of the peak density + # type='COM' -> Calculate centre of mass velocity + # Index=True -> return indices used to create velocity + # + # Default: Peak velocity with no index returning + + size = n.shape + + try: + v_type = kwargs["type"] + except: + v_type = "peak" # Default to peak velocity calculation + try: + return_index = kwargs["Index"] + except: + return_index = False # Default to no index returning + + if v_type == "peak": + x = np.zeros(size[0]) + z = np.zeros(size[0]) + for i in np.arange(size[0]): + nmax, nmin = np.amax((n[i, :, :])), np.amin((n[i, :, :])) + xpos, zpos = np.where(n[i, :, :] == nmax) + x[i] = xpos[0] + z[i] = zpos[0] + + if v_type == "COM": + x = np.zeros(size[0]) + z = np.zeros(size[0]) + for i in np.arange(size[0]): + data = n[i, :, :] - n[0, 0, 0] # use corner cell rather than nmin + ntot = np.sum(data[:, :]) + + z[i] = old_div( + np.sum(np.sum(data[:, :], axis=0) * (np.arange(size[2]))), ntot + ) + x[i] = old_div( + np.sum(np.sum(data[:, :], axis=1) * (np.arange(size[1]))), ntot + ) + + vx = Calc.deriv(x) + vz = Calc.deriv(z) + + if return_index: + return vx, vz, x, z + else: + return vx, vz + + +data = "data" if True: - import sys - if len(sys.argv) > 1: - data=sys.argv[1] + import sys + + if len(sys.argv) > 1: + data = sys.argv[1] -n = collect('n', path=data, info=False) +n = collect("n", path=data, info=False) -vx,vy,xx,yy = blob_velocity(n[:,:,0,:],type='COM',Index=True) +vx, vy, xx, yy = blob_velocity(n[:, :, 0, :], type="COM", Index=True) -f = open('Velocity.dat','wb') -pickle.dump(vx,f) +f = open("Velocity.dat", "wb") +pickle.dump(vx, f) f.close() -f = open('Position.dat','wb') -pickle.dump(xx,f) +f = open("Position.dat", "wb") +pickle.dump(xx, f) f.close() -f = open('Velocity.dat','rb') +f = open("Velocity.dat", "rb") vx = pickle.load(f) f.close() try: - import matplotlib.pyplot as plt - plt.plot(vx) - plt.show() + import matplotlib.pyplot as plt + + plt.plot(vx) + plt.show() except ImportError: - pass + pass diff --git a/examples/boutpp/simulation.py b/examples/boutpp/simulation.py index 1b57ff8b33..9948bd9b03 100755 --- a/examples/boutpp/simulation.py +++ b/examples/boutpp/simulation.py @@ -3,12 +3,13 @@ bc.init("mesh:n=48") + class Model(bc.PhysicsModel): - def init(self,restart): + def init(self, restart): self.dens = bc.create3D("sin(x)") self.solve_for(n=self.dens) - def rhs(self,time): + def rhs(self, time): self.dens.ddt(bc.DDX(self.dens)) diff --git a/examples/conduction-snb/fit_temperature.py b/examples/conduction-snb/fit_temperature.py index 1e7f3ddf94..81faa9cf93 100644 --- a/examples/conduction-snb/fit_temperature.py +++ b/examples/conduction-snb/fit_temperature.py @@ -3,23 +3,31 @@ import matplotlib.pyplot as plt Te_ref = np.loadtxt("temperature.csv", delimiter=",") -Te_ref[:,0] *= 1e-4 # Convert X axis to m +Te_ref[:, 0] *= 1e-4 # Convert X axis to m + def te_function(ypos, mid, wwid, w0, w1, w2, Tmax, Tmin, clip=False): - width = w0 + ((ypos - mid)*w1 + (ypos - mid)**2 * w2) * np.exp(-((ypos - mid)/wwid)**2) + width = w0 + ((ypos - mid) * w1 + (ypos - mid) ** 2 * w2) * np.exp( + -(((ypos - mid) / wwid) ** 2) + ) if clip: width = np.clip(width, 1e-10, None) - return Tmax - 0.5 * (1 + np.tanh((ypos - mid)/width)) * (Tmax - Tmin) + return Tmax - 0.5 * (1 + np.tanh((ypos - mid) / width)) * (Tmax - Tmin) + -popt, pcov = optimize.curve_fit(te_function, Te_ref[:,0], Te_ref[:,1], - p0 = [2.2e-4, 1e-4, 1e-4, 0.0, 0.0, 0.960, 0.190]) +popt, pcov = optimize.curve_fit( + te_function, + Te_ref[:, 0], + Te_ref[:, 1], + p0=[2.2e-4, 1e-4, 1e-4, 0.0, 0.0, 0.960, 0.190], +) print(popt) -xfit = np.linspace(Te_ref[0,0], Te_ref[-1,0], 100) +xfit = np.linspace(Te_ref[0, 0], Te_ref[-1, 0], 100) -plt.plot(xfit, te_function(xfit, *popt, clip=True), '-k') -plt.plot(Te_ref[:,0], Te_ref[:,1], 'or') +plt.plot(xfit, te_function(xfit, *popt, clip=True), "-k") +plt.plot(Te_ref[:, 0], Te_ref[:, 1], "or") plt.show() diff --git a/examples/conduction-snb/sinusoid.py b/examples/conduction-snb/sinusoid.py index 10c81923a3..439530383e 100644 --- a/examples/conduction-snb/sinusoid.py +++ b/examples/conduction-snb/sinusoid.py @@ -11,7 +11,7 @@ build_and_log("Sinusoidal SNB") # Electron temperature in eV -Telist = 10 ** np.linspace(0,3,20) +Telist = 10 ** np.linspace(0, 3, 20) # Electron density in m^-3 Ne = 1e20 @@ -19,29 +19,30 @@ # Length of the domain in m length = 1.0 -c = 299792458 -mu0 = 4.e-7*np.pi -e0 = 1/(c*c*mu0) +c = 299792458 +mu0 = 4.0e-7 * np.pi +e0 = 1 / (c * c * mu0) qe = 1.602176634e-19 me = 9.10938356e-31 -thermal_speed = np.sqrt(2.*qe * Telist / me) -Y = (qe**2 / (e0 * me))**2 / (4 * np.pi) +thermal_speed = np.sqrt(2.0 * qe * Telist / me) +Y = (qe**2 / (e0 * me)) ** 2 / (4 * np.pi) coulomb_log = 6.6 - 0.5 * np.log(Ne * 1e-20) + 1.5 * np.log(Telist) -lambda_ee_T = thermal_speed**4 / (Y * Ne * coulomb_log) +lambda_ee_T = thermal_speed**4 / (Y * Ne * coulomb_log) beta_max_list = [5, 10, 20, 40] -colors = ['k','b','g','r'] +colors = ["k", "b", "g", "r"] ngroups_list = [20, 40, 80] -syms = ['x', 'o', 'D'] +syms = ["x", "o", "D"] for beta_max, color in zip(beta_max_list, colors): for ngroups, sym in zip(ngroups_list, syms): - flux_ratio = [] for Te in Telist: - cmd = "./conduction-snb \"Te={0}+0.01*sin(y)\" Ne={1} mesh:length={2} snb:beta_max={3} snb:ngroups={4}".format(Te, Ne, length, beta_max, ngroups) + cmd = './conduction-snb "Te={0}+0.01*sin(y)" Ne={1} mesh:length={2} snb:beta_max={3} snb:ngroups={4}'.format( + Te, Ne, length, beta_max, ngroups + ) # Run the case s, out = launch_safe(cmd, nproc=1, mthread=1, pipe=True) @@ -54,7 +55,12 @@ flux_ratio.append(div_q[ind] / div_q_SH[ind]) - plt.plot(lambda_ee_T / length, flux_ratio, '-'+sym+color, label=r"$\beta_{{max}}={0}, N_g={1}$".format(beta_max,ngroups)) + plt.plot( + lambda_ee_T / length, + flux_ratio, + "-" + sym + color, + label=r"$\beta_{{max}}={0}, N_g={1}$".format(beta_max, ngroups), + ) plt.legend() plt.xlabel(r"$\lambda_{ee,T} / L$") diff --git a/examples/conduction-snb/step.py b/examples/conduction-snb/step.py index 1f8933e66b..63a13149f4 100644 --- a/examples/conduction-snb/step.py +++ b/examples/conduction-snb/step.py @@ -3,7 +3,7 @@ # # Uses a step in the temperature, intended for comparison to VFP results -length = 6e-4 # Domain length in m +length = 6e-4 # Domain length in m qe = 1.602176634e-19 @@ -40,37 +40,37 @@ # Read reference values Te_ref = np.loadtxt("temperature.csv", delimiter=",") -Te_ref[:,0] *= 1e-4 # Convert X axis to m +Te_ref[:, 0] *= 1e-4 # Convert X axis to m SH_ref = np.loadtxt("spitzer-harm.csv", delimiter=",") -SH_ref[:,0] *= 1e-4 +SH_ref[:, 0] *= 1e-4 SNB_ref = np.loadtxt("snb.csv", delimiter=",") -SNB_ref[:,0] *= 1e-4 +SNB_ref[:, 0] *= 1e-4 VFP_ref = np.loadtxt("vfp.csv", delimiter=",") -VFP_ref[:,0] *= 1e-4 +VFP_ref[:, 0] *= 1e-4 ######################################### fig, ax1 = plt.subplots() -color='tab:red' +color = "tab:red" ax1.plot(position, Te * 1e-3, color=color, label="Te") -ax1.plot(Te_ref[:,0], Te_ref[:,1], color=color, marker="o", label="Reference Te") +ax1.plot(Te_ref[:, 0], Te_ref[:, 1], color=color, marker="o", label="Reference Te") ax1.set_xlabel("position [m]") ax1.set_ylabel("Electron temperature [keV]", color=color) -ax1.set_ylim(0,1) -ax1.tick_params(axis='y', colors=color) +ax1.set_ylim(0, 1) +ax1.tick_params(axis="y", colors=color) ax2 = ax1.twinx() -ax2.plot(position, q_SH * 1e-4, '-k', label="Spitzer-Harm") -ax2.plot(SH_ref[:,0], SH_ref[:,1], '--k', label="Reference SH") +ax2.plot(position, q_SH * 1e-4, "-k", label="Spitzer-Harm") +ax2.plot(SH_ref[:, 0], SH_ref[:, 1], "--k", label="Reference SH") -ax2.plot(position, q * 1e-4, '-b', label="SNB") -ax2.plot(SNB_ref[:,0], SNB_ref[:,1], '--b', label="Reference SNB") +ax2.plot(position, q * 1e-4, "-b", label="SNB") +ax2.plot(SNB_ref[:, 0], SNB_ref[:, 1], "--b", label="Reference SNB") -ax2.plot(VFP_ref[:,0], VFP_ref[:,1], '--g', label="Reference VFP") +ax2.plot(VFP_ref[:, 0], VFP_ref[:, 1], "--g", label="Reference VFP") ax2.set_ylabel("Heat flux W/cm^2") ax2.set_ylim(bottom=0.0) @@ -78,8 +78,7 @@ plt.legend() fig.tight_layout() -plt.xlim(0,3.5e-4) +plt.xlim(0, 3.5e-4) plt.savefig("snb-step.png") plt.show() - diff --git a/examples/conduction/generate.py b/examples/conduction/generate.py index 7138026898..4a2a2acfc0 100755 --- a/examples/conduction/generate.py +++ b/examples/conduction/generate.py @@ -4,9 +4,9 @@ # Generate an input mesh # -from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries +from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries -nx = 5 # Minimum is 5: 2 boundary, one evolved +nx = 5 # Minimum is 5: 2 boundary, one evolved ny = 64 # Minimum 5. Should be divisible by number of processors (so powers of 2 nice) f = DataFile() diff --git a/examples/eigen-box/eigenvals.py b/examples/eigen-box/eigenvals.py index 7b52fc60f2..5a71aabfc6 100755 --- a/examples/eigen-box/eigenvals.py +++ b/examples/eigen-box/eigenvals.py @@ -27,7 +27,8 @@ def plot_eigenvals(eigenvalues, eigenvectors=None): raise ValueError("Expecting eigenvectors to be 2D") if eigenvectors.shape[0] != len(eigenvalues): raise ValueError( - "First dimension of eigenvectors must match length of eigenvalues") + "First dimension of eigenvectors must match length of eigenvalues" + ) # If no eigenvectors supplied, only plot eigenvalues, otherwise # eigenvalues and eigenvectors @@ -44,18 +45,18 @@ def plot_eigenvals(eigenvalues, eigenvectors=None): range_r = amax(eigs_r) - amin(eigs_r) range_i = amax(eigs_i) - amin(eigs_i) - ax[0].plot(eigs_r, eigs_i, 'x') + ax[0].plot(eigs_r, eigs_i, "x") ax[0].set_xlabel("Real component") ax[0].set_ylabel("Imaginary component") ax[0].set_title("Eigenvalue") - overplot, = ax[0].plot([], [], 'ok') + (overplot,) = ax[0].plot([], [], "ok") if eigenvectors is not None: # Add a eigenvectors plot - vector_r, = ax[1].plot([], [], '-k', label="Real") - vector_i, = ax[1].plot([], [], '-r', label="Imag") - ax[1].legend(loc='upper right') + (vector_r,) = ax[1].plot([], [], "-k", label="Real") + (vector_i,) = ax[1].plot([], [], "-r", label="Imag") + ax[1].legend(loc="upper right") ax[1].set_xlabel("X") ax[1].set_ylabel("Amplitude") ax[1].set_title("Eigenvector") @@ -68,33 +69,33 @@ def onclick(event): # Find closest eigenvectors point, but stretch axes so # real and imaginary components are weighted equally - if(range_r == 0): - dist = ((eigs_i - event.ydata)/range_i)**2 - elif(range_i == 0): - dist = ((eigs_r - event.xdata)/range_r)**2 + if range_r == 0: + dist = ((eigs_i - event.ydata) / range_i) ** 2 + elif range_i == 0: + dist = ((eigs_r - event.xdata) / range_r) ** 2 else: - dist = ((eigs_r - event.xdata)/range_r)**2 + \ - ((eigs_i - event.ydata)/range_i)**2 + dist = ((eigs_r - event.xdata) / range_r) ** 2 + ( + (eigs_i - event.ydata) / range_i + ) ** 2 ind = argmin(dist) # Update the highlight plot overplot.set_data([eigs_r[ind]], [eigs_i[ind]]) - print("Eigenvalue number: %d (%e,%e)" % - (ind, eigs_r[ind], eigs_i[ind])) + print("Eigenvalue number: %d (%e,%e)" % (ind, eigs_r[ind], eigs_i[ind])) if eigenvectors is not None: # Update plots nx = eigenvectors.shape[1] - vector_r.set_data(arange(nx), eigenvectors[2*ind, :]) - vector_i.set_data(arange(nx), eigenvectors[2*ind+1, :]) + vector_r.set_data(arange(nx), eigenvectors[2 * ind, :]) + vector_i.set_data(arange(nx), eigenvectors[2 * ind + 1, :]) ax[1].relim() ax[1].autoscale_view() fig.canvas.draw() - fig.canvas.mpl_connect('button_press_event', onclick) + fig.canvas.mpl_connect("button_press_event", onclick) plt.show() diff --git a/examples/elm-pb/Python/2dprofile.py b/examples/elm-pb/Python/2dprofile.py index 5bb01972f9..e95709ab5b 100644 --- a/examples/elm-pb/Python/2dprofile.py +++ b/examples/elm-pb/Python/2dprofile.py @@ -4,81 +4,92 @@ import numpy as np import matplotlib.pyplot as plt from boututils.datafile import DataFile -from matplotlib.ticker import FixedFormatter, FormatStrFormatter, AutoLocator, AutoMinorLocator +from matplotlib.ticker import ( + FixedFormatter, + FormatStrFormatter, + AutoLocator, + AutoMinorLocator, +) with DataFile("./cbm18_dens8.grid_nx68ny64.nc") as f: g = {v: f.read(v) for v in f.keys()} -majorLocator = AutoLocator() -majorFormatter = FormatStrFormatter('%3.0e') -minorLocator = AutoMinorLocator() -Fm = FixedFormatter(['0','$1 \\times 10^4$','$2 \\times 10^4$','$3 \\times 10^4$','$4 \\times 10^4$']) -Fm2 = FixedFormatter(['0','$2 \\times 10^5$','$4 \\times 10^5$','$6 \\times 10^5$']) - -bxy=g.get('Bxy') -p=g.get('pressure') -jpar0=g.get('Jpar0') -psixy=g.get('psixy') -btxy=g.get('Btxy') -shiftangle=g.get('ShiftAngle') - -nx=g.get('nx') -ny=g.get('ny') +majorLocator = AutoLocator() +majorFormatter = FormatStrFormatter("%3.0e") +minorLocator = AutoMinorLocator() +Fm = FixedFormatter( + [ + "0", + "$1 \\times 10^4$", + "$2 \\times 10^4$", + "$3 \\times 10^4$", + "$4 \\times 10^4$", + ] +) +Fm2 = FixedFormatter(["0", "$2 \\times 10^5$", "$4 \\times 10^5$", "$6 \\times 10^5$"]) + +bxy = g.get("Bxy") +p = g.get("pressure") +jpar0 = g.get("Jpar0") +psixy = g.get("psixy") +btxy = g.get("Btxy") +shiftangle = g.get("ShiftAngle") + +nx = g.get("nx") +ny = g.get("ny") q = np.zeros((nx, ny)) for i in range(ny): - q[:,i] = old_div(- shiftangle, (2 * np.pi)) - - + q[:, i] = old_div(-shiftangle, (2 * np.pi)) -xarr = psixy[:,0] +xarr = psixy[:, 0] xarr = old_div((xarr + 0.854856), (0.854856 + 0.0760856)) -fig=plt.figure() -plt.plot(xarr,q[:,32]) -plt.xlabel('normalized $\psi$', fontsize=25) -plt.ylabel('$q$',rotation='horizontal',fontsize=25) +fig = plt.figure() +plt.plot(xarr, q[:, 32]) +plt.xlabel("normalized $\psi$", fontsize=25) +plt.ylabel("$q$", rotation="horizontal", fontsize=25) fig.set_tight_layout(True) fig, ax1 = plt.subplots() -ax1.plot(xarr, p[:,32], 'r-', markevery=1, linewidth=3) -ax1.set_xlabel('normalized $\psi$',fontsize=25) +ax1.plot(xarr, p[:, 32], "r-", markevery=1, linewidth=3) +ax1.set_xlabel("normalized $\psi$", fontsize=25) # Make the y-axis label and tick labels match the line color. -ax1.set_ylabel('Pressure [Pa]', color='k',fontsize=25) +ax1.set_ylabel("Pressure [Pa]", color="k", fontsize=25) -#set y limit -ax1.set_ylim(0,40000,10000) +# set y limit +ax1.set_ylim(0, 40000, 10000) -#define ticks# +# define ticks# ax1.yaxis.set_ticks(np.arange(0, 40000, 10000)) -#ax1.yaxis.set_major_locator(majorLocator) -#ax1.yaxis.set_major_formatter(majorFormatter) +# ax1.yaxis.set_major_locator(majorLocator) +# ax1.yaxis.set_major_formatter(majorFormatter) ax1.yaxis.set_major_formatter(Fm) -#for the minor ticks, use no labels; default NullFormatter +# for the minor ticks, use no labels; default NullFormatter ax1.xaxis.set_minor_locator(AutoMinorLocator()) ax1.yaxis.set_minor_locator(AutoMinorLocator(10)) -#format tick labels +# format tick labels for tl in ax1.get_yticklabels(): - tl.set_color('k') + tl.set_color("k") ax2 = ax1.twinx() s2 = -jpar0 -ax2.plot(xarr, s2[:,32], 'r-',markevery=1,linewidth=3) -ax2.set_ylabel('$J_\parallel [A/m^2]$', color='k',fontsize=25) -ax2.set_ylim(0,600000) +ax2.plot(xarr, s2[:, 32], "r-", markevery=1, linewidth=3) +ax2.set_ylabel("$J_\parallel [A/m^2]$", color="k", fontsize=25) +ax2.set_ylim(0, 600000) ax2.yaxis.set_ticks(np.arange(0, 600000, 200000)) ax2.yaxis.set_major_formatter(Fm2) for tl in ax2.get_yticklabels(): - tl.set_color('k') + tl.set_color("k") fig.set_tight_layout(True) @@ -86,4 +97,4 @@ plt.show() -#plt.savefig('2d.png', transparent=True) +# plt.savefig('2d.png', transparent=True) diff --git a/examples/elm-pb/Python/analysis.py b/examples/elm-pb/Python/analysis.py index 4faf85f8ce..0e4a3acfdd 100644 --- a/examples/elm-pb/Python/analysis.py +++ b/examples/elm-pb/Python/analysis.py @@ -6,24 +6,24 @@ import pylab as plt from boutdata.collect import collect -path='./data/' -var=collect('P', path=path) +path = "./data/" +var = collect("P", path=path) -dcvar=np.mean(var, axis=3) -rmsvar=np.sqrt(np.mean(var**2,axis=3)-dcvar**2) +dcvar = np.mean(var, axis=3) +rmsvar = np.sqrt(np.mean(var**2, axis=3) - dcvar**2) plt.figure() -plt.plot(rmsvar[:,34,32]) +plt.plot(rmsvar[:, 34, 32]) plt.show(block=False) -fvar=np.fft.rfft(var,axis=3) +fvar = np.fft.rfft(var, axis=3) plt.figure() -plt.plot(abs(fvar[:,34,32,1:10])) +plt.plot(abs(fvar[:, 34, 32, 1:10])) plt.show(block=False) plt.figure() -plt.semilogy(abs(fvar[:,34,32,1:7])) +plt.semilogy(abs(fvar[:, 34, 32, 1:7])) plt.show(block=False) plt.show() diff --git a/examples/elm-pb/Python/elm_size.py b/examples/elm-pb/Python/elm_size.py index 0d5c105870..6ad2ed6a0d 100644 --- a/examples/elm-pb/Python/elm_size.py +++ b/examples/elm-pb/Python/elm_size.py @@ -4,87 +4,126 @@ from past.utils import old_div import numpy as np -def elm_size(dcp,p0,uedge,xmin=None,xmax=None,yind=None,Bbar=None): - - lis=[dcp,p0,uedge] - if np.size(lis) != 3 : + +def elm_size(dcp, p0, uedge, xmin=None, xmax=None, yind=None, Bbar=None): + lis = [dcp, p0, uedge] + if np.size(lis) != 3: print("lack of parameters") return 0 - - if xmin == None : xmin=0 - if xmax == None : xmax=327 - if yind == None : yind=63 # choose the poloidal location for 1D size - if Bbar == None : Bbar=1.992782 # the normalized magnetic field + if xmin == None: + xmin = 0 + if xmax == None: + xmax = 327 + if yind == None: + yind = 63 # choose the poloidal location for 1D size + if Bbar == None: + Bbar = 1.992782 # the normalized magnetic field - mydcp=dcp - myp0=p0 - g=uedge + mydcp = dcp + myp0 = p0 + g = uedge PI = 3.1415926 - MU0 = 4.0e-7*PI + MU0 = 4.0e-7 * PI - s=np.shape(mydcp) + s = np.shape(mydcp) - if np.ndim(mydcp) != 3 : + if np.ndim(mydcp) != 3: print("dcp should be 3D(t,x,y)") - - - nt=s[0] - nx=s[1] - ny=s[2] - - Dtheta=g['dy'] #using correct poloidal angle - psixy=g['psixy'] - R=g['Rxy'] - Bp=g['Bpxy'] - hthe=g['hthe'] - - Dpsi=np.zeros((nx,ny)) - Dpsi[0,:]=psixy[1,:]-psixy[0,:] - Dpsi[nx-1,:]=psixy[nx-1,:]-psixy[nx-2,:] - for i in range(1,nx-2): - Dpsi[i,:]=old_div((psixy[i+1,:]-psixy[i-1,:]),2) - - - Ddcp1=np.zeros(nt) - Ddcp2=np.zeros(nt) - Ddcp3=np.zeros(nt) - Tp01=0. - Tp02=0. - Tp03=0. - - for t in range(nt) : - Ddcp3[t]=2.0*PI*np.sum(mydcp[t,xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/Bp[xmin:xmax,:]) - Ddcp2[t]=np.sum(mydcp[t,xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/(R[xmin:xmax,:]*Bp[xmin:xmax,:])) - Ddcp1[t]=np.sum(mydcp[t,xmin:xmax,yind]*Dpsi[xmin:xmax,yind]/(R[xmin:xmax,yind]*Bp[xmin:xmax,yind])) - - - Tp03=2.0*PI*np.sum(myp0[xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/Bp[xmin:xmax,:]) - Tp02=np.sum(myp0[xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/(R[xmin:xmax,:]*Bp[xmin:xmax,:])) - Tp01=np.sum(myp0[xmin:xmax,yind]*Dpsi[xmin:xmax,yind]/(R[xmin:xmax,yind]*Bp[xmin:xmax,yind])) - - s1=np.zeros(nt) - s2=np.zeros(nt) - s3=np.zeros(nt) - E_loss=np.zeros(nt) - - s1=old_div(-Ddcp1,Tp01) #1D elm size - s2=old_div(-Ddcp2,Tp02) #2D elm size - s3=old_div(-Ddcp3,Tp03) #3D elm size - - E_loss=-Ddcp3*(0.5*Bbar*Bbar/MU0) #energy loss, unit J - E_total=Tp03*(0.5*Bbar*Bbar/MU0) #total energy, unit J + + nt = s[0] + nx = s[1] + ny = s[2] + + Dtheta = g["dy"] # using correct poloidal angle + psixy = g["psixy"] + R = g["Rxy"] + Bp = g["Bpxy"] + hthe = g["hthe"] + + Dpsi = np.zeros((nx, ny)) + Dpsi[0, :] = psixy[1, :] - psixy[0, :] + Dpsi[nx - 1, :] = psixy[nx - 1, :] - psixy[nx - 2, :] + for i in range(1, nx - 2): + Dpsi[i, :] = old_div((psixy[i + 1, :] - psixy[i - 1, :]), 2) + + Ddcp1 = np.zeros(nt) + Ddcp2 = np.zeros(nt) + Ddcp3 = np.zeros(nt) + Tp01 = 0.0 + Tp02 = 0.0 + Tp03 = 0.0 + + for t in range(nt): + Ddcp3[t] = ( + 2.0 + * PI + * np.sum( + mydcp[t, xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / Bp[xmin:xmax, :] + ) + ) + Ddcp2[t] = np.sum( + mydcp[t, xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / (R[xmin:xmax, :] * Bp[xmin:xmax, :]) + ) + Ddcp1[t] = np.sum( + mydcp[t, xmin:xmax, yind] + * Dpsi[xmin:xmax, yind] + / (R[xmin:xmax, yind] * Bp[xmin:xmax, yind]) + ) + + Tp03 = ( + 2.0 + * PI + * np.sum( + myp0[xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / Bp[xmin:xmax, :] + ) + ) + Tp02 = np.sum( + myp0[xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / (R[xmin:xmax, :] * Bp[xmin:xmax, :]) + ) + Tp01 = np.sum( + myp0[xmin:xmax, yind] + * Dpsi[xmin:xmax, yind] + / (R[xmin:xmax, yind] * Bp[xmin:xmax, yind]) + ) + + s1 = np.zeros(nt) + s2 = np.zeros(nt) + s3 = np.zeros(nt) + E_loss = np.zeros(nt) + + s1 = old_div(-Ddcp1, Tp01) # 1D elm size + s2 = old_div(-Ddcp2, Tp02) # 2D elm size + s3 = old_div(-Ddcp3, Tp03) # 3D elm size + + E_loss = -Ddcp3 * (0.5 * Bbar * Bbar / MU0) # energy loss, unit J + E_total = Tp03 * (0.5 * Bbar * Bbar / MU0) # total energy, unit J class ELM: pass - elmsize=ELM() - elmsize.s1=s1 - elmsize.s2=s2 - elmsize.s3=s3 - elmsize.E_loss=E_loss - elmsize.E_total=E_total + + elmsize = ELM() + elmsize.s1 = s1 + elmsize.s2 = s2 + elmsize.s3 = s3 + elmsize.E_loss = E_loss + elmsize.E_total = E_total return elmsize - - diff --git a/examples/elm-pb/Python/fftall.py b/examples/elm-pb/Python/fftall.py index e0a4385764..c6d5b87ded 100644 --- a/examples/elm-pb/Python/fftall.py +++ b/examples/elm-pb/Python/fftall.py @@ -2,8 +2,7 @@ from numpy import * from scipy.io import readsav -print('Calculating P..') -a=transpose(readsav('phi.idl.dat')['phi']) -fa=fft.fft(a,axis=2) -save('fp',fa) - +print("Calculating P..") +a = transpose(readsav("phi.idl.dat")["phi"]) +fa = fft.fft(a, axis=2) +save("fp", fa) diff --git a/examples/elm-pb/Python/fftall2.py b/examples/elm-pb/Python/fftall2.py index d864f356d0..64b08c437a 100644 --- a/examples/elm-pb/Python/fftall2.py +++ b/examples/elm-pb/Python/fftall2.py @@ -2,9 +2,9 @@ from numpy import * from boutdata.collect import collect -path='./data/' -data=collect('P',path=path) +path = "./data/" +data = collect("P", path=path) -print('Saving P..') -fa=fft.fft(data,axis=3) -save('fp',rollaxis(fa,0,4)) +print("Saving P..") +fa = fft.fft(data, axis=3) +save("fp", rollaxis(fa, 0, 4)) diff --git a/examples/elm-pb/Python/grate.py b/examples/elm-pb/Python/grate.py index 003f38ff8e..65d6da6449 100644 --- a/examples/elm-pb/Python/grate.py +++ b/examples/elm-pb/Python/grate.py @@ -8,12 +8,11 @@ from boututils.moment_xyzt import moment_xyzt +path = "./data/" -path='./data/' +p = collect("P", path=path) +rmsp_f = moment_xyzt(p[:, 34:35, 32:33, :], "RMS").rms -p=collect('P',path=path) -rmsp_f=moment_xyzt(p[:,34:35,32:33,:], 'RMS').rms - -print(np.gradient(np.log(rmsp_f[:,0,0]))[-1]) +print(np.gradient(np.log(rmsp_f[:, 0, 0]))[-1]) diff --git a/examples/elm-pb/Python/grate2.py b/examples/elm-pb/Python/grate2.py index f8172bcf08..af0768803a 100644 --- a/examples/elm-pb/Python/grate2.py +++ b/examples/elm-pb/Python/grate2.py @@ -2,6 +2,7 @@ from __future__ import division from builtins import range from past.utils import old_div + ### # computes average growth rate for all points at the final timestep # computes average growth rate for points in the mead plane at the final timestep @@ -10,40 +11,44 @@ from boutdata.collect import collect from boututils.moment_xyzt import moment_xyzt -path='./data/' +path = "./data/" + +p = collect("P", path=path) -p=collect('P',path=path) +nmpy = old_div(p.shape[2], 2) # define mead plane -nmpy=old_div(p.shape[2],2) # define mead plane +ik = 50 # disregard the first ik timesteps -ik = 50 # disregard the first ik timesteps def gr(p): - rmsp_f=moment_xyzt(p, 'RMS').rms + rmsp_f = moment_xyzt(p, "RMS").rms - ni=np.shape(rmsp_f)[1] - nj=np.shape(rmsp_f)[2] + ni = np.shape(rmsp_f)[1] + nj = np.shape(rmsp_f)[2] - growth=np.zeros((ni,nj)) + growth = np.zeros((ni, nj)) - for i in range(ni): - for j in range(nj): - growth[i,j]=np.gradient(np.log(rmsp_f[ik::,i,j]))[-1] + for i in range(ni): + for j in range(nj): + growth[i, j] = np.gradient(np.log(rmsp_f[ik::, i, j]))[-1] - return growth + return growth -growth=gr(p) +growth = gr(p) -d=np.ma.masked_array(growth,np.isnan(growth)) +d = np.ma.masked_array(growth, np.isnan(growth)) # masked arrays # http://stackoverflow.com/questions/5480694/numpy-calculate-averages-with-nans-removed -print('Total mean value = ', np.mean(np.ma.masked_array(d,np.isinf(d)))) -mm=np.ma.masked_array(growth[:,nmpy],np.isnan(growth[:,nmpy])) -if np.isinf(np.mean(mm)) : - print('There is an Inf value in the mead plane') - print('Mean value of floating numbers in mead plane is = ', np.mean(np.ma.masked_array(mm,np.isinf(mm)))) +print("Total mean value = ", np.mean(np.ma.masked_array(d, np.isinf(d)))) +mm = np.ma.masked_array(growth[:, nmpy], np.isnan(growth[:, nmpy])) +if np.isinf(np.mean(mm)): + print("There is an Inf value in the mead plane") + print( + "Mean value of floating numbers in mead plane is = ", + np.mean(np.ma.masked_array(mm, np.isinf(mm))), + ) else: - print('Mean value in mead plane= ', np.mean(mm)) + print("Mean value in mead plane= ", np.mean(mm)) diff --git a/examples/elm-pb/Python/plotcollapse.py b/examples/elm-pb/Python/plotcollapse.py index ee64ca381f..f68e7b06aa 100755 --- a/examples/elm-pb/Python/plotcollapse.py +++ b/examples/elm-pb/Python/plotcollapse.py @@ -10,49 +10,52 @@ import os from pathlib import Path -#Dynamic matplotlib settings +# Dynamic matplotlib settings from matplotlib import rcParams -rcParams['font.size'] = 20. -rcParams['legend.fontsize'] = 'small' -rcParams['lines.linewidth'] = 2 -if not os.path.exists('image'): - os.makedirs('image') +rcParams["font.size"] = 20.0 +rcParams["legend.fontsize"] = "small" +rcParams["lines.linewidth"] = 2 + +if not os.path.exists("image"): + os.makedirs("image") filename = Path(__file__).with_name("cbm18_dens8.grid_nx68ny64.nc") with DataFile(str(filename)) as f: g = {v: f.read(v) for v in f.keys()} -psi = old_div((g['psixy'][:, 32] - g['psi_axis']), (g['psi_bndry'] - g['psi_axis'])) +psi = old_div((g["psixy"][:, 32] - g["psi_axis"]), (g["psi_bndry"] - g["psi_axis"])) -path = './data' +path = "./data" plt.figure() -p0=collect('P0', path=path) +p0 = collect("P0", path=path) -p=collect('P', path=path) -res = moment_xyzt(p,'RMS','DC') +p = collect("P", path=path) +res = moment_xyzt(p, "RMS", "DC") rmsp = res.rms dcp = res.dc nt = dcp.shape[0] -plt.plot(psi, p0[:, 32], 'k--', label='t=0') -plt.plot(psi, p0[:, 32] + dcp[nt//4, :, 32], 'r-', label='t='+np.str(nt//4)) -plt.plot(psi, p0[:, 32] + dcp[nt//2, :, 32], 'g-', label='t='+np.str(nt//2)) -plt.plot(psi, p0[:, 32] + dcp[3*nt//4, :, 32], 'b-', label='t='+np.str(3*nt//4)) -plt.plot(psi, p0[:, 32] + dcp[-1, :, 32], 'c-', label='t='+np.str(nt)) +plt.plot(psi, p0[:, 32], "k--", label="t=0") +plt.plot(psi, p0[:, 32] + dcp[nt // 4, :, 32], "r-", label="t=" + np.str(nt // 4)) +plt.plot(psi, p0[:, 32] + dcp[nt // 2, :, 32], "g-", label="t=" + np.str(nt // 2)) +plt.plot( + psi, p0[:, 32] + dcp[3 * nt // 4, :, 32], "b-", label="t=" + np.str(3 * nt // 4) +) +plt.plot(psi, p0[:, 32] + dcp[-1, :, 32], "c-", label="t=" + np.str(nt)) plt.legend() -#plt.xlim(0.6, 1.0) -plt.xlabel(r'Normalized poloidal flux ($\psi$)') -plt.ylabel(r'$\langle p\rangle_\xi$') -plt.title(r'Pressure') +# plt.xlim(0.6, 1.0) +plt.xlabel(r"Normalized poloidal flux ($\psi$)") +plt.ylabel(r"$\langle p\rangle_\xi$") +plt.title(r"Pressure") xmin, xmax = plt.xlim() ymin, ymax = plt.ylim() -#plt.savefig('image/plotcollapse.png', bbox_inches='tight') -#plt.savefig('image/plotcollapse.eps', bbox_inches='tight') +# plt.savefig('image/plotcollapse.png', bbox_inches='tight') +# plt.savefig('image/plotcollapse.eps', bbox_inches='tight') plt.tight_layout() diff --git a/examples/elm-pb/Python/plotmode.py b/examples/elm-pb/Python/plotmode.py index d89fdaf940..9325ee8764 100644 --- a/examples/elm-pb/Python/plotmode.py +++ b/examples/elm-pb/Python/plotmode.py @@ -4,60 +4,57 @@ from builtins import range from past.utils import old_div -from numpy import *; -#from scipy.io import readsav; -import matplotlib.pyplot as plt; +from numpy import * + +# from scipy.io import readsav; +import matplotlib.pyplot as plt # Dynamic matplotlib settings -from matplotlib import rcParams; -rcParams['font.size'] = 20; -rcParams['legend.fontsize'] = 'small'; -rcParams['legend.labelspacing'] = 0.1; -rcParams['lines.linewidth'] = 2; -rcParams['savefig.bbox'] = 'tight'; +from matplotlib import rcParams +rcParams["font.size"] = 20 +rcParams["legend.fontsize"] = "small" +rcParams["legend.labelspacing"] = 0.1 +rcParams["lines.linewidth"] = 2 +rcParams["savefig.bbox"] = "tight" # Create image directory if not exists -import os; -if not os.path.exists('image'): - os.makedirs('image'); - -#fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; -fphi = load('fp.npy') - -plt.figure(); -for i in range(1, 9): - print("Growth rate for mode number", i) - print(gradient(log(abs(fphi[34, 32, i, :])))) - plt.semilogy(((abs(fphi[34, 32, i, :]))), label = 'n=' + str(i * 5)); +import os -plt.legend(loc=2); -plt.xlabel('Time'); -plt.savefig('image/plotmode.png'); -plt.savefig('image/plotmode.eps'); +if not os.path.exists("image"): + os.makedirs("image") +# fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; +fphi = load("fp.npy") -plt.show(block=False); -plt.figure(); +plt.figure() for i in range(1, 9): - plt.plot(abs(fphi[:, 32, i, -1]), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodeamp.png'); -plt.savefig('image/plotmodeamp.eps'); - -plt.show(block=False); - -plt.figure(); + print("Growth rate for mode number", i) + print(gradient(log(abs(fphi[34, 32, i, :])))) + plt.semilogy((abs(fphi[34, 32, i, :])), label="n=" + str(i * 5)) + +plt.legend(loc=2) +plt.xlabel("Time") +plt.savefig("image/plotmode.png") +plt.savefig("image/plotmode.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(old_div(abs(fphi[:, 32, i, -1]),abs(fphi[:, 32, i, -1]).max()), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodenorm.png'); -plt.savefig('image/plotmodenorm.eps'); - -plt.show(); - + plt.plot(abs(fphi[:, 32, i, -1]), label="n=" + str(i * 5)) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodeamp.png") +plt.savefig("image/plotmodeamp.eps") +plt.show(block=False) +plt.figure() +for i in range(1, 9): + plt.plot( + old_div(abs(fphi[:, 32, i, -1]), abs(fphi[:, 32, i, -1]).max()), + label="n=" + str(i * 5), + ) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodenorm.png") +plt.savefig("image/plotmodenorm.eps") +plt.show() diff --git a/examples/elm-pb/Python/plotmode2.py b/examples/elm-pb/Python/plotmode2.py index d0c63f32d9..c298a3a5ef 100644 --- a/examples/elm-pb/Python/plotmode2.py +++ b/examples/elm-pb/Python/plotmode2.py @@ -4,63 +4,61 @@ from builtins import range from past.utils import old_div -from numpy import *; -#from scipy.io import readsav; -import matplotlib.pyplot as plt; +from numpy import * + +# from scipy.io import readsav; +import matplotlib.pyplot as plt from boutdata.collect import collect # Dynamic matplotlib settings -from matplotlib import rcParams; -rcParams['font.size'] = 20; -rcParams['legend.fontsize'] = 'small'; -rcParams['legend.labelspacing'] = 0.1; -rcParams['lines.linewidth'] = 2; -rcParams['savefig.bbox'] = 'tight'; +from matplotlib import rcParams +rcParams["font.size"] = 20 +rcParams["legend.fontsize"] = "small" +rcParams["legend.labelspacing"] = 0.1 +rcParams["lines.linewidth"] = 2 +rcParams["savefig.bbox"] = "tight" # Create image directory if not exists -import os; -if not os.path.exists('image'): - os.makedirs('image'); +import os + +if not os.path.exists("image"): + os.makedirs("image") -path='./data/' -data=collect('P',path=path) +path = "./data/" +data = collect("P", path=path) -#fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; +# fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; fphi = fft.fft(data, axis=3) -plt.figure(); +plt.figure() for i in range(1, 9): - print("Growth rate for mode number", i) - print(gradient(log(abs(fphi[:,34, 32, i])))) - plt.semilogy(((abs(fphi[:,34, 32, i]))), label = 'n=' + str(i * 5)); - -plt.legend(loc=2); -plt.xlabel('Time'); -plt.savefig('image/plotmode.png'); -plt.savefig('image/plotmode.eps'); - - -plt.show(block=False); -plt.figure(); + print("Growth rate for mode number", i) + print(gradient(log(abs(fphi[:, 34, 32, i])))) + plt.semilogy((abs(fphi[:, 34, 32, i])), label="n=" + str(i * 5)) + +plt.legend(loc=2) +plt.xlabel("Time") +plt.savefig("image/plotmode.png") +plt.savefig("image/plotmode.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(abs(fphi[-1, :, 32, i]), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodeamp.png'); -plt.savefig('image/plotmodeamp.eps'); - -plt.show(block=False); - -plt.figure(); + plt.plot(abs(fphi[-1, :, 32, i]), label="n=" + str(i * 5)) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodeamp.png") +plt.savefig("image/plotmodeamp.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(old_div(abs(fphi[-1, :, 32, i]),abs(fphi[-1, :, 32, i]).max()), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodenorm.png'); -plt.savefig('image/plotmodenorm.eps'); - -plt.show(); + plt.plot( + old_div(abs(fphi[-1, :, 32, i]), abs(fphi[-1, :, 32, i]).max()), + label="n=" + str(i * 5), + ) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodenorm.png") +plt.savefig("image/plotmodenorm.eps") +plt.show() diff --git a/examples/elm-pb/Python/plotphase.py b/examples/elm-pb/Python/plotphase.py index 9225e498ae..10f4279cf4 100755 --- a/examples/elm-pb/Python/plotphase.py +++ b/examples/elm-pb/Python/plotphase.py @@ -4,34 +4,33 @@ from numpy import save, load, angle import matplotlib.pyplot as plt -fphi = load('fphi.npy') +fphi = load("fphi.npy") -fte = load('fte.npy') -phase_te = angle(old_div(fphi,fte)) -save('phase_te', phase_te) +fte = load("fte.npy") +phase_te = angle(old_div(fphi, fte)) +save("phase_te", phase_te) plt.figure() -plt.plot(mean(mean(phase_te[:,:,3,:],axis=1),axis=1)) -plt.title('Te') -plt.savefig('image/phase_te.png') -plt.savefig('image/phase_te.eps') +plt.plot(mean(mean(phase_te[:, :, 3, :], axis=1), axis=1)) +plt.title("Te") +plt.savefig("image/phase_te.png") +plt.savefig("image/phase_te.eps") -fti = load('fti.npy') -phase_ti = angle(old_div(fphi,fti)) -save('phase_ti', phase_ti) +fti = load("fti.npy") +phase_ti = angle(old_div(fphi, fti)) +save("phase_ti", phase_ti) plt.figure() -plt.plot(mean(mean(phase_ti[:,:,3,:],axis=1),axis=1)) -plt.title('ti') -plt.savefig('image/phase_ti.png') -plt.savefig('image/phase_ti.eps') +plt.plot(mean(mean(phase_ti[:, :, 3, :], axis=1), axis=1)) +plt.title("ti") +plt.savefig("image/phase_ti.png") +plt.savefig("image/phase_ti.eps") -fni = load('fni.npy') -phase_ni = angle(old_div(fphi,fni)) -save('phase_ni', phase_ni) +fni = load("fni.npy") +phase_ni = angle(old_div(fphi, fni)) +save("phase_ni", phase_ni) plt.figure() -plt.plot(mean(mean(phase_ni[:,:,3,:],axis=1),axis=1)) -plt.title('ni') -plt.savefig('image/phase_ni.png') -plt.savefig('image/phase_ni.eps') +plt.plot(mean(mean(phase_ni[:, :, 3, :], axis=1), axis=1)) +plt.title("ni") +plt.savefig("image/phase_ni.png") +plt.savefig("image/phase_ni.eps") plt.show() - diff --git a/examples/elm-pb/Python/polslice.py b/examples/elm-pb/Python/polslice.py index fe78495b5a..6a1179b2ae 100644 --- a/examples/elm-pb/Python/polslice.py +++ b/examples/elm-pb/Python/polslice.py @@ -11,31 +11,33 @@ # Specify parameters -path='./data/' +path = "./data/" -variable="P" +variable = "P" p = collect(variable, path=path) -period=15 +period = 15 -grid='../cbm18_dens8.grid_nx68ny64.nc' +grid = "../cbm18_dens8.grid_nx68ny64.nc" ######################################################## # Call plotpolslice once to get extended poloidal grid -r,z,fun=plotpolslice(p[0,:,:,:],grid,period=period,rz=1) +r, z, fun = plotpolslice(p[0, :, :, :], grid, period=period, rz=1) -nx=r.shape[0] # number of points in r -ny=r.shape[1] # number of points in z -nt=p.shape[0] # time intervals +nx = r.shape[0] # number of points in r +ny = r.shape[1] # number of points in z +nt = p.shape[0] # time intervals -fm=np.zeros((nt,nx,ny)) # array to store the time sequence of the poloidal cross section +fm = np.zeros( + (nt, nx, ny) +) # array to store the time sequence of the poloidal cross section -#Compute all time frames +# Compute all time frames for k in range(nt): - fm[k,:,:]=plotpolslice(p[k,:,:,:],grid,period=period,rz=0) + fm[k, :, :] = plotpolslice(p[k, :, :, :], grid, period=period, rz=0) -np.savez('pslice',fm=fm, z=z, r=r) +np.savez("pslice", fm=fm, z=z, r=r) diff --git a/examples/elm-pb/Python/post.py b/examples/elm-pb/Python/post.py index 9bb67347f0..cdaefa98cf 100644 --- a/examples/elm-pb/Python/post.py +++ b/examples/elm-pb/Python/post.py @@ -15,12 +15,12 @@ from mayavi import mlab -path0="./data0/" -path1="./data/" +path0 = "./data0/" +path1 = "./data/" -period=15 +period = 15 -gfile='./cbm18_dens8.grid_nx68ny64.nc' +gfile = "./cbm18_dens8.grid_nx68ny64.nc" with DataFile(gfile) as f: @@ -28,71 +28,115 @@ Dphi0 = collect("Dphi0", path=path0) -phi0 = collect("phi0", path=path1) # needs diamagnetic effects +phi0 = collect("phi0", path=path1) # needs diamagnetic effects # -psixy=g.get('psixy') -PSI_AXIS=g.get('psi_axis') -PSI_BNDRY=g.get('psi_bndry') +psixy = g.get("psixy") +PSI_AXIS = g.get("psi_axis") +PSI_BNDRY = g.get("psi_bndry") # -psix=old_div((psixy[:,32]-PSI_AXIS),(PSI_BNDRY-PSI_AXIS)) -Epsi=-deriv(phi0[:,32],psix) +psix = old_div((psixy[:, 32] - PSI_AXIS), (PSI_BNDRY - PSI_AXIS)) +Epsi = -deriv(phi0[:, 32], psix) # # -fig=figure() -plot(psix,-Dphi0[:,32], 'r', linewidth=5) -plot(psix,Epsi,'k',linewidth=5) -annotate('w/o flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -annotate('w/ flow', xy=(.7, .4), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='r', size=30) -xlabel('Radial $\psi$',fontsize=25) -ylabel('$\Omega(\psi)/\omega_A$',fontsize=25) -ylim([-.05,0]) -xlim([0.4,1.2]) +fig = figure() +plot(psix, -Dphi0[:, 32], "r", linewidth=5) +plot(psix, Epsi, "k", linewidth=5) +annotate( + "w/o flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +annotate( + "w/ flow", + xy=(0.7, 0.4), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="r", + size=30, +) +xlabel("Radial $\psi$", fontsize=25) +ylabel("$\Omega(\psi)/\omega_A$", fontsize=25) +ylim([-0.05, 0]) +xlim([0.4, 1.2]) fig.set_tight_layout(True) show(block=False) p_f0 = collect("P", path=path0) p_f = collect("P", path=path1) # -rmsp_f0=moment_xyzt(p_f0, 'RMS').rms -rmsp_f=moment_xyzt(p_f, 'RMS').rms +rmsp_f0 = moment_xyzt(p_f0, "RMS").rms +rmsp_f = moment_xyzt(p_f, "RMS").rms # -fig=figure(figsize=(10, 8)) -plot(np.gradient(np.log(rmsp_f0[:,34,32])), color='k',linewidth=3) -plot(np.gradient(np.log(rmsp_f[:,34,32])),color='red',linewidth=3) - -ylabel('$\gamma / \omega_A$',fontsize=25) -xlabel('Time$(\\tau_A)$',fontsize=25) -annotate('w/o flow', xy=(.5, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -annotate('w/ flow', xy=(.5, .4), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='r', size=30) -ylim([0,0.5]) -xlim([0,100]) +fig = figure(figsize=(10, 8)) +plot(np.gradient(np.log(rmsp_f0[:, 34, 32])), color="k", linewidth=3) +plot(np.gradient(np.log(rmsp_f[:, 34, 32])), color="red", linewidth=3) + +ylabel("$\gamma / \omega_A$", fontsize=25) +xlabel("Time$(\\tau_A)$", fontsize=25) +annotate( + "w/o flow", + xy=(0.5, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +annotate( + "w/ flow", + xy=(0.5, 0.4), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="r", + size=30, +) +ylim([0, 0.5]) +xlim([0, 100]) fig.set_tight_layout(True) show(block=False) - -plotpolslice(p_f0[50,:,:,:],gfile,period=period, fig=1) -mlab.text(.01,.99,"w/o flow") - -plotpolslice(p_f[50,:,:,:],gfile,period=period, fig=1) -mlab.text(.01,.99,"w/ flow") - -fig=figure() -mode_structure(p_f0[50,:,:,:], g, period=period) -plot([40,40],[0,.014],'k--',linewidth=5) -annotate('w/o flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -ylim([0,0.014]) -xlim([0,80]) +plotpolslice(p_f0[50, :, :, :], gfile, period=period, fig=1) +mlab.text(0.01, 0.99, "w/o flow") + +plotpolslice(p_f[50, :, :, :], gfile, period=period, fig=1) +mlab.text(0.01, 0.99, "w/ flow") + +fig = figure() +mode_structure(p_f0[50, :, :, :], g, period=period) +plot([40, 40], [0, 0.014], "k--", linewidth=5) +annotate( + "w/o flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +ylim([0, 0.014]) +xlim([0, 80]) fig.set_tight_layout(True) show(block=False) figure() -mode_structure(p_f[50,:,:,:], g, period=period) -plot([40,40],[0,.014],'k--',linewidth=5) -annotate('w/ flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='k', size=30) -ylim([0,0.0001]) -xlim([0,80]) +mode_structure(p_f[50, :, :, :], g, period=period) +plot([40, 40], [0, 0.014], "k--", linewidth=5) +annotate( + "w/ flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="k", + size=30, +) +ylim([0, 0.0001]) +xlim([0, 80]) show(block=False) show() diff --git a/examples/elm-pb/Python/read_elmsize.py b/examples/elm-pb/Python/read_elmsize.py index fcd3d24e53..340f57ce7b 100644 --- a/examples/elm-pb/Python/read_elmsize.py +++ b/examples/elm-pb/Python/read_elmsize.py @@ -4,12 +4,12 @@ from pylab import save, figure, plot, title, xlabel, ylabel, show, tight_layout from elm_size import elm_size -path='./data' +path = "./data" -t_array=collect('t_array', path=path) -save('t_array.dat', t_array) -p0=collect('P0', path=path) -save('p0.dat', p0) +t_array = collect("t_array", path=path) +save("t_array.dat", t_array) +p0 = collect("P0", path=path) +save("p0.dat", p0) # n0=collect('n0', path=path) @@ -22,27 +22,27 @@ with DataFile("./cbm18_dens8.grid_nx68ny64.nc") as f: gfile = {v: f.read(v) for v in f.keys()} -p=collect('P', path=path) -save('p.dat', p) -res=moment_xyzt(p,'RMS','DC') -rmsp=res.rms -dcp=res.dc -save('rmsp.dat', rmsp) -save('dcp.dat', dcp) -elmsp=elm_size(dcp,p0,gfile,yind=32,Bbar=gfile['bmag']) -save('elmsp.dat', elmsp) +p = collect("P", path=path) +save("p.dat", p) +res = moment_xyzt(p, "RMS", "DC") +rmsp = res.rms +dcp = res.dc +save("rmsp.dat", rmsp) +save("dcp.dat", dcp) +elmsp = elm_size(dcp, p0, gfile, yind=32, Bbar=gfile["bmag"]) +save("elmsp.dat", elmsp) figure(0) -plot(t_array,elmsp.s2, 'k-') -xlabel('t/Ta') -ylabel('Elm size') -title('Elm size, P') +plot(t_array, elmsp.s2, "k-") +xlabel("t/Ta") +ylabel("Elm size") +title("Elm size, P") tight_layout() show() -phi=collect('phi', path=path ) -save('phi.dat', phi) -res=moment_xyzt( phi, 'DC', 'RMS') -save('dcphi.dat',res.dc) -save('rmsphi.dat', res.rms) +phi = collect("phi", path=path) +save("phi.dat", phi) +res = moment_xyzt(phi, "DC", "RMS") +save("dcphi.dat", res.dc) +save("rmsphi.dat", res.rms) diff --git a/examples/elm-pb/Python/showpolslice.py b/examples/elm-pb/Python/showpolslice.py index d292575e35..072c53dcd6 100644 --- a/examples/elm-pb/Python/showpolslice.py +++ b/examples/elm-pb/Python/showpolslice.py @@ -6,10 +6,12 @@ import numpy as np from tvtk.tools import visual + try: from enthought.mayavi import mlab except ImportError: - try: from mayavi import mlab + try: + from mayavi import mlab except ImportError: print("No mlab available") @@ -17,40 +19,42 @@ ########################### # Read polslice array -npzfile=np.load('pslice.npz') -r=npzfile['r'] -z=npzfile['z'] -fm=npzfile['fm'] +npzfile = np.load("pslice.npz") +r = npzfile["r"] +z = npzfile["z"] +fm = npzfile["fm"] ######################################################## # Set up the window -f = mlab.figure(size=(800,600)) +f = mlab.figure(size=(800, 600)) # Tell visual to use this as the viewer. visual.set_viewer(f) ######################################################## # Do the appropriate graph -#s = mlab.contour_surf(r,z,fun, contours=30, line_width=.5, transparent=True) -#s=mlab.surf(r,z,fun, colormap='Spectral') -s = mlab.mesh(r,z,fm[0,:,:], scalars=fm[0,:,:], colormap='PuOr')#, wrap_scale='true')#, representation='wireframe') -s.enable_contours=True -s.contour.filled_contours=True +# s = mlab.contour_surf(r,z,fun, contours=30, line_width=.5, transparent=True) +# s=mlab.surf(r,z,fun, colormap='Spectral') +s = mlab.mesh( + r, z, fm[0, :, :], scalars=fm[0, :, :], colormap="PuOr" +) # , wrap_scale='true')#, representation='wireframe') +s.enable_contours = True +s.contour.filled_contours = True # Define perspective and optional attributes. You can also implement from the window afterwards -mlab.view(0,0) -#mlab.view(-94.159958841373324, +mlab.view(0, 0) +# mlab.view(-94.159958841373324, # 53.777002382688906, # 8.2311808018087582) mlab.draw(f) mlab.colorbar(orientation="vertical") -#mlab.axes() -#mlab.outline() +# mlab.axes() +# mlab.outline() ######################################################## # mlab animation -anim(s,fm, save=True) +anim(s, fm, save=True) diff --git a/examples/elm-pb/Python/sprofiles.py b/examples/elm-pb/Python/sprofiles.py index 9d8eea48de..244599f8af 100644 --- a/examples/elm-pb/Python/sprofiles.py +++ b/examples/elm-pb/Python/sprofiles.py @@ -16,45 +16,46 @@ with DataFile(gfile) as f: g = {v: f.read(v) for v in f.keys()} -var=collect("P", path=path) +var = collect("P", path=path) -sol=surface_average(var, g) -#sol=np.mean(var,axis=3) +sol = surface_average(var, g) +# sol=np.mean(var,axis=3) -p0av=collect("P0", path=path) +p0av = collect("P0", path=path) -q=np.zeros(sol.shape) +q = np.zeros(sol.shape) for i in range(sol.shape[1]): - q[:,i]=sol[:,i]+p0av[:,0] + q[:, i] = sol[:, i] + p0av[:, 0] -psixy=g.get('psixy') -psi0=g.get('psi_axis') -psix=g.get('psi_bndry') +psixy = g.get("psixy") +psi0 = g.get("psi_axis") +psix = g.get("psi_bndry") -xarr = psixy[:,0] -xarr = old_div((xarr - psi0), (-psi0 + psix)) #for this grid +xarr = psixy[:, 0] +xarr = old_div((xarr - psi0), (-psi0 + psix)) # for this grid -fig=figure() +fig = figure() -nt=q.shape[1] +nt = q.shape[1] -plot(xarr, p0av,'k',label='t=0') -plot(xarr,q[:,nt/4],'r',label='t='+np.str(nt/4)) -plot(xarr,q[:,nt/2],'b',label='t='+np.str(nt/2)) -plot(xarr,q[:,3*nt/4],'g',label='t='+np.str(3*nt/4)) -plot(xarr, q[:,-1],'k',label='t='+np.str(nt)) +plot(xarr, p0av, "k", label="t=0") +plot(xarr, q[:, nt / 4], "r", label="t=" + np.str(nt / 4)) +plot(xarr, q[:, nt / 2], "b", label="t=" + np.str(nt / 2)) +plot(xarr, q[:, 3 * nt / 4], "g", label="t=" + np.str(3 * nt / 4)) +plot(xarr, q[:, -1], "k", label="t=" + np.str(nt)) from collections import OrderedDict + handles, labels = gca().get_legend_handles_labels() by_label = OrderedDict(list(zip(labels, handles))) legend(list(by_label.values()), list(by_label.keys())) -xlabel(r"$\psi$",fontsize=25) -ylabel(r"$2 \mu_0 / B^2$",fontsize=25) +xlabel(r"$\psi$", fontsize=25) +ylabel(r"$2 \mu_0 / B^2$", fontsize=25) fig.set_tight_layout(True) diff --git a/examples/elm-pb/runexample.py b/examples/elm-pb/runexample.py index f7ebc01028..b49902d29c 100755 --- a/examples/elm-pb/runexample.py +++ b/examples/elm-pb/runexample.py @@ -28,18 +28,22 @@ # Calculate RMS in toroidal direction prms = np.sqrt(np.mean(p**2, axis=3)) -growth = np.gradient(np.log(prms[:,42,32])) +growth = np.gradient(np.log(prms[:, 42, 32])) # Final growth-rate gamma = growth[-2] import matplotlib.pyplot as plt -plt.plot(tarr, prms[:,42,32], label='Outboard midplane') -plt.plot( [tarr[0], tarr[-1]], - [prms[-1,42,32]*np.exp(gamma*(tarr[0] - tarr[-1])), prms[-1,42,32]], '--', label=r'$\gamma =$'+str(gamma)) +plt.plot(tarr, prms[:, 42, 32], label="Outboard midplane") +plt.plot( + [tarr[0], tarr[-1]], + [prms[-1, 42, 32] * np.exp(gamma * (tarr[0] - tarr[-1])), prms[-1, 42, 32]], + "--", + label=r"$\gamma =$" + str(gamma), +) -plt.yscale('log') +plt.yscale("log") plt.grid() plt.xlabel(r"Time [$1/\tau_A$]") @@ -57,19 +61,21 @@ # Take a poloidal slice at fixed toroidal angle from boutdata.pol_slice import pol_slice -p2d = pol_slice(p[-1,:,:,:], 'cbm18_dens8.grid_nx68ny64.nc', n=15, zangle=0.0) + +p2d = pol_slice(p[-1, :, :, :], "cbm18_dens8.grid_nx68ny64.nc", n=15, zangle=0.0) # Read grid file to get coordinates from boututils.datafile import DataFile -g = DataFile('cbm18_dens8.grid_nx68ny64.nc') -Rxy = g.read("Rxy") # Major radius [m] -Zxy = g.read("Zxy") # Height [m] +g = DataFile("cbm18_dens8.grid_nx68ny64.nc") + +Rxy = g.read("Rxy") # Major radius [m] +Zxy = g.read("Zxy") # Height [m] plt.contourf(Rxy, Zxy, p2d, 30) -plt.axis('equal') # Maintain aspect ratio +plt.axis("equal") # Maintain aspect ratio -plt.colorbar() # Plot a bar down the side with a color scale +plt.colorbar() # Plot a bar down the side with a color scale plt.savefig("poloidal_slice.pdf") diff --git a/examples/fci-wave/compare-density.py b/examples/fci-wave/compare-density.py index c039c250b6..16e620a08f 100644 --- a/examples/fci-wave/compare-density.py +++ b/examples/fci-wave/compare-density.py @@ -1,4 +1,3 @@ - import matplotlib.pyplot as plt from boutdata import collect import numpy as np @@ -9,60 +8,61 @@ # Note: Data from fci-wave-logn examples commented out. data_noboundary = [ - ("div", "Model 1 (density, point interpolation)") - ,("div-integrate", "Model 2 (density, area integration)") - ,("logn", "Model 3 (log density, area integration)") - #,("../fci-wave-logn/div-integrate", "Model 5 (velocity, log density, area integration)") + ("div", "Model 1 (density, point interpolation)"), + ("div-integrate", "Model 2 (density, area integration)"), + ("logn", "Model 3 (log density, area integration)"), + # ,("../fci-wave-logn/div-integrate", "Model 5 (velocity, log density, area integration)") ] data_boundary = [ - ("boundary", "Model 2 (density, momentum)") - ,("boundary-logn", "Model 3 (log density, momentum)") - #,("../fci-wave-logn/boundary", "Model 5 (log density, velocity)") - ] + ("boundary", "Model 2 (density, momentum)"), + ("boundary-logn", "Model 3 (log density, momentum)"), + # ,("../fci-wave-logn/boundary", "Model 5 (log density, velocity)") +] # Change this to select no boundary or boundary cases data = data_noboundary if run: from boututils.run_wrapper import shell_safe, launch_safe + shell_safe("make > make.log") - for path,label in data: - launch_safe("./fci-wave -d "+path, nproc=nproc, pipe=False) + for path, label in data: + launch_safe("./fci-wave -d " + path, nproc=nproc, pipe=False) -# Collect the results into a dictionary +# Collect the results into a dictionary sum_n_B = {} -for path,label in data: +for path, label in data: n = collect("n", path=path) Bxyz = collect("Bxyz", path=path) time = collect("t_array", path=path) - + nt, nx, ny, nz = n.shape - + n_B = np.ndarray(nt) for t in range(nt): - n_B[t] = np.sum(n[t,:,:,:] / Bxyz) + n_B[t] = np.sum(n[t, :, :, :] / Bxyz) sum_n_B[path] = (time, n_B) # Plot the density at the final time - + plt.figure() - plt.contourf(n[-1,:,0,:].T, 100) + plt.contourf(n[-1, :, 0, :].T, 100) plt.colorbar() plt.xlabel("Major radius") plt.ylabel("Height") - plt.title("Density n, "+label) - plt.savefig(path+".pdf") + plt.title("Density n, " + label) + plt.savefig(path + ".pdf") plt.show() # Make a plot comparing total sum density / B - + plt.figure() -for path,label in data: +for path, label in data: time, n_B = sum_n_B[path] plt.plot(time, n_B, label=label) plt.legend() @@ -71,4 +71,3 @@ plt.savefig("compare-density.pdf") plt.show() - diff --git a/examples/finite-volume/diffusion/mms.py b/examples/finite-volume/diffusion/mms.py index 2c609ca82e..31b7714727 100644 --- a/examples/finite-volume/diffusion/mms.py +++ b/examples/finite-volume/diffusion/mms.py @@ -5,26 +5,26 @@ from math import pi # Length of the y domain -Ly = 10. +Ly = 10.0 # metric tensor metric = Metric() # Identity # Define solution in terms of input x,y,z -f = 1 + 0.1*sin(2*y - t) -k = 1 + 0.1*sin(y) +f = 1 + 0.1 * sin(2 * y - t) +k = 1 + 0.1 * sin(y) # Turn solution into real x and z coordinates -replace = [ (y, metric.y*2*pi/Ly) ] +replace = [(y, metric.y * 2 * pi / Ly)] f = f.subs(replace) -k = k.subs(replace) +k = k.subs(replace) ############################## # Calculate time derivatives -dfdt = Div_par( k * Grad_par(f) ) +dfdt = Div_par(k * Grad_par(f)) ############################# # Calculate sources @@ -32,7 +32,7 @@ Sf = diff(f, t) - dfdt # Substitute back to get input y coordinates -replace = [ (metric.y, y*Ly/(2*pi) ) ] +replace = [(metric.y, y * Ly / (2 * pi))] k = k.subs(replace) f = f.subs(replace) diff --git a/examples/finite-volume/fluid/mms.py b/examples/finite-volume/fluid/mms.py index 8ed8fba517..a31782e2c7 100644 --- a/examples/finite-volume/fluid/mms.py +++ b/examples/finite-volume/fluid/mms.py @@ -5,19 +5,19 @@ from math import pi # Length of the y domain -Ly = 10. +Ly = 10.0 # metric tensor metric = Metric() # Identity # Define solution in terms of input x,y,z -n = 1 + 0.1*sin(2*y - t) -p = 1 + 0.1*cos(3*y + t) -nv = 0.1*sin(y + 2*t) +n = 1 + 0.1 * sin(2 * y - t) +p = 1 + 0.1 * cos(3 * y + t) +nv = 0.1 * sin(y + 2 * t) # Turn solution into real x and z coordinates -replace = [ (y, metric.y*2*pi/Ly) ] +replace = [(y, metric.y * 2 * pi / Ly)] n = n.subs(replace) p = p.subs(replace) @@ -27,16 +27,16 @@ # Calculate time derivatives v = nv / n -gamma = 5./3 +gamma = 5.0 / 3 # Density equation -dndt = - Div_par(nv) +dndt = -Div_par(nv) # Pressure equation -dpdt = - Div_par(p*v) - (gamma-1.0)*p*Div_par(v) +dpdt = -Div_par(p * v) - (gamma - 1.0) * p * Div_par(v) # Momentum equation -dnvdt = - Div_par(nv*v) - Grad_par(p) +dnvdt = -Div_par(nv * v) - Grad_par(p) ############################# # Calculate sources @@ -46,7 +46,7 @@ Snv = diff(nv, t) - dnvdt # Substitute back to get input y coordinates -replace = [ (metric.y, y*Ly/(2*pi) ) ] +replace = [(metric.y, y * Ly / (2 * pi))] n = n.subs(replace) p = p.subs(replace) diff --git a/examples/laplace-petsc3d/plotcheck.py b/examples/laplace-petsc3d/plotcheck.py index 707e5db1ee..7f2758dd81 100755 --- a/examples/laplace-petsc3d/plotcheck.py +++ b/examples/laplace-petsc3d/plotcheck.py @@ -10,27 +10,27 @@ xg = 2 -f = collect('f', path=datadir)[xg:-xg, :, :] -rhs = collect('rhs', path=datadir)[xg:-xg, :, :] -rhs_check = collect('rhs_check', path=datadir)[xg:-xg, :, :] -error = collect('error', path=datadir)[xg:-xg, :, :] +f = collect("f", path=datadir)[xg:-xg, :, :] +rhs = collect("rhs", path=datadir)[xg:-xg, :, :] +rhs_check = collect("rhs_check", path=datadir)[xg:-xg, :, :] +error = collect("error", path=datadir)[xg:-xg, :, :] pyplot.subplot(221) pyplot.pcolormesh(f[:, yind, :]) pyplot.colorbar() -pyplot.title('f') +pyplot.title("f") pyplot.subplot(222) pyplot.pcolormesh(rhs[:, yind, :]) pyplot.colorbar() -pyplot.title('rhs') +pyplot.title("rhs") pyplot.subplot(223) pyplot.pcolormesh(rhs_check[:, yind, :]) pyplot.colorbar() -pyplot.title('rhs_check') +pyplot.title("rhs_check") pyplot.subplot(224) pyplot.pcolormesh(error[:, yind, :]) pyplot.colorbar() -pyplot.title('error') +pyplot.title("error") pyplot.show() diff --git a/examples/orszag-tang/generate.py b/examples/orszag-tang/generate.py index 3ba6dbbea9..acce7497c3 100644 --- a/examples/orszag-tang/generate.py +++ b/examples/orszag-tang/generate.py @@ -2,14 +2,16 @@ from __future__ import division from builtins import range from past.utils import old_div + # the Scientific Python netCDF 3 interface # http://dirac.cnrs-orleans.fr/ScientificPython/ -#from Scientific.IO.NetCDF import NetCDFFile as Dataset +# from Scientific.IO.NetCDF import NetCDFFile as Dataset # the 'classic' version of the netCDF4 python interface # http://code.google.com/p/netcdf4-python/ import numpy as np from netCDF4 import Dataset -from numpy import dtype # array module from http://numpy.scipy.org +from numpy import dtype # array module from http://numpy.scipy.org + """ This example writes some surface pressure and temperatures The companion program sfc_pres_temp_rd.py shows how to read the netCDF @@ -30,44 +32,45 @@ # # the output array to write will be nx x ny -ny = 100; nx = ny + 4 +ny = 100 +nx = ny + 4 # dy of grid dy = old_div(1.0, np.float(ny)) dx = dy # create grid -dxarr=np.zeros((nx,ny),dtype='float32')+dx -dyarr=np.zeros((nx,ny),dtype='float32')+dy +dxarr = np.zeros((nx, ny), dtype="float32") + dx +dyarr = np.zeros((nx, ny), dtype="float32") + dy -xarr=np.arange(0.,np.float(nx),1.,dtype='float32')*dx -yarr=np.arange(0.,np.float(ny),1.,dtype='float32')*dy +xarr = np.arange(0.0, np.float(nx), 1.0, dtype="float32") * dx +yarr = np.arange(0.0, np.float(ny), 1.0, dtype="float32") * dy # compute initial variables -rho=np.zeros((nx,ny),dtype='float32')+old_div(25.,(36.*np.pi)) -p=np.zeros((nx,ny),dtype='float32')+old_div(5.,(12.*np.pi)) +rho = np.zeros((nx, ny), dtype="float32") + old_div(25.0, (36.0 * np.pi)) +p = np.zeros((nx, ny), dtype="float32") + old_div(5.0, (12.0 * np.pi)) -rho=1. -p=old_div(rho,3.) +rho = 1.0 +p = old_div(rho, 3.0) -v_x=np.zeros((nx,ny),dtype='float32') -Bx=np.zeros((nx,ny),dtype='float32') +v_x = np.zeros((nx, ny), dtype="float32") +Bx = np.zeros((nx, ny), dtype="float32") for y in range(ny): - v_x[:,y]=-np.sin(2.*np.pi*yarr[y]) - Bx[:,y]=-np.sin(2.*np.pi*yarr[y]) - -#Bx=Bx/np.sqrt(4.*np.pi) + v_x[:, y] = -np.sin(2.0 * np.pi * yarr[y]) + Bx[:, y] = -np.sin(2.0 * np.pi * yarr[y]) +# Bx=Bx/np.sqrt(4.*np.pi) -v_y=np.zeros((nx,ny),dtype='float32') -By=np.zeros((nx,ny),dtype='float32') + +v_y = np.zeros((nx, ny), dtype="float32") +By = np.zeros((nx, ny), dtype="float32") for x in range(nx): - v_y[x,:]=np.sin(2.*np.pi*xarr[x]) - By[x,:]=np.sin(4.*np.pi*xarr[x]) - -#By=By/np.sqrt(4.*np.pi) + v_y[x, :] = np.sin(2.0 * np.pi * xarr[x]) + By[x, :] = np.sin(4.0 * np.pi * xarr[x]) + +# By=By/np.sqrt(4.*np.pi) # Domain inside core (periodic) @@ -76,55 +79,62 @@ ixseps2 = nx # open a new netCDF file for writing. -ncfile = Dataset('otv.grd.128.nc','w', format='NETCDF3_CLASSIC') +ncfile = Dataset("otv.grd.128.nc", "w", format="NETCDF3_CLASSIC") # output data. # create the nx and ny dimensions. -ncfile.createDimension('x',nx) -ncfile.createDimension('y',ny) -ncfile.createDimension('single',1) +ncfile.createDimension("x", nx) +ncfile.createDimension("y", ny) +ncfile.createDimension("single", 1) # create and write nx,ny variables -nxx=ncfile.createVariable('nx','i4',('single')) -nyy=ncfile.createVariable('ny','i4',('single')) +nxx = ncfile.createVariable("nx", "i4", ("single")) +nyy = ncfile.createVariable("ny", "i4", ("single")) -nxx[:]=nx -nyy[:]=ny +nxx[:] = nx +nyy[:] = ny # Define the coordinate variables. They will hold the coordinate # information, that is, xarr,yarr -dx = ncfile.createVariable('dx',dtype('float32').char,('x','y')) -dy = ncfile.createVariable('dy',dtype('float32').char,('x','y',)) +dx = ncfile.createVariable("dx", dtype("float32").char, ("x", "y")) +dy = ncfile.createVariable( + "dy", + dtype("float32").char, + ( + "x", + "y", + ), +) # write data to coordinate vars. -dx[:,:] = dxarr -dy[:,:] = dyarr +dx[:, :] = dxarr +dy[:, :] = dyarr # create and write ixseps* dimensions. -ix1=ncfile.createVariable('ixseps1','i4',('single')) -ix2=ncfile.createVariable('ixseps2','i4',('single')) +ix1 = ncfile.createVariable("ixseps1", "i4", ("single")) +ix2 = ncfile.createVariable("ixseps2", "i4", ("single")) -ix1[:]=ixseps1 -ix2[:]=ixseps2 +ix1[:] = ixseps1 +ix2[:] = ixseps2 -# create the corresponding variables -rho0 = ncfile.createVariable('rho0',dtype('float32').char,('x','y')) -p0 = ncfile.createVariable('p0',dtype('float32').char,('x','y')) -v0_x = ncfile.createVariable('v0_x',dtype('float32').char,('x','y')) -v0_y = ncfile.createVariable('v0_y',dtype('float32').char,('x','y')) -B0x = ncfile.createVariable('B0x',dtype('float32').char,('x','y')) -B0y = ncfile.createVariable('B0y',dtype('float32').char,('x','y')) +# create the corresponding variables +rho0 = ncfile.createVariable("rho0", dtype("float32").char, ("x", "y")) +p0 = ncfile.createVariable("p0", dtype("float32").char, ("x", "y")) +v0_x = ncfile.createVariable("v0_x", dtype("float32").char, ("x", "y")) +v0_y = ncfile.createVariable("v0_y", dtype("float32").char, ("x", "y")) +B0x = ncfile.createVariable("B0x", dtype("float32").char, ("x", "y")) +B0y = ncfile.createVariable("B0y", dtype("float32").char, ("x", "y")) # write data to variables. -rho0[:,:]=rho -p0[:,:]=p -v0_x[:,:]=v_x -v0_y[:,:]=v_y -B0x[:,:]=Bx -B0y[:,:]=By +rho0[:, :] = rho +p0[:, :] = p +v0_x[:, :] = v_x +v0_y[:, :] = v_y +B0x[:, :] = Bx +B0y[:, :] = By ncfile.close() -print('*** SUCCESS writing file otv.grd.py.nc!') +print("*** SUCCESS writing file otv.grd.py.nc!") diff --git a/examples/performance/bracket/scaling_parser.py b/examples/performance/bracket/scaling_parser.py index d7a23842d4..15f0ea8780 100644 --- a/examples/performance/bracket/scaling_parser.py +++ b/examples/performance/bracket/scaling_parser.py @@ -2,8 +2,7 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): @@ -13,7 +12,7 @@ def read_file(filename): for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} diff --git a/examples/performance/ddx/new_scaling_parser.py b/examples/performance/ddx/new_scaling_parser.py index d2a5fee191..5c0c92ff5e 100644 --- a/examples/performance/ddx/new_scaling_parser.py +++ b/examples/performance/ddx/new_scaling_parser.py @@ -2,19 +2,19 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): continue from collections import OrderedDict - case_lines = OrderedDict() #{} + + case_lines = OrderedDict() # {} for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} @@ -25,12 +25,13 @@ def read_file(filename): for title, col in zip(titles, line[:-1]): cases_weak[title].append(float(col)) - axis = cases_weak['Local grid'] + axis = cases_weak["Local grid"] data = [cases_weak[x] for x in case_lines] labels = [case_lines[x] for x in case_lines] - return {'axis':axis, 'data':data, 'labels':labels} + return {"axis": axis, "data": data, "labels": labels} + -def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): +def getScan(baseName="timing_{n}.txt", nthreads=(1, 2, 4, 8, 16, 32)): from numpy import zeros dataS = [] @@ -38,17 +39,16 @@ def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): f = baseName.format(n=n) dataS.append(read_file(f)) - nnt = len(dataS) - nlines = len(dataS[0]['data']) - nval = len(dataS[0]['data'][0]) - rawDat = zeros([nnt,nval,nlines]) + nlines = len(dataS[0]["data"]) + nval = len(dataS[0]["data"][0]) + rawDat = zeros([nnt, nval, nlines]) for i, dat in enumerate(dataS): - print(len(dat['data'])) - - rawDat[i,:,:] = dat['data'] + print(len(dat["data"])) + + rawDat[i, :, :] = dat["data"] - axes = [nthreads, dataS[0]['axis']] + axes = [nthreads, dataS[0]["axis"]] - return axes, rawDat, dataS[0]['labels'] + return axes, rawDat, dataS[0]["labels"] diff --git a/examples/performance/ddy/new_scaling_parser.py b/examples/performance/ddy/new_scaling_parser.py index d2a5fee191..5c0c92ff5e 100644 --- a/examples/performance/ddy/new_scaling_parser.py +++ b/examples/performance/ddy/new_scaling_parser.py @@ -2,19 +2,19 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): continue from collections import OrderedDict - case_lines = OrderedDict() #{} + + case_lines = OrderedDict() # {} for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} @@ -25,12 +25,13 @@ def read_file(filename): for title, col in zip(titles, line[:-1]): cases_weak[title].append(float(col)) - axis = cases_weak['Local grid'] + axis = cases_weak["Local grid"] data = [cases_weak[x] for x in case_lines] labels = [case_lines[x] for x in case_lines] - return {'axis':axis, 'data':data, 'labels':labels} + return {"axis": axis, "data": data, "labels": labels} + -def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): +def getScan(baseName="timing_{n}.txt", nthreads=(1, 2, 4, 8, 16, 32)): from numpy import zeros dataS = [] @@ -38,17 +39,16 @@ def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): f = baseName.format(n=n) dataS.append(read_file(f)) - nnt = len(dataS) - nlines = len(dataS[0]['data']) - nval = len(dataS[0]['data'][0]) - rawDat = zeros([nnt,nval,nlines]) + nlines = len(dataS[0]["data"]) + nval = len(dataS[0]["data"][0]) + rawDat = zeros([nnt, nval, nlines]) for i, dat in enumerate(dataS): - print(len(dat['data'])) - - rawDat[i,:,:] = dat['data'] + print(len(dat["data"])) + + rawDat[i, :, :] = dat["data"] - axes = [nthreads, dataS[0]['axis']] + axes = [nthreads, dataS[0]["axis"]] - return axes, rawDat, dataS[0]['labels'] + return axes, rawDat, dataS[0]["labels"] diff --git a/examples/performance/ddz/new_scaling_parser.py b/examples/performance/ddz/new_scaling_parser.py index d2a5fee191..5c0c92ff5e 100644 --- a/examples/performance/ddz/new_scaling_parser.py +++ b/examples/performance/ddz/new_scaling_parser.py @@ -2,19 +2,19 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): continue from collections import OrderedDict - case_lines = OrderedDict() #{} + + case_lines = OrderedDict() # {} for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} @@ -25,12 +25,13 @@ def read_file(filename): for title, col in zip(titles, line[:-1]): cases_weak[title].append(float(col)) - axis = cases_weak['Local grid'] + axis = cases_weak["Local grid"] data = [cases_weak[x] for x in case_lines] labels = [case_lines[x] for x in case_lines] - return {'axis':axis, 'data':data, 'labels':labels} + return {"axis": axis, "data": data, "labels": labels} + -def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): +def getScan(baseName="timing_{n}.txt", nthreads=(1, 2, 4, 8, 16, 32)): from numpy import zeros dataS = [] @@ -38,17 +39,16 @@ def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): f = baseName.format(n=n) dataS.append(read_file(f)) - nnt = len(dataS) - nlines = len(dataS[0]['data']) - nval = len(dataS[0]['data'][0]) - rawDat = zeros([nnt,nval,nlines]) + nlines = len(dataS[0]["data"]) + nval = len(dataS[0]["data"][0]) + rawDat = zeros([nnt, nval, nlines]) for i, dat in enumerate(dataS): - print(len(dat['data'])) - - rawDat[i,:,:] = dat['data'] + print(len(dat["data"])) + + rawDat[i, :, :] = dat["data"] - axes = [nthreads, dataS[0]['axis']] + axes = [nthreads, dataS[0]["axis"]] - return axes, rawDat, dataS[0]['labels'] + return axes, rawDat, dataS[0]["labels"] diff --git a/examples/performance/iterator/scaling_parser.py b/examples/performance/iterator/scaling_parser.py index d7a23842d4..15f0ea8780 100644 --- a/examples/performance/iterator/scaling_parser.py +++ b/examples/performance/iterator/scaling_parser.py @@ -2,8 +2,7 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): @@ -13,7 +12,7 @@ def read_file(filename): for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} diff --git a/examples/staggered_grid/generate.py b/examples/staggered_grid/generate.py index da84e9c2e6..a5a0120612 100755 --- a/examples/staggered_grid/generate.py +++ b/examples/staggered_grid/generate.py @@ -4,11 +4,11 @@ # Generate an input mesh # -from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries +from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries -nx = 5 # Minimum is 5: 2 boundary, one evolved +nx = 5 # Minimum is 5: 2 boundary, one evolved ny = 32 # Minimum 5. Should be divisible by number of processors (so powers of 2 nice) -dy = 1. # distance between points in y, in m/g22/lengthunit +dy = 1.0 # distance between points in y, in m/g22/lengthunit ixseps1 = -1 ixseps2 = -1 diff --git a/examples/subsampling/show.py b/examples/subsampling/show.py index 34c6ee3084..b79c5e2964 100755 --- a/examples/subsampling/show.py +++ b/examples/subsampling/show.py @@ -11,14 +11,14 @@ for pack in monitors: filename, data_name = pack - t = DataFile(path+'/'+filename+'.dmp.0.nc').read('t_array') - data = DataFile(path+'/'+filename+'.dmp.0.nc').read(data_name).flatten() + t = DataFile(path + "/" + filename + ".dmp.0.nc").read("t_array") + data = DataFile(path + "/" + filename + ".dmp.0.nc").read(data_name).flatten() plt.plot(t, data, label="{} {}".format(filename, data_name)) -time = DataFile(path+'/BOUT.dmp.0.nc').read('t_array') -data = DataFile(path+'/BOUT.dmp.0.nc').read("T")[:, 2, 2, 0] +time = DataFile(path + "/BOUT.dmp.0.nc").read("t_array") +data = DataFile(path + "/BOUT.dmp.0.nc").read("T")[:, 2, 2, 0] -plt.plot(time, data, marker='+', label="BOUT++ T") +plt.plot(time, data, marker="+", label="BOUT++ T") plt.xlabel("Time") plt.legend() diff --git a/examples/wave-slab/generate.py b/examples/wave-slab/generate.py index 0a2e8f3049..12698d1e24 100755 --- a/examples/wave-slab/generate.py +++ b/examples/wave-slab/generate.py @@ -51,7 +51,7 @@ for x in range(nx): Bpxy[x, :] = Bpx[x] -Bxy = sqrt(Bpxy ** 2 + Bt ** 2) +Bxy = sqrt(Bpxy**2 + Bt**2) # Calculate change in poloidal flux dr = Lx / nx # Constant mesh spacing in radius diff --git a/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb b/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb index c3c39a3c12..4e940b998f 100644 --- a/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb +++ b/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb @@ -40,10 +40,10 @@ "outputs": [], "source": [ "# Note, these can be max 9 due to the current index convention\n", - "nx = 3 # Not including ghost points\n", + "nx = 3 # Not including ghost points\n", "nz = 3\n", "\n", - "startXIndex = 10 # The x indices are the slowest growing indices\n", + "startXIndex = 10 # The x indices are the slowest growing indices\n", "startZIndex = 1 # The z indices are the fastest growing indices" ] }, @@ -85,13 +85,13 @@ "for z in range(nz):\n", " f.append([])\n", " xStart = startZIndex\n", - " xEnd = startXIndex*(nx+1) # +1 due to ghost point\n", + " xEnd = startXIndex * (nx + 1) # +1 due to ghost point\n", " # +startXIndex in the range as the range does not include endpoint\n", - " for xInd in range(xStart, xEnd+startXIndex, 10):\n", - " ind = str(xInd+z)\n", - " if (xInd+z) < startXIndex:\n", - " ind = '0'+str(xInd+z)\n", - " f[z].append(symbols('f_' + ind))\n", + " for xInd in range(xStart, xEnd + startXIndex, 10):\n", + " ind = str(xInd + z)\n", + " if (xInd + z) < startXIndex:\n", + " ind = \"0\" + str(xInd + z)\n", + " f[z].append(symbols(\"f_\" + ind))\n", "\n", "mesh = Matrix(f[::-1])\n", "display(mesh)" @@ -195,21 +195,21 @@ } ], "source": [ - "xVec=[]\n", - "bVec=[]\n", + "xVec = []\n", + "bVec = []\n", "# Do the inner loop, so start ranges at 1\n", "# (nx+1) to include outer ghost point, +1 in the range as the range does not include endpoint\n", - "for x in range(1, (nx+1)+1):\n", - " for z in range(1,nz+1):\n", - " xVec.append(symbols('x_'+str(x)+'_'+str(z)))\n", - " bVec.append(symbols('b_'+str(x)+'_'+str(z)))\n", + "for x in range(1, (nx + 1) + 1):\n", + " for z in range(1, nz + 1):\n", + " xVec.append(symbols(\"x_\" + str(x) + \"_\" + str(z)))\n", + " bVec.append(symbols(\"b_\" + str(x) + \"_\" + str(z)))\n", "\n", "# Do the inner ghost points\n", "# Must count backwards since we are inserting in the front\n", - "for ind in range(nz,0,-1):\n", - " xVec.insert(0, symbols('x_0_'+str(ind)))\n", - " bVec.insert(0, symbols('b_0_'+str(ind)))\n", - " \n", + "for ind in range(nz, 0, -1):\n", + " xVec.insert(0, symbols(\"x_0_\" + str(ind)))\n", + " bVec.insert(0, symbols(\"b_0_\" + str(ind)))\n", + "\n", "display(Matrix(xVec))\n", "display(Matrix(bVec))" ] @@ -274,7 +274,7 @@ "globInd = []\n", "for rows in range(len(xVec)):\n", " cols = []\n", - " for col in range(rows*len(xVec), (rows+1)*len(xVec)):\n", + " for col in range(rows * len(xVec), (rows + 1) * len(xVec)):\n", " cols.append(col)\n", " globInd.append(cols)\n", "\n", @@ -305,10 +305,14 @@ "source": [ "c = []\n", "for x in range(nx):\n", - " indexStart = (startXIndex+1)+(startXIndex*x) # Multiply by 10 due to index system\n", - " indexEnd = (startXIndex+nz+1)+(startXIndex*x) # Multiply by 10 due to index system\n", + " indexStart = (startXIndex + 1) + (\n", + " startXIndex * x\n", + " ) # Multiply by 10 due to index system\n", + " indexEnd = (startXIndex + nz + 1) + (\n", + " startXIndex * x\n", + " ) # Multiply by 10 due to index system\n", " for ind in range(indexStart, indexEnd):\n", - " c.append(symbols('c_'+str(ind)))" + " c.append(symbols(\"c_\" + str(ind)))" ] }, { @@ -328,11 +332,11 @@ "source": [ "# The inner ghost\n", "innerGhostStart = startZIndex\n", - "innerGhostEnd = nz\n", + "innerGhostEnd = nz\n", "ig = []\n", "# +1 in the range as last point is not included\n", - "for z in range(innerGhostStart, innerGhostEnd+1):\n", - " ig.append(symbols('ig_0_'+str(z)))" + "for z in range(innerGhostStart, innerGhostEnd + 1):\n", + " ig.append(symbols(\"ig_0_\" + str(z)))" ] }, { @@ -345,12 +349,12 @@ "source": [ "# The outer ghost\n", "# nx+1 as we want to go past the last inner x grid point\n", - "outerGhostStart = startXIndex*(nx+1) + startZIndex\n", - "outerGhostEnd = startXIndex*(nx+1) + nz\n", + "outerGhostStart = startXIndex * (nx + 1) + startZIndex\n", + "outerGhostEnd = startXIndex * (nx + 1) + nz\n", "og = []\n", "# +1 in the range as last point is not included\n", - "for z in range(outerGhostStart, outerGhostEnd+1):\n", - " og.append(symbols('og_'+str(z)))" + "for z in range(outerGhostStart, outerGhostEnd + 1):\n", + " og.append(symbols(\"og_\" + str(z)))" ] }, { @@ -396,21 +400,27 @@ "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 is there as the matrix indices count from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz-1)-1 # Ending row-1 before the last z-index (last z will be wrapped around)\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz - 1) - 1\n", + " ) # Ending row-1 before the last z-index (last z will be wrapped around)\n", " # +1 in range as last point is not included\n", - " rows = range(startRow, endRow+1)\n", - " cols = range(startRow+1, endRow+1) # Column is shifted +1 from the diagonal\n", - " \n", + " rows = range(startRow, endRow + 1)\n", + " cols = range(startRow + 1, endRow + 1) # Column is shifted +1 from the diagonal\n", + "\n", " # The indices referring to the spatial point in the grid\n", " # The last \"+1\" is fue to the fact that the column is shifted +1 from the diagonal\n", - " startInd = (startXIndex+startZIndex) + (startXIndex*x) + 1\n", - " endInd = (startXIndex+startZIndex) + (nz-1) + (startXIndex*x) + 1 # Wrap around last point\n", + " startInd = (startXIndex + startZIndex) + (startXIndex * x) + 1\n", + " endInd = (\n", + " (startXIndex + startZIndex) + (nz - 1) + (startXIndex * x) + 1\n", + " ) # Wrap around last point\n", " # +1 in range as last point is not included\n", - " inds = range(startInd, endInd+1)\n", - " \n", + " inds = range(startInd, endInd + 1)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('zp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"zp_\" + str(ind))" ] }, { @@ -423,15 +433,17 @@ "source": [ "# The wrap around\n", "# The index referring to the spatial point in the grid\n", - "startInd = startXIndex+startZIndex\n", + "startInd = startXIndex + startZIndex\n", "# The indices referring to the matrix index\n", - "# Last -1 as the matrix indices are counted from 0 \n", - "startRow = (nz+1) + (nz-1) - 1 # nz+1 below from the ghost sub matrix, nz-1 below after that\n", - "startCol = (nz+1)-1 # nz+1 left of the ghost sub matrix\n", + "# Last -1 as the matrix indices are counted from 0\n", + "startRow = (\n", + " (nz + 1) + (nz - 1) - 1\n", + ") # nz+1 below from the ghost sub matrix, nz-1 below after that\n", + "startCol = (nz + 1) - 1 # nz+1 left of the ghost sub matrix\n", "for wrap in range(nx):\n", - " row = startRow+wrap*nz\n", - " col = startCol+wrap*nz\n", - " InvM[row, col] = symbols('zp_'+str(startInd+startXIndex*wrap))" + " row = startRow + wrap * nz\n", + " col = startCol + wrap * nz\n", + " InvM[row, col] = symbols(\"zp_\" + str(startInd + startXIndex * wrap))" ] }, { @@ -452,20 +464,26 @@ "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 is there as the matrix indices count from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz-1)-1 # Ending row-1 before the last z-index (last z will be wrapped around)\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz - 1) - 1\n", + " ) # Ending row-1 before the last z-index (last z will be wrapped around)\n", " # +1 in range as last point is not included\n", - " rows = range(startRow+1, endRow+1) # Row is shifted +1 from the diagonal\n", - " cols = range(startRow, endRow+1)\n", - " \n", + " rows = range(startRow + 1, endRow + 1) # Row is shifted +1 from the diagonal\n", + " cols = range(startRow, endRow + 1)\n", + "\n", " # The indices referring to the spatial point in the grid\n", - " startInd = (startXIndex+startZIndex) + (startXIndex*x)\n", - " endInd = (startXIndex+startZIndex) + (nz-1) + (startXIndex*x) # Wrap around last point\n", + " startInd = (startXIndex + startZIndex) + (startXIndex * x)\n", + " endInd = (\n", + " (startXIndex + startZIndex) + (nz - 1) + (startXIndex * x)\n", + " ) # Wrap around last point\n", " # +1 in range as last point is not included\n", - " inds = range(startInd, endInd+1)\n", - " \n", + " inds = range(startInd, endInd + 1)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('zm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"zm_\" + str(ind))" ] }, { @@ -478,15 +496,19 @@ "source": [ "# The wrap around\n", "# The index referring to the spatial point in the grid\n", - "startInd = startXIndex+startZIndex+(nz-1) # +(nz-1) as this will be the last z point for the current x\n", + "startInd = (\n", + " startXIndex + startZIndex + (nz - 1)\n", + ") # +(nz-1) as this will be the last z point for the current x\n", "# The indices referring to the matrix index\n", - "# Last -1 as the matrix indices are counted from 0 \n", - "startRow = (nz+1)-1 # nz+1 below the ghost sub matrix\n", - "startCol = (nz+1) + (nz-1) - 1 # nz+1 left from the ghost sub matrix, nz-1 left after that\n", + "# Last -1 as the matrix indices are counted from 0\n", + "startRow = (nz + 1) - 1 # nz+1 below the ghost sub matrix\n", + "startCol = (\n", + " (nz + 1) + (nz - 1) - 1\n", + ") # nz+1 left from the ghost sub matrix, nz-1 left after that\n", "for wrap in range(nx):\n", - " row = startRow+wrap*nz\n", - " col = startCol+wrap*nz\n", - " InvM[row, col] = symbols('zm_'+str(startInd+startXIndex*wrap))" + " row = startRow + wrap * nz\n", + " col = startCol + wrap * nz\n", + " InvM[row, col] = symbols(\"zm_\" + str(startInd + startXIndex * wrap))" ] }, { @@ -505,23 +527,29 @@ "outputs": [], "source": [ "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex*2 + startZIndex # *2 as we start at the second inner x-index\n", - "endInd = startInd + (startZIndex*nz) # *nz as this is the last z-index in the current x-index\n", + "startInd = startXIndex * 2 + startZIndex # *2 as we start at the second inner x-index\n", + "endInd = startInd + (\n", + " startZIndex * nz\n", + ") # *nz as this is the last z-index in the current x-index\n", "\n", "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 as the matrix indices counts from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz)-1 # Ending at the row referring to the last z-index\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz) - 1\n", + " ) # Ending at the row referring to the last z-index\n", " # Not +1 in range as we do not want to include last point\n", - " rows = range(startRow, endRow)\n", - " cols = range(startRow+nz, endRow+nz) # Start at first index after last z-index\n", - " \n", + " rows = range(startRow, endRow)\n", + " cols = range(startRow + nz, endRow + nz) # Start at first index after last z-index\n", + "\n", " # Indices referring to the spatial points in the grid\n", - " inds = range(startInd+startXIndex*x, endInd+startXIndex*x)\n", - " \n", + " inds = range(startInd + startXIndex * x, endInd + startXIndex * x)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('xp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"xp_\" + str(ind))" ] }, { @@ -534,22 +562,22 @@ "source": [ "# x+1 for inner ghost point\n", "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex + startZIndex # First inner point for first z\n", - "endInd = startInd + (startZIndex*nz) # First inner point for last z\n", + "startInd = startXIndex + startZIndex # First inner point for first z\n", + "endInd = startInd + (startZIndex * nz) # First inner point for last z\n", "\n", "# The indices referring to the matrix index\n", "# The last -1 as the matrix indices counts from 0\n", - "startRow = startZIndex-1 # Starting at first row\n", - "endRow = startZIndex+nz-1 # Ending at the row referring to the last z-index\n", + "startRow = startZIndex - 1 # Starting at first row\n", + "endRow = startZIndex + nz - 1 # Ending at the row referring to the last z-index\n", "# Not +1 in range as we do not want to include last point\n", - "rows = range(startRow, endRow)\n", - "cols = range(startRow+nz, endRow+nz) # Start at first index after last z-index\n", - " \n", + "rows = range(startRow, endRow)\n", + "cols = range(startRow + nz, endRow + nz) # Start at first index after last z-index\n", + "\n", "# Indices referring to the spatial points in the grid\n", - "inds = range(startInd, endInd)\n", - " \n", + "inds = range(startInd, endInd)\n", + "\n", "for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('igxp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"igxp_\" + str(ind))" ] }, { @@ -569,25 +597,25 @@ "source": [ "# Indices referring to the spatial points in the grid\n", "startInd = startZIndex\n", - "endInd = startInd + nz\n", + "endInd = startInd + nz\n", "\n", "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # Note that x starts counting from zero, so we must add 1 to x in the rows\n", - " startRow = ((x+1)*nz) # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = ((x+1)*nz)+(nz) # Ending at the row referring to the last z-index\n", + " startRow = (x + 1) * nz # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = ((x + 1) * nz) + (nz) # Ending at the row referring to the last z-index\n", " # Not +1 in range as we do not want to include last point\n", - " rows = range(startRow, endRow)\n", - " cols = range(startRow-nz, endRow-nz) # Start at first index after last z-index\n", - " \n", + " rows = range(startRow, endRow)\n", + " cols = range(startRow - nz, endRow - nz) # Start at first index after last z-index\n", + "\n", " # Indices referring to the spatial points in the grid\n", - " inds = range(startInd+startXIndex*x, endInd+startXIndex*x)\n", - " \n", + " inds = range(startInd + startXIndex * x, endInd + startXIndex * x)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", " if (ind) < startXIndex:\n", - " ind = '0'+str(ind)\n", + " ind = \"0\" + str(ind)\n", "\n", - " InvM[rInd, cInd] = symbols('xm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"xm_\" + str(ind))" ] }, { @@ -600,22 +628,24 @@ "source": [ "# x-1 for inner ghost point\n", "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex*nx + startZIndex # Last inner point for first z\n", - "endInd = startInd + (startZIndex*nz) # Last inner point for last z\n", + "startInd = startXIndex * nx + startZIndex # Last inner point for first z\n", + "endInd = startInd + (startZIndex * nz) # Last inner point for last z\n", "\n", "# The indices referring to the matrix index\n", "# The last -1 as the matrix indices counts from 0\n", - "startRow = len(xVec)-nz-1 # Starting at last inner point row\n", - "endRow = len(xVec)-1 # Ending at the last row\n", + "startRow = len(xVec) - nz - 1 # Starting at last inner point row\n", + "endRow = len(xVec) - 1 # Ending at the last row\n", "# +1 in range as last point is not included\n", - "rows = range(startRow+1, endRow+1)\n", - "cols = range(startRow-nz+1, endRow-nz+1) # Start at first index after last z-index\n", - " \n", + "rows = range(startRow + 1, endRow + 1)\n", + "cols = range(\n", + " startRow - nz + 1, endRow - nz + 1\n", + ") # Start at first index after last z-index\n", + "\n", "# Indices referring to the spatial points in the grid\n", - "inds = range(startInd, endInd)\n", - " \n", + "inds = range(startInd, endInd)\n", + "\n", "for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('ogxm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"ogxm_\" + str(ind))" ] }, { diff --git a/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py b/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py index 8a95a13436..5a4a2a0843 100644 --- a/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py +++ b/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py @@ -10,10 +10,10 @@ mmag@fysik.dtu.dk """ -__authors__ = 'Michael Loeiten' -__email__ = 'mmag@fysik.dtu.dk' -__version__ = '1.0' -__date__ = '21.01.2016' +__authors__ = "Michael Loeiten" +__email__ = "mmag@fysik.dtu.dk" +__version__ = "1.0" +__date__ = "21.01.2016" import pygraphviz as pgv @@ -21,113 +21,118 @@ tree = pgv.AGraph() # Appendable lists -files = [] +files = [] dead_ends = [] # Default node attributes -tree.node_attr['shape']='box' -tree.node_attr['style']='bold' +tree.node_attr["shape"] = "box" +tree.node_attr["style"] = "bold" # Adding nodes and edges -l0 = 'project' -l1 = ['data', 'source\nfiles', 'driver.py'] +l0 = "project" +l1 = ["data", "source\nfiles", "driver.py"] # Append the files files.append(l1[1]) files.append(l1[2]) # Add the boxes to the mother node for box in l1: - tree.add_edge(l0,box) + tree.add_edge(l0, box) -l2 = ['solver1', 'solver2',\ - 'BOUT.inp', 'run_log.txt'] +l2 = ["solver1", "solver2", "BOUT.inp", "run_log.txt"] # Append the files files.append(l2[2]) files.append(l2[3]) # Add the boxes to the mother node for box in l2: - tree.add_edge('data', box) -tree.add_edge('solver2', 'solver2/...') + tree.add_edge("data", box) +tree.add_edge("solver2", "solver2/...") # Append the dead_end -de = l2[1] + '/...' +de = l2[1] + "/..." dead_ends.append(de) -l3 = ['method1', 'method2', 'solver1/...'] +l3 = ["method1", "method2", "solver1/..."] for box in l3: - tree.add_edge('solver1', box) -tree.add_edge('method2', 'method2/...') + tree.add_edge("solver1", box) +tree.add_edge("method2", "method2/...") # Append the dead_end de = l3[2] dead_ends.append(de) -de = l3[1] + '/...' +de = l3[1] + "/..." dead_ends.append(de) -l4 = ['nout\ntimestep1', 'nout\ntimestep2', 'method1/...'] +l4 = ["nout\ntimestep1", "nout\ntimestep2", "method1/..."] for box in l4: - tree.add_edge('method1', box) -tree.add_edge('nout\ntimestep2', 'nout\ntimestep2/...') + tree.add_edge("method1", box) +tree.add_edge("nout\ntimestep2", "nout\ntimestep2/...") # Append the dead_end de = l4[2] dead_ends.append(de) -de = l4[1] + '/...' +de = l4[1] + "/..." dead_ends.append(de) -l5 = ['mesh1', 'mesh2', 'nout\ntimestep1/...'] +l5 = ["mesh1", "mesh2", "nout\ntimestep1/..."] for box in l5: - tree.add_edge('nout\ntimestep1', box) -tree.add_edge('mesh2', 'mesh2/...') + tree.add_edge("nout\ntimestep1", box) +tree.add_edge("mesh2", "mesh2/...") # Append the dead_end de = l5[2] dead_ends.append(de) -de = l5[1] + '/...' +de = l5[1] + "/..." dead_ends.append(de) -l6 = ['additional1', 'additional2', 'mesh1/...'] +l6 = ["additional1", "additional2", "mesh1/..."] for box in l6: - tree.add_edge('mesh1', box) -tree.add_edge('additional2', 'additional2/...') + tree.add_edge("mesh1", box) +tree.add_edge("additional2", "additional2/...") # Append the dead_end de = l6[2] dead_ends.append(de) -de = l6[1] + '/...' +de = l6[1] + "/..." dead_ends.append(de) -l7 = ['grid_file1', 'grid_file2', 'additional1/...'] +l7 = ["grid_file1", "grid_file2", "additional1/..."] for box in l7: - tree.add_edge('additional1', box) -tree.add_edge('grid_file2', 'grid_file2/...') + tree.add_edge("additional1", box) +tree.add_edge("grid_file2", "grid_file2/...") # Append the dead_end de = l7[2] dead_ends.append(de) -de = l7[1] + '/...' +de = l7[1] + "/..." dead_ends.append(de) -l8 = ['BOUT.inp\n(copy)', 'BOUT.log', 'BOUT.dmp',\ - 'BOUT.restart', '(source_files\n(copy))', '(grid_file\n(copy))'] +l8 = [ + "BOUT.inp\n(copy)", + "BOUT.log", + "BOUT.dmp", + "BOUT.restart", + "(source_files\n(copy))", + "(grid_file\n(copy))", +] # Add l8 to the files list for cur_file in l8: files.append(cur_file) # Append them to the mother node for box in l8: - tree.add_edge('grid_file1', box) + tree.add_edge("grid_file1", box) # Change colors for the files for the_file in files: - member=tree.get_node(the_file) -# member.attr['fontcolor'] = 'limegreen' - member.attr['color'] = 'limegreen' + member = tree.get_node(the_file) + # member.attr['fontcolor'] = 'limegreen' + member.attr["color"] = "limegreen" # Change colors for the dead_ends for dead_end in dead_ends: - member=tree.get_node(dead_end) -# member.attr['fontcolor'] = 'darksalmon' - member.attr['color'] = 'darksalmon' + member = tree.get_node(dead_end) + # member.attr['fontcolor'] = 'darksalmon' + member.attr["color"] = "darksalmon" # Print the graph print(tree.string()) # Set layout -tree.layout('dot') +tree.layout("dot") # Write to file -tree.draw('folder_tree.svg') +tree.draw("folder_tree.svg") diff --git a/tests/MMS/advection/runtest b/tests/MMS/advection/runtest index 9a0a691287..5109c47815 100755 --- a/tests/MMS/advection/runtest +++ b/tests/MMS/advection/runtest @@ -50,7 +50,7 @@ def run_mms(options, exit=True): dx = 2.0 * pi / (nx) - args = f"{opts} mesh:nx={nx+4} mesh:dx={dx} MZ={nx}" + args = f"{opts} mesh:nx={nx + 4} mesh:dx={dx} MZ={nx}" print(" Running with " + args) From 96b86a4c785f326c79f7604f05832fce6a099a10 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Thu, 29 Jan 2026 10:45:03 +0000 Subject: [PATCH 176/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index ead5bc3729..5e90d36c14 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -2,3 +2,4 @@ d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 # Added by the bot +4b010b7634aee1045743be80c268d4644522cd29 From 6305598f8b3578205fe4327314b61e443f95c56b Mon Sep 17 00:00:00 2001 From: David Dickinson Date: Fri, 14 Dec 2018 10:09:28 +0000 Subject: [PATCH 177/461] Provide `GlobalZ` and related routines on mesh Tests are updated because `FakeMesh` has `GlobalZ(z) == z`. This is completely arbitrary, we could have a `GlobalZ` definition that matches `BoutMesh` (`z / nz`) then we wouldn't need to change the tests, but this way is a bit clearer what we're testing. --- include/bout/mesh.hxx | 2 + src/mesh/data/gridfromoptions.cxx | 3 +- src/mesh/impls/bout/boutmesh.cxx | 40 ++++++++++++++++--- src/mesh/impls/bout/boutmesh.hxx | 13 ++++-- src/sys/generator_context.cxx | 16 ++++---- .../test-interpolate-z/test_interpolate.cxx | 3 +- .../test-interpolate/test_interpolate.cxx | 3 +- tests/unit/fake_mesh.hxx | 10 +++-- tests/unit/field/test_field_factory.cxx | 6 +-- tests/unit/mesh/data/test_gridfromoptions.cxx | 27 ++++++------- 10 files changed, 76 insertions(+), 47 deletions(-) diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 2b24a0e24a..c22d6e969b 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -507,8 +507,10 @@ public: virtual BoutReal GlobalX(int jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(int jy) const = 0; ///< Continuous Y index (0 -> 1) + virtual BoutReal GlobalZ(int jz) const = 0; ///< Continuous Z index (0 -> 1) virtual BoutReal GlobalX(BoutReal jx) const = 0; ///< Continuous X index between 0 and 1 virtual BoutReal GlobalY(BoutReal jy) const = 0; ///< Continuous Y index (0 -> 1) + virtual BoutReal GlobalZ(BoutReal jz) const = 0; ///< Continuous Z index (0 -> 1) ////////////////////////////////////////////////////////// diff --git a/src/mesh/data/gridfromoptions.cxx b/src/mesh/data/gridfromoptions.cxx index 379e279de0..662329c526 100644 --- a/src/mesh/data/gridfromoptions.cxx +++ b/src/mesh/data/gridfromoptions.cxx @@ -146,8 +146,7 @@ bool GridFromOptions::get(Mesh* m, std::vector& var, const std::string } case GridDataSource::Z: { for (int z = 0; z < len; z++) { - pos.set("z", - (TWOPI * (z - m->OffsetZ + offset)) / static_cast(m->LocalNz)); + pos.set("z", TWOPI * m->GlobalZ(z - m->OffsetZ + offset)); var[z] = gen->generate(pos); } break; diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 4be01d4637..507e3b9495 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -57,6 +57,8 @@ If you want the old setting, you have to specify mesh:symmetricGlobalY=false in << optionfile << "\n"; } OPTION(options, symmetricGlobalY, true); + OPTION(options, symmetricGlobalZ, false); // The default should be updated to true but + // this breaks backwards compatibility comm_x = MPI_COMM_NULL; comm_inner = MPI_COMM_NULL; @@ -516,9 +518,10 @@ int BoutMesh::load() { findProcessorSplit(); } - // Get X and Y processor indices + // Get X, Y, Z processor indices PE_YIND = MYPE / NXPE; PE_XIND = MYPE % NXPE; + PE_ZIND = 0; // Set the other grid sizes from nx, ny, nz setDerivedGridSizes(); @@ -1741,16 +1744,22 @@ int BoutMesh::getLocalYIndexNoBoundaries(int yglobal) const { return yglobal - PE_YIND * MYSUB + MYG; } -int BoutMesh::YGLOBAL(int yloc, int yproc) const { return yloc + yproc * MYSUB - MYG; } +int BoutMesh::YGLOBAL(int yloc, int yproc) const { return yloc + (yproc * MYSUB) - MYG; } -int BoutMesh::YLOCAL(int yglo, int yproc) const { return yglo - yproc * MYSUB + MYG; } +int BoutMesh::YLOCAL(int yglo, int yproc) const { return yglo - (yproc * MYSUB) + MYG; } -int BoutMesh::getGlobalZIndex(int zlocal) const { return zlocal; } +int BoutMesh::getGlobalZIndex(int zlocal) const { return zlocal + (PE_ZIND * MZSUB); } -int BoutMesh::getGlobalZIndexNoBoundaries(int zlocal) const { return zlocal; } +int BoutMesh::getGlobalZIndexNoBoundaries(int zlocal) const { return zlocal + (PE_ZIND * MZSUB) - MZG; } int BoutMesh::getLocalZIndex(int zglobal) const { return zglobal; } +/// Returns the global Z index given a local index +int BoutMesh::ZGLOBAL(BoutReal zloc, BoutReal& zglo) const { + zglo = zloc + (PE_ZIND * MZSUB); + return static_cast(zglo); +} + int BoutMesh::getLocalZIndexNoBoundaries(int zglobal) const { return zglobal; } int BoutMesh::YPROC(int yind) const { @@ -3222,6 +3231,27 @@ BoutReal BoutMesh::GlobalY(BoutReal jy) const { return yglo / static_cast(nycore); } +BoutReal BoutMesh::GlobalZ(int jz) const { + if (symmetricGlobalZ) { + // With this definition the boundary sits dz/2 away form the first/last inner points + return (0.5 + getGlobalZIndex(jz) - (nz - MZ) * 0.5) / static_cast(MZ); + } + return static_cast(getGlobalZIndex(jz)) / static_cast(MZ); +} + +BoutReal BoutMesh::GlobalZ(BoutReal jz) const { + + // Get global Z index as a BoutReal + BoutReal zglo; + ZGLOBAL(jz, zglo); + + if (symmetricGlobalZ) { + // With this definition the boundary sits dx/2 away form the first/last inner points + return (0.5 + zglo - (nz - MZ) * 0.5) / static_cast(MZ); + } + return zglo / static_cast(MZ); +} + void BoutMesh::outputVars(Options& output_options) { Timer time("io"); output_options["zperiod"].force(zperiod, "BoutMesh"); diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index 876edab1da..f39856867c 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -172,8 +172,10 @@ public: BoutReal GlobalX(int jx) const override; BoutReal GlobalY(int jy) const override; + BoutReal GlobalZ(int jz) const override; BoutReal GlobalX(BoutReal jx) const override; BoutReal GlobalY(BoutReal jy) const override; + BoutReal GlobalZ(BoutReal jz) const override; BoutReal getIxseps1() const { return ixseps1; } BoutReal getIxseps2() const { return ixseps2; } @@ -296,15 +298,17 @@ private: int MYPE; ///< Rank of this processor int PE_YIND; ///< Y index of this processor - int NYPE; // Number of processors in the Y direction + int NYPE; ///< Number of processors in the Y direction - int NZPE; + int PE_ZIND{0}; ///< Z index of this processor + int NZPE{1}; ///< Number of processors in the Z direction /// Is this processor in the core region? bool MYPE_IN_CORE{false}; int XGLOBAL(BoutReal xloc, BoutReal& xglo) const; int YGLOBAL(BoutReal yloc, BoutReal& yglo) const; + int ZGLOBAL(BoutReal zloc, BoutReal& zglo) const; // Topology int ixseps1, ixseps2, jyseps1_1, jyseps2_1, jyseps1_2, jyseps2_2; @@ -355,8 +359,9 @@ private: // Settings bool TwistShift; // Use a twist-shift condition in core? - bool symmetricGlobalX; ///< Use a symmetric definition in GlobalX() function - bool symmetricGlobalY; + bool symmetricGlobalX; ///< Use a symmetric definition in `GlobalX()` function + bool symmetricGlobalY; ///< Use a symmetric definition in `GlobalY()` function + bool symmetricGlobalZ{false}; ///< Use a symmetric definition in `GlobalZ()` function int zperiod; BoutReal ZMIN, ZMAX; // Range of the Z domain (in fractions of 2pi) diff --git a/src/sys/generator_context.cxx b/src/sys/generator_context.cxx index 25274e8107..03b52ad24b 100644 --- a/src/sys/generator_context.cxx +++ b/src/sys/generator_context.cxx @@ -15,9 +15,8 @@ Context::Context(int ix, int iy, int iz, CELL_LOC loc, Mesh* msh, BoutReal t) parameters["y"] = (loc == CELL_YLOW) ? PI * (msh->GlobalY(iy) + msh->GlobalY(iy - 1)) : TWOPI * msh->GlobalY(iy); - parameters["z"] = (loc == CELL_ZLOW) - ? TWOPI * (iz - 0.5) / static_cast(msh->LocalNz) - : TWOPI * iz / static_cast(msh->LocalNz); + parameters["z"] = (loc == CELL_ZLOW) ? PI * (msh->GlobalZ(iz) + msh->GlobalZ(iz - 1)) + : TWOPI * msh->GlobalZ(iz); parameters["t"] = t; } @@ -26,21 +25,20 @@ Context::Context(const BoundaryRegion* bndry, int iz, CELL_LOC loc, BoutReal t, : localmesh(msh) { // Add one to X index if boundary is in -x direction, so that XLOW is on the boundary - int ix = (bndry->bx < 0) ? bndry->x + 1 : bndry->x; + const int ix = (bndry->bx < 0) ? bndry->x + 1 : bndry->x; parameters["x"] = ((loc == CELL_XLOW) || (bndry->bx != 0)) ? 0.5 * (msh->GlobalX(ix) + msh->GlobalX(ix - 1)) : msh->GlobalX(ix); - int iy = (bndry->by < 0) ? bndry->y + 1 : bndry->y; + const int iy = (bndry->by < 0) ? bndry->y + 1 : bndry->y; - parameters["y"] = ((loc == CELL_YLOW) || bndry->by) + parameters["y"] = ((loc == CELL_YLOW) || (bndry->by != 0)) ? PI * (msh->GlobalY(iy) + msh->GlobalY(iy - 1)) : TWOPI * msh->GlobalY(iy); - parameters["z"] = (loc == CELL_ZLOW) - ? TWOPI * (iz - 0.5) / static_cast(msh->LocalNz) - : TWOPI * iz / static_cast(msh->LocalNz); + parameters["z"] = (loc == CELL_ZLOW) ? PI * (msh->GlobalZ(iz) + msh->GlobalZ(iz - 1)) + : TWOPI * msh->GlobalZ(iz); parameters["t"] = t; } diff --git a/tests/integrated/test-interpolate-z/test_interpolate.cxx b/tests/integrated/test-interpolate-z/test_interpolate.cxx index 3ef6e50425..8ee96d2897 100644 --- a/tests/integrated/test-interpolate-z/test_interpolate.cxx +++ b/tests/integrated/test-interpolate-z/test_interpolate.cxx @@ -71,8 +71,7 @@ int main(int argc, char** argv) { deltaz[index] = dz; // Get the global indices bout::generator::Context pos{index, CELL_CENTRE, deltaz.getMesh(), 0.0}; - pos.set("x", mesh->GlobalX(index.x()), "z", - TWOPI * static_cast(dz) / static_cast(mesh->LocalNz)); + pos.set("x", mesh->GlobalX(index.x()), "z", TWOPI * mesh->GlobalZ(dz)); // Generate the analytic solution at the displacements a_solution[index] = a_gen->generate(pos); b_solution[index] = b_gen->generate(pos); diff --git a/tests/integrated/test-interpolate/test_interpolate.cxx b/tests/integrated/test-interpolate/test_interpolate.cxx index 33963dbb9e..98ecd68bc5 100644 --- a/tests/integrated/test-interpolate/test_interpolate.cxx +++ b/tests/integrated/test-interpolate/test_interpolate.cxx @@ -79,8 +79,7 @@ int main(int argc, char** argv) { deltaz[index] = dz; // Get the global indices bout::generator::Context pos{index, CELL_CENTRE, deltax.getMesh(), 0.0}; - pos.set("x", mesh->GlobalX(dx), "z", - TWOPI * static_cast(dz) / static_cast(mesh->LocalNz)); + pos.set("x", mesh->GlobalX(dx), "z", TWOPI * mesh->GlobalZ(dz)); // Generate the analytic solution at the displacements a_solution[index] = a_gen->generate(pos); b_solution[index] = b_gen->generate(pos); diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 2feb43826d..b93cddf9ca 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -170,20 +170,22 @@ public: } BoutReal GlobalX(int jx) const override { return jx; } BoutReal GlobalY(int jy) const override { return jy; } + BoutReal GlobalZ(int jz) const override { return jz; } BoutReal GlobalX(BoutReal jx) const override { return jx; } BoutReal GlobalY(BoutReal jy) const override { return jy; } + BoutReal GlobalZ(BoutReal jz) const override { return jz; } int getGlobalXIndex(int) const override { return 0; } int getGlobalXIndexNoBoundaries(int) const override { return 0; } int getGlobalYIndex(int y) const override { return y; } int getGlobalYIndexNoBoundaries(int y) const override { return y; } - int getGlobalZIndex(int) const override { return 0; } - int getGlobalZIndexNoBoundaries(int) const override { return 0; } + int getGlobalZIndex(int z) const override { return z; } + int getGlobalZIndexNoBoundaries(int z) const override { return z; } int getLocalXIndex(int) const override { return 0; } int getLocalXIndexNoBoundaries(int) const override { return 0; } int getLocalYIndex(int y) const override { return y; } int getLocalYIndexNoBoundaries(int y) const override { return y; } - int getLocalZIndex(int) const override { return 0; } - int getLocalZIndexNoBoundaries(int) const override { return 0; } + int getLocalZIndex(int z) const override { return z; } + int getLocalZIndexNoBoundaries(int z) const override { return z; } void initDerivs(Options* opt) { StaggerGrids = true; diff --git a/tests/unit/field/test_field_factory.cxx b/tests/unit/field/test_field_factory.cxx index 26dc1e2990..b45206b979 100644 --- a/tests/unit/field/test_field_factory.cxx +++ b/tests/unit/field/test_field_factory.cxx @@ -161,9 +161,7 @@ TYPED_TEST(FieldFactoryCreationTest, CreateZ) { auto output = this->create("z"); auto expected = makeField( - [](typename TypeParam::ind_type& index) -> BoutReal { - return TWOPI * index.z() / FieldFactoryCreationTest::nz; - }, + [](typename TypeParam::ind_type& index) -> BoutReal { return TWOPI * index.z(); }, mesh); EXPECT_TRUE(IsFieldEqual(output, expected)); @@ -209,7 +207,7 @@ TYPED_TEST(FieldFactoryCreationTest, CreateZStaggered) { offset = 0.5; } - return TWOPI * (index.z() - offset) / FieldFactoryCreationTest::nz; + return TWOPI * (index.z() - offset); }, mesh); diff --git a/tests/unit/mesh/data/test_gridfromoptions.cxx b/tests/unit/mesh/data/test_gridfromoptions.cxx index b821d2e225..41dd40fcc3 100644 --- a/tests/unit/mesh/data/test_gridfromoptions.cxx +++ b/tests/unit/mesh/data/test_gridfromoptions.cxx @@ -57,13 +57,13 @@ class GridFromOptionsTest : public ::testing::Test { expected_2d = makeField( [](Field2D::ind_type& index) { - return index.x() + (TWOPI * index.y()) + (TWOPI * index.z() / nz) + 3; + return index.x() + (TWOPI * index.y()) + (TWOPI * index.z()) + 3; }, &mesh_from_options); expected_3d = makeField( [](Field3D::ind_type& index) { - return index.x() + (TWOPI * index.y()) + (TWOPI * index.z() / nz) + 3; + return index.x() + (TWOPI * index.y()) + (TWOPI * index.z()) + 3; }, &mesh_from_options); expected_metric = @@ -316,8 +316,8 @@ TEST_F(GridFromOptionsTest, GetVectorBoutRealYNone) { TEST_F(GridFromOptionsTest, GetVectorBoutRealZ) { std::vector result{}; - std::vector expected{3., 3. + (1. * TWOPI / nz), 3. + (2. * TWOPI / nz), - 3. + (3. * TWOPI / nz), 3. + (4. * TWOPI / nz)}; + std::vector expected{3., 3. + (1. * TWOPI), 3. + (2. * TWOPI), + 3. + (3. * TWOPI), 3. + (4. * TWOPI)}; EXPECT_TRUE(griddata->get(&mesh_from_options, result, "f", nz, 0, GridDataSource::Direction::Z)); @@ -326,9 +326,8 @@ TEST_F(GridFromOptionsTest, GetVectorBoutRealZ) { TEST_F(GridFromOptionsTest, GetVectorBoutRealZOffset) { std::vector result{}; - std::vector expected{3. + (1. * TWOPI / nz), 3. + (2. * TWOPI / nz), - 3. + (3. * TWOPI / nz), 3. + (4. * TWOPI / nz), - 3. + (5. * TWOPI / nz)}; + std::vector expected{3. + (1. * TWOPI), 3. + (2. * TWOPI), 3. + (3. * TWOPI), + 3. + (4. * TWOPI), 3. + (5. * TWOPI)}; EXPECT_TRUE(griddata->get(&mesh_from_options, result, "f", nz, 1, GridDataSource::Direction::Z)); @@ -337,8 +336,8 @@ TEST_F(GridFromOptionsTest, GetVectorBoutRealZOffset) { TEST_F(GridFromOptionsTest, GetVectorBoutRealZMeshOffset) { std::vector result{}; - std::vector expected{3. + (-1. * TWOPI / nz), 3., 3. + (1. * TWOPI / nz), - 3. + (2. * TWOPI / nz), 3. + (3. * TWOPI / nz)}; + std::vector expected{3. + (-1. * TWOPI), 3., 3. + (1. * TWOPI), + 3. + (2. * TWOPI), 3. + (3. * TWOPI)}; mesh_from_options.OffsetX = 100; mesh_from_options.OffsetY = 100; @@ -397,7 +396,7 @@ TEST_F(GridFromOptionsTest, CoordinatesXlowInterp) { Coordinates::FieldMetric expected_xlow = makeField( [](Coordinates::FieldMetric::ind_type& index) { - return index.x() - 0.5 + (TWOPI * index.y()) + (TWOPI * index.z() / nz) + 3; + return index.x() - 0.5 + (TWOPI * index.y()) + (TWOPI * index.z()) + 3; }, &mesh_from_options); @@ -438,8 +437,7 @@ TEST_F(GridFromOptionsTest, CoordinatesXlowRead) { Field2D expected_xlow = makeField( [](Field2D::ind_type& index) { - return (nx - index.x() + 0.5) + (TWOPI * index.y()) + (TWOPI * index.z() / nz) - + 3; + return (nx - index.x() + 0.5) + (TWOPI * index.y()) + (TWOPI * index.z()) + 3; }, &mesh_from_options); @@ -471,7 +469,7 @@ TEST_F(GridFromOptionsTest, CoordinatesYlowInterp) { Field2D expected_ylow = makeField( [](Field2D::ind_type& index) { - return index.x() + (TWOPI * (index.y() - 0.5)) + (TWOPI * index.z() / nz) + 3; + return index.x() + (TWOPI * (index.y() - 0.5)) + (TWOPI * index.z()) + 3; }, &mesh_from_options); @@ -521,8 +519,7 @@ TEST_F(GridFromOptionsTest, CoordinatesYlowRead) { Field2D expected_ylow = makeField( [](Field2D::ind_type& index) { - return index.x() + (TWOPI * (ny - index.y() + 0.5)) + (TWOPI * index.z() / nz) - + 3; + return index.x() + (TWOPI * (ny - index.y() + 0.5)) + (TWOPI * index.z()) + 3; }, &mesh_from_options); From 6c6872505dd013e7bee3d5be48e2dba90b05572a Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 8 Jan 2026 17:13:58 +0000 Subject: [PATCH 178/461] Move `NXPE`/`PE_XIND` out of `Mesh` --- include/bout/mesh.hxx | 2 -- .../iterative_parallel_tri.cxx | 2 +- src/invert/laplace/impls/spt/spt.cxx | 12 ++++++------ src/mesh/impls/bout/boutmesh.cxx | 11 +++++------ src/mesh/impls/bout/boutmesh.hxx | 6 ++++-- tests/unit/fake_mesh.hxx | 2 -- 6 files changed, 16 insertions(+), 19 deletions(-) diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index c22d6e969b..83066b860f 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -368,8 +368,6 @@ public: /// Domain is periodic in X? bool periodicX{false}; - int NXPE, PE_XIND; ///< Number of processors in X, and X processor index - /// Send a buffer of data to processor at X index +1 /// /// @param[in] buffer The data to send. Must be at least length \p size diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx index 926facd9e6..7535ffb3c7 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx @@ -72,7 +72,7 @@ LaplaceIPT::LaplaceIPT(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED D.setLocation(location); // Number of procs must be a factor of 2 - const int n = localmesh->NXPE; + const int n = localmesh->getNXPE(); if (!is_pow2(n)) { throw BoutException("LaplaceIPT error: NXPE must be a power of 2"); } diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index e39ca7e89f..e4eab810af 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -341,14 +341,14 @@ int LaplaceSPT::start(const FieldPerp& b, SPT_data& data) { // Send data localmesh->sendXOut(std::begin(data.buffer), 4 * (maxmode + 1), data.comm_tag); - } else if (localmesh->PE_XIND == 1) { + } else if (localmesh->getXProcIndex() == 1) { // Post a receive data.recv_handle = localmesh->irecvXIn(std::begin(data.buffer), 4 * (maxmode + 1), data.comm_tag); } data.proc++; // Now moved onto the next processor - if (localmesh->NXPE == 2) { + if (localmesh->getNXPE() == 2) { data.dir = -1; // Special case. Otherwise reversal handled in spt_continue } @@ -366,7 +366,7 @@ int LaplaceSPT::next(SPT_data& data) { return 1; } - if (localmesh->PE_XIND == data.proc) { + if (localmesh->getXProcIndex() == data.proc) { /// This processor's turn to do inversion // Wait for data to arrive @@ -450,7 +450,7 @@ int LaplaceSPT::next(SPT_data& data) { } } - if (localmesh->PE_XIND != 0) { // If not finished yet + if (localmesh->getXProcIndex() != 0) { // If not finished yet /// Send data if (data.dir > 0) { @@ -460,7 +460,7 @@ int LaplaceSPT::next(SPT_data& data) { } } - } else if (localmesh->PE_XIND == data.proc + data.dir) { + } else if (localmesh->getXProcIndex() == data.proc + data.dir) { // This processor is next, post receive if (data.dir > 0) { @@ -474,7 +474,7 @@ int LaplaceSPT::next(SPT_data& data) { data.proc += data.dir; - if (data.proc == localmesh->NXPE - 1) { + if (data.proc == localmesh->getNXPE() - 1) { data.dir = -1; // Reverses direction at the end } diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 507e3b9495..74c06dfce4 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -1827,16 +1827,15 @@ BoutMesh::BoutMesh(int input_nx, int input_ny, int input_nz, int mxg, int myg, i BoutMesh::BoutMesh(int input_nx, int input_ny, int input_nz, int mxg, int myg, int nxpe, int nype, int pe_xind, int pe_yind, bool symmetric_X, bool symmetric_Y, - bool periodicX_, int ixseps1_, int ixseps2_, int jyseps1_1_, + bool periodic_X_, int ixseps1_, int ixseps2_, int jyseps1_1_, int jyseps2_1_, int jyseps1_2_, int jyseps2_2_, int ny_inner_, bool create_regions) : nx(input_nx), ny(input_ny), nz(input_nz), NPES(nxpe * nype), - MYPE(nxpe * pe_yind + pe_xind), PE_YIND(pe_yind), NYPE(nype), NZPE(1), - ixseps1(ixseps1_), ixseps2(ixseps2_), symmetricGlobalX(symmetric_X), + MYPE((nxpe * pe_yind) + pe_xind), PE_XIND(pe_xind), NXPE(nxpe), PE_YIND(pe_yind), + NYPE(nype), ixseps1(ixseps1_), ixseps2(ixseps2_), symmetricGlobalX(symmetric_X), symmetricGlobalY(symmetric_Y), MXG(mxg), MYG(myg), MZG(0) { - NXPE = nxpe; - PE_XIND = pe_xind; - periodicX = periodicX_; + + periodicX = periodic_X_; setYDecompositionIndices(jyseps1_1_, jyseps2_1_, jyseps1_2_, jyseps2_2_, ny_inner_); setDerivedGridSizes(); topology(); diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index f39856867c..09d0015890 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -7,7 +7,6 @@ #include "bout/unused.hxx" #include -#include #include #include #include @@ -208,7 +207,7 @@ protected: /// `getPossibleBoundaries`. \p create_regions controls whether or /// not the various `Region`s are created on the new mesh BoutMesh(int input_nx, int input_ny, int input_nz, int mxg, int myg, int nxpe, int nype, - int pe_xind, int pe_yind, bool symmetric_X, bool symmetric_Y, bool periodic_X, + int pe_xind, int pe_yind, bool symmetric_X, bool symmetric_Y, bool periodic_X_, int ixseps1_, int ixseps2_, int jyseps1_1_, int jyseps2_1_, int jyseps1_2_, int jyseps2_2_, int ny_inner_, bool create_regions = true); @@ -297,6 +296,9 @@ private: int NPES; ///< Number of processors int MYPE; ///< Rank of this processor + int PE_XIND; ///< X index of this processor + int NXPE; ///< Number of processors in the X direction + int PE_YIND; ///< Y index of this processor int NYPE; ///< Number of processors in the Y direction diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index b93cddf9ca..255e13ae36 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -73,8 +73,6 @@ public: // Unused variables periodicX = false; - NXPE = 1; - PE_XIND = 0; IncIntShear = false; maxregionblocksize = MAXREGIONBLOCKSIZE; From eaf4c57665da8d66d5e14bf0b53ec7a644a36f24 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 8 Jan 2026 17:27:56 +0000 Subject: [PATCH 179/461] Add `Mesh::getNZPE()/getZProcIndex` Also make related methods all `const` --- include/bout/mesh.hxx | 10 ++++++---- src/mesh/impls/bout/boutmesh.cxx | 12 ++++++++---- src/mesh/impls/bout/boutmesh.hxx | 10 ++++++---- tests/unit/fake_mesh.hxx | 10 ++++++---- 4 files changed, 26 insertions(+), 16 deletions(-) diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 83066b860f..502b0c5668 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -354,10 +354,12 @@ public: // non-local communications - virtual int getNXPE() = 0; ///< The number of processors in the X direction - virtual int getNYPE() = 0; ///< The number of processors in the Y direction - virtual int getXProcIndex() = 0; ///< This processor's index in X direction - virtual int getYProcIndex() = 0; ///< This processor's index in Y direction + virtual int getNXPE() const = 0; ///< The number of processors in the X direction + virtual int getNYPE() const = 0; ///< The number of processors in the Y direction + virtual int getNZPE() const = 0; ///< The number of processors in the Z direction + virtual int getXProcIndex() const = 0; ///< This processor's index in X direction + virtual int getYProcIndex() const = 0; ///< This processor's index in Y direction + virtual int getZProcIndex() const = 0; ///< This processor's index in Z direction // X communications virtual bool firstX() diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 74c06dfce4..6ce6f8783e 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -1522,13 +1522,17 @@ int BoutMesh::wait(comm_handle handle) { * Non-Local Communications ***************************************************************/ -int BoutMesh::getNXPE() { return NXPE; } +int BoutMesh::getNXPE() const { return NXPE; } -int BoutMesh::getNYPE() { return NYPE; } +int BoutMesh::getNYPE() const { return NYPE; } -int BoutMesh::getXProcIndex() { return PE_XIND; } +int BoutMesh::getNZPE() const { return NZPE; } -int BoutMesh::getYProcIndex() { return PE_YIND; } +int BoutMesh::getXProcIndex() const { return PE_XIND; } + +int BoutMesh::getYProcIndex() const { return PE_YIND; } + +int BoutMesh::getZProcIndex() const { return PE_ZIND; } /**************************************************************** * X COMMUNICATIONS diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index 09d0015890..fea3583c4b 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -57,10 +57,12 @@ public: ///////////////////////////////////////////// // non-local communications - int getNXPE() override; ///< The number of processors in the X direction - int getNYPE() override; ///< The number of processors in the Y direction - int getXProcIndex() override; ///< This processor's index in X direction - int getYProcIndex() override; ///< This processor's index in Y direction + int getNXPE() const override; ///< The number of processors in the X direction + int getNYPE() const override; ///< The number of processors in the Y direction + int getNZPE() const override; ///< The number of processors in the Z direction + int getXProcIndex() const override; ///< This processor's index in X direction + int getYProcIndex() const override; ///< This processor's index in Y direction + int getZProcIndex() const override; ///< This processor's index in Z direction ///////////////////////////////////////////// // X communications diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 255e13ae36..5e37406f5d 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -107,10 +107,12 @@ public: return nullptr; } int wait(comm_handle UNUSED(handle)) override { return 0; } - int getNXPE() override { return 1; } - int getNYPE() override { return 1; } - int getXProcIndex() override { return 1; } - int getYProcIndex() override { return 1; } + int getNXPE() const override { return 1; } + int getNYPE() const override { return 1; } + int getNZPE() const override { return 1; } + int getXProcIndex() const override { return 1; } + int getYProcIndex() const override { return 1; } + int getZProcIndex() const override { return 1; } bool firstX() const override { return true; } bool lastX() const override { return true; } int sendXOut(BoutReal* UNUSED(buffer), int UNUSED(size), int UNUSED(tag)) override { From ca3507674ac5130c15a669a5740b0fb1bf567acf Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 09:50:22 +0000 Subject: [PATCH 180/461] Add guards for FFT routines against parallel-in-z runs --- include/bout/fft.hxx | 16 +++++++++++++++- src/field/field3d.cxx | 4 +++- src/invert/fft_fftw.cxx | 18 +++++++++++++++++- .../laplace/impls/cyclic/cyclic_laplace.cxx | 2 ++ .../iterative_parallel_tri.cxx | 2 ++ src/invert/laplace/impls/pcr/pcr.cxx | 2 ++ .../laplace/impls/pcr_thomas/pcr_thomas.cxx | 2 ++ .../laplace/impls/serial_band/serial_band.cxx | 3 +++ .../laplace/impls/serial_tri/serial_tri.cxx | 7 ++++--- src/invert/laplace/impls/spt/spt.cxx | 3 +++ .../impls/cyclic/laplacexz-cyclic.cxx | 1 + src/invert/parderiv/impls/cyclic/cyclic.cxx | 2 ++ .../pardiv/impls/cyclic/pardiv_cyclic.cxx | 1 + src/mesh/boundary_standard.cxx | 4 ++++ src/mesh/coordinates.cxx | 4 ++-- src/mesh/index_derivs.cxx | 2 ++ src/mesh/parallel/shiftedmetric.cxx | 1 + 17 files changed, 66 insertions(+), 8 deletions(-) diff --git a/include/bout/fft.hxx b/include/bout/fft.hxx index fdec8b7bec..649b3bb5db 100644 --- a/include/bout/fft.hxx +++ b/include/bout/fft.hxx @@ -28,9 +28,13 @@ #ifndef BOUT_FFT_H #define BOUT_FFT_H -#include "bout/dcomplex.hxx" +#include "bout/build_defines.hxx" + #include #include +#include + +#include class Options; @@ -111,6 +115,16 @@ Array rfft(const Array& in); /// Expects that `in.size() == (length / 2) + 1` Array irfft(const Array& in, int length); +/// Check simulation is using 1 processor in Z, throw exception if not +/// +/// Generally, FFTs must be done over the full Z domain. Currently, most +/// methods using FFTs don't handle parallelising in Z +#if BOUT_CHECK_LEVEL > 0 +void checkZSerial(const Mesh& mesh, std::string_view name); +#else +inline void checkZSerial([[maybe_unused]] const Mesh& mesh, + [[maybe_unused]] std::string_view name) {} +#endif } // namespace fft } // namespace bout diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 9821c638f7..c0209947b6 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -604,6 +604,7 @@ FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, const std::string& rgn) Field3D filter(const Field3D& var, int N0, const std::string& rgn) { + bout::fft::checkZSerial(*var.getMesh(), "`filter`"); checkData(var); int ncz = var.getNz(); @@ -649,6 +650,7 @@ Field3D filter(const Field3D& var, int N0, const std::string& rgn) { // Fourier filter in z with zmin Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string& rgn) { + bout::fft::checkZSerial(*var.getMesh(), "`lowPass`"); checkData(var); int ncz = var.getNz(); @@ -697,7 +699,7 @@ Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string * Use FFT to shift by an angle in the Z direction */ void shiftZ(Field3D& var, int jx, int jy, double zangle) { - + bout::fft::checkZSerial(*var.getMesh(), "`shiftZ`"); checkData(var); var.allocate(); // Ensure that var is unique Mesh* localmesh = var.getMesh(); diff --git a/src/invert/fft_fftw.cxx b/src/invert/fft_fftw.cxx index 05977e5f3f..94dd528b27 100644 --- a/src/invert/fft_fftw.cxx +++ b/src/invert/fft_fftw.cxx @@ -27,8 +27,10 @@ #include "bout/build_defines.hxx" +#include #include #include +#include #include #include @@ -36,7 +38,6 @@ #include #include -#include #include #if BOUT_USE_OPENMP @@ -46,6 +47,12 @@ #include #endif // BOUT_HAS_FFTW +#if BOUT_CHECK_LEVEL > 0 +#include + +#include +#endif + namespace bout { namespace fft { @@ -527,5 +534,14 @@ Array irfft(const Array& in, int length) { return out; } +#if BOUT_CHECK_LEVEL > 0 +void checkZSerial(const Mesh& mesh, std::string_view name) { + if (mesh.getNZPE() != 1) { + throw BoutException("{} uses FFTs which are currently incompatible with multiple " + "processors in Z (using {})", + name, mesh.getNZPE()); + } +} +#endif } // namespace fft } // namespace bout diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index 99aacea4f8..adb30def83 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -57,6 +57,8 @@ LaplaceCyclic::LaplaceCyclic(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), C1coef(1.0), C2coef(1.0), Dcoef(1.0) { + bout::fft::checkZSerial(*localmesh, "`cyclic` inversion"); + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx index 7535ffb3c7..8743d59db2 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx @@ -67,6 +67,8 @@ LaplaceIPT::LaplaceIPT(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED au(ny, nmode), bu(ny, nmode), rl(nmode), ru(nmode), r1(ny, nmode), r2(ny, nmode), first_call(ny), x0saved(ny, 4, nmode), converged(nmode), fine_error(4, nmode) { + bout::fft::checkZSerial(*localmesh, "`ipt` inversion"); + A.setLocation(location); C.setLocation(location); D.setLocation(location); diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index 60169c6eb4..29f007118c 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -67,6 +67,8 @@ LaplacePCR::LaplacePCR(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED ncx(localmesh->LocalNx), ny(localmesh->LocalNy), avec(ny, nmode, ncx), bvec(ny, nmode, ncx), cvec(ny, nmode, ncx) { + bout::fft::checkZSerial(*localmesh, "`pcr` inversion"); + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index 600166aa6b..73b7d6ec5c 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -65,6 +65,8 @@ LaplacePCR_THOMAS::LaplacePCR_THOMAS(Options* opt, CELL_LOC loc, Mesh* mesh_in, ncx(localmesh->LocalNx), ny(localmesh->LocalNy), avec(ny, nmode, ncx), bvec(ny, nmode, ncx), cvec(ny, nmode, ncx) { + bout::fft::checkZSerial(*localmesh, "`pcr_thomas` inversion"); + Acoef.setLocation(location); C1coef.setLocation(location); C2coef.setLocation(location); diff --git a/src/invert/laplace/impls/serial_band/serial_band.cxx b/src/invert/laplace/impls/serial_band/serial_band.cxx index d7b4ac5d3b..921f2af24d 100644 --- a/src/invert/laplace/impls/serial_band/serial_band.cxx +++ b/src/invert/laplace/impls/serial_band/serial_band.cxx @@ -45,6 +45,9 @@ LaplaceSerialBand::LaplaceSerialBand(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), Ccoef(1.0), Dcoef(1.0) { + + bout::fft::checkZSerial(*localmesh, "`band` inversion"); + Acoef.setLocation(location); Ccoef.setLocation(location); Dcoef.setLocation(location); diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.cxx b/src/invert/laplace/impls/serial_tri/serial_tri.cxx index 0fb9294d76..1a64984a3e 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.cxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.cxx @@ -33,14 +33,15 @@ #include #include #include -#include -#include - #include +#include LaplaceSerialTri::LaplaceSerialTri(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), A(0.0), C(1.0), D(1.0) { + + bout::fft::checkZSerial(*localmesh, "`tri` inversion"); + A.setLocation(location); C.setLocation(location); D.setLocation(location); diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index e4eab810af..1a5cb5f23c 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -45,6 +45,9 @@ LaplaceSPT::LaplaceSPT(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), Ccoef(1.0), Dcoef(1.0) { + + bout::fft::checkZSerial(*localmesh, "`spt` inversion"); + Acoef.setLocation(location); Ccoef.setLocation(location); Dcoef.setLocation(location); diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx index dc987edc75..56780eaf5c 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx @@ -13,6 +13,7 @@ LaplaceXZcyclic::LaplaceXZcyclic(Mesh* m, Options* options, const CELL_LOC loc) : LaplaceXZ(m, options, loc) { // Note: `m` may be nullptr, but localmesh is set in LaplaceXZ base constructor + bout::fft::checkZSerial(*localmesh, "`cyclic` X-Z inversion"); // Number of Z Fourier modes, including DC nmode = (localmesh->LocalNz) / 2 + 1; diff --git a/src/invert/parderiv/impls/cyclic/cyclic.cxx b/src/invert/parderiv/impls/cyclic/cyclic.cxx index 61d23be823..d20537a0f3 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.cxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.cxx @@ -53,6 +53,8 @@ InvertParCR::InvertParCR(Options* opt, CELL_LOC location, Mesh* mesh_in) : InvertPar(opt, location, mesh_in), A(1.0), B(0.0), C(0.0), D(0.0), E(0.0) { + + bout::fft::checkZSerial(*localmesh, "InvertParCR"); // Number of k equations to solve for each x location nsys = 1 + (localmesh->LocalNz) / 2; diff --git a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx index d4e773f017..67ddef9d6f 100644 --- a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx +++ b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx @@ -53,6 +53,7 @@ InvertParDivCR::InvertParDivCR(Options* opt, CELL_LOC location, Mesh* mesh_in) : InvertParDiv(opt, location, mesh_in) { + bout::fft::checkZSerial(*localmesh, "InvertParDivCR"); // Number of k equations to solve for each x location nsys = 1 + (localmesh->LocalNz) / 2; } diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index 6fd659b375..ae92e6165d 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -2631,6 +2631,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { #if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); + bout::fft::checkZSerial(*mesh, "Zero Laplace on Field3D"); int ncz = mesh->LocalNz; Coordinates* metric = f.getCoordinates(); @@ -2734,6 +2735,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { #if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); + bout::fft::checkZSerial(*mesh, "Zero Laplace on Field3D"); const int ncz = mesh->LocalNz; ASSERT0(ncz % 2 == 0); // Allocation assumes even number @@ -2843,6 +2845,8 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); + bout::fft::checkZSerial(*mesh, "Zero Laplace on Field3D"); + Coordinates* metric = f.getCoordinates(); int ncz = mesh->LocalNz; diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 086fa2e23e..91bbddfd56 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1659,7 +1659,7 @@ Field3D Coordinates::Delp2(const Field3D& f, CELL_LOC outloc, bool useFFT) { Field3D result{emptyFrom(f).setLocation(outloc)}; - if (useFFT and not bout::build::use_metric_3d) { + if (useFFT and not bout::build::use_metric_3d and localmesh->getNZPE() == 1) { int ncz = localmesh->LocalNz; // Allocate memory @@ -1727,7 +1727,7 @@ FieldPerp Coordinates::Delp2(const FieldPerp& f, CELL_LOC outloc, bool useFFT) { int jy = f.getIndex(); result.setIndex(jy); - if (useFFT) { + if (useFFT and localmesh->getNZPE() == 1) { int ncz = localmesh->LocalNz; // Allocate memory diff --git a/src/mesh/index_derivs.cxx b/src/mesh/index_derivs.cxx index 48284c3536..70fc47b538 100644 --- a/src/mesh/index_derivs.cxx +++ b/src/mesh/index_derivs.cxx @@ -427,6 +427,7 @@ class FFTDerivativeType { ASSERT2(bout::utils::is_Field3D_v); // Should never need to call this with Field2D auto* theMesh = var.getMesh(); + ASSERT2(theMesh->getNZPE() == 1); // Only works if serial in Z for FFTs // Calculate how many Z wavenumbers will be removed const int ncz = theMesh->getNpoints(direction); @@ -493,6 +494,7 @@ class FFT2ndDerivativeType { ASSERT2(bout::utils::is_Field3D_v); // Should never need to call this with Field2D auto* theMesh = var.getMesh(); + ASSERT2(theMesh->getNZPE() == 1); // Only works if serial in Z for FFTs // Calculate how many Z wavenumbers will be removed const int ncz = theMesh->getNpoints(direction); diff --git a/src/mesh/parallel/shiftedmetric.cxx b/src/mesh/parallel/shiftedmetric.cxx index 382052047d..121c52fe3c 100644 --- a/src/mesh/parallel/shiftedmetric.cxx +++ b/src/mesh/parallel/shiftedmetric.cxx @@ -24,6 +24,7 @@ ShiftedMetric::ShiftedMetric(Mesh& m, CELL_LOC location_in, Field2D zShift_, ASSERT1(zShift.getLocation() == location); // check the coordinate system used for the grid data source ShiftedMetric::checkInputGrid(); + bout::fft::checkZSerial(m, "ShiftedMetric"); cachePhases(); } From 3b0510f539d128cb0ec4d5abcad1f24f7dbba96b Mon Sep 17 00:00:00 2001 From: David Dickinson Date: Fri, 14 Dec 2018 15:27:31 +0000 Subject: [PATCH 181/461] Update unit tests for z-guards --- tests/unit/fake_mesh.hxx | 2 +- tests/unit/include/bout/test_region.cxx | 16 ++++++++-------- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 5e37406f5d..7d7326c149 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -66,7 +66,7 @@ public: xend = nx - 2; ystart = 1; yend = ny - 2; - zstart = 0; + zstart = 0; // no guards zend = nz - 1; StaggerGrids = false; diff --git a/tests/unit/include/bout/test_region.cxx b/tests/unit/include/bout/test_region.cxx index 3b21700412..00137c1ce7 100644 --- a/tests/unit/include/bout/test_region.cxx +++ b/tests/unit/include/bout/test_region.cxx @@ -211,8 +211,8 @@ TEST_F(RegionTest, regionLoopNoBndry) { BOUT_FOR(i, region) { a[i] = 1.0; } const int nmesh = RegionTest::nx * RegionTest::ny * RegionTest::nz; - const int ninner = - (mesh->LocalNz * (1 + mesh->xend - mesh->xstart) * (1 + mesh->yend - mesh->ystart)); + const int ninner = ((1 + mesh->zend - mesh->zstart) * (1 + mesh->xend - mesh->xstart) + * (1 + mesh->yend - mesh->ystart)); int numExpectNotMatching = nmesh - ninner; int numNotMatching = 0; @@ -249,8 +249,8 @@ TEST_F(RegionTest, regionLoopNoBndrySerial) { int count = 0; BOUT_FOR_SERIAL(i, region) { ++count; } - const int ninner = - (mesh->LocalNz * (1 + mesh->xend - mesh->xstart) * (1 + mesh->yend - mesh->ystart)); + const int ninner = ((1 + mesh->zend - mesh->zstart) * (1 + mesh->xend - mesh->xstart) + * (1 + mesh->yend - mesh->ystart)); EXPECT_EQ(count, ninner); } @@ -300,8 +300,8 @@ TEST_F(RegionTest, regionLoopNoBndrySection) { } } - const int ninner = - (mesh->LocalNz * (1 + mesh->xend - mesh->xstart) * (1 + mesh->yend - mesh->ystart)); + const int ninner = ((1 + mesh->zend - mesh->zstart) * (1 + mesh->xend - mesh->xstart) + * (1 + mesh->yend - mesh->ystart)); EXPECT_EQ(count, ninner); } @@ -334,8 +334,8 @@ TEST_F(RegionTest, regionLoopNoBndryInner) { } const int nmesh = RegionTest::nx * RegionTest::ny * RegionTest::nz; - const int ninner = - (mesh->LocalNz * (1 + mesh->xend - mesh->xstart) * (1 + mesh->yend - mesh->ystart)); + const int ninner = ((1 + mesh->zend - mesh->zstart) * (1 + mesh->xend - mesh->xstart) + * (1 + mesh->yend - mesh->ystart)); int numExpectNotMatching = nmesh - ninner; int numNotMatching = 0; From f75b6a628942c481f68b2d76bfaff19197e49e9a Mon Sep 17 00:00:00 2001 From: David Dickinson Date: Fri, 14 Dec 2018 15:25:31 +0000 Subject: [PATCH 182/461] Update mms tests for z-guards --- tests/MMS/laplace/laplace.cxx | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/tests/MMS/laplace/laplace.cxx b/tests/MMS/laplace/laplace.cxx index fbcdee355c..214c022cd5 100644 --- a/tests/MMS/laplace/laplace.cxx +++ b/tests/MMS/laplace/laplace.cxx @@ -26,9 +26,8 @@ int main(int argc, char** argv) { meshoptions->get("Lx", Lx, 1.0); /*this assumes equidistant grid*/ - int nguard = mesh->xstart; - mesh->getCoordinates()->dx = Lx / (mesh->GlobalNx - 2 * nguard); - mesh->getCoordinates()->dz = TWOPI * Lx / (mesh->LocalNz); + mesh->getCoordinates()->dx = Lx / (mesh->GlobalNx - 2 * mesh->xstart); + mesh->getCoordinates()->dz = TWOPI * Lx / (mesh->GlobalNz - 2 * mesh->zstart); ///// // Create a Laplacian inversion solver From 5c7bf58b5d64bd843d64b2424c6e21be5b5686fa Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 11:40:05 +0000 Subject: [PATCH 183/461] Use `Mesh::GlobalZ` in `shiftedmetricinterp` --- src/mesh/parallel/shiftedmetricinterp.cxx | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index 4543c4c3fb..f55081410d 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -128,10 +128,10 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, for (int z = mesh.zstart; z <= mesh.zend; z++) { forward_boundary_xin->add_point( it.ind, mesh.yend, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend))), 0.25 * (1 // dy/2 + dy(it.ind, mesh.yend + 1) / dy(it.ind, mesh.yend)), // length @@ -144,10 +144,10 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, for (int z = mesh.zstart; z <= mesh.zend; z++) { backward_boundary_xin->add_point( it.ind, mesh.ystart, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1))), 0.25 * (1 // dy/2 + dy(it.ind, mesh.ystart - 1) / dy(it.ind, mesh.ystart)), @@ -161,10 +161,10 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, for (int z = mesh.zstart; z <= mesh.zend; z++) { forward_boundary_xout->add_point( it.ind, mesh.yend, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.yend + 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.yend + 1) - zShift(it.ind, mesh.yend))), 0.25 * (1 // dy/2 + dy(it.ind, mesh.yend + 1) / dy(it.ind, mesh.yend)), @@ -177,10 +177,10 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, for (int z = mesh.zstart; z <= mesh.zend; z++) { backward_boundary_xout->add_point( it.ind, mesh.ystart, z, - mesh.GlobalX(it.ind), // x - 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y - zlength * BoutReal(z) / BoutReal(mesh.GlobalNz) // z - + 0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1)), + mesh.GlobalX(it.ind), // x + 2. * PI * mesh.GlobalY(mesh.ystart - 0.5), // y + (zlength * mesh.GlobalZ(z)) // z + + (0.5 * (zShift(it.ind, mesh.ystart) - zShift(it.ind, mesh.ystart - 1))), 0.25 * (dy(it.ind, mesh.ystart - 1) / dy(it.ind, mesh.ystart) // dy/2 + 1), From 97d245b3ce26f6dacab4701c0b784e41e6b05da4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 11:47:14 +0000 Subject: [PATCH 184/461] Fix incorrect type of local variable --- src/mesh/parallel/shiftedmetricinterp.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index f55081410d..91a889af0f 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -27,6 +27,8 @@ * **************************************************************************/ +#include + #include "shiftedmetricinterp.hxx" #include "bout/constants.hxx" #include "bout/parallel_boundary_region.hxx" @@ -114,11 +116,10 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, interp_from_aligned->calcWeights(zt_prime_from); - int yvalid = mesh.LocalNy - 2 * mesh.ystart; // avoid overflow - no stencil need more than 5 points - if (yvalid > 20) { - yvalid = 20; - } + const auto yvalid = + static_cast(std::min(mesh.LocalNy - (2 * mesh.ystart), 20)); + // Create regions for parallel boundary conditions Field2D dy; mesh.get(dy, "dy", 1.); From daff6a135f2e26878ffb679d9d99669ff621935e Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 14:07:04 +0000 Subject: [PATCH 185/461] Fix some clang-tidy warnings about includes --- include/bout/fft.hxx | 1 + src/sys/generator_context.cxx | 2 ++ 2 files changed, 3 insertions(+) diff --git a/include/bout/fft.hxx b/include/bout/fft.hxx index 649b3bb5db..afc7bb8f36 100644 --- a/include/bout/fft.hxx +++ b/include/bout/fft.hxx @@ -36,6 +36,7 @@ #include +class Mesh; class Options; BOUT_ENUM_CLASS(FFT_MEASUREMENT_FLAG, estimate, measure, exhaustive); diff --git a/src/sys/generator_context.cxx b/src/sys/generator_context.cxx index 03b52ad24b..31a5662378 100644 --- a/src/sys/generator_context.cxx +++ b/src/sys/generator_context.cxx @@ -1,5 +1,7 @@ #include "bout/sys/generator_context.hxx" + #include "bout/boundary_region.hxx" +#include "bout/bout_types.hxx" #include "bout/constants.hxx" #include "bout/mesh.hxx" From 9cf05c26277e6b4d60357f3f188615b775a0a700 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 9 Jan 2026 14:47:04 +0000 Subject: [PATCH 186/461] Rename private `BoutMesh::{X,Y,Z}GLOBAL` methods for consistency These are just the `BoutReal` overloads of the equivalent `getGlobal?Index` methods --- src/mesh/impls/bout/boutmesh.cxx | 52 ++++++++++++++------------------ src/mesh/impls/bout/boutmesh.hxx | 9 ++++-- 2 files changed, 29 insertions(+), 32 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 6ce6f8783e..4e6e88bd20 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -1696,35 +1696,32 @@ int BoutMesh::PROC_NUM(int xind, int yind) const { return -1; } - return yind * NXPE + xind; + return (yind * NXPE) + xind; } -/// Returns the global X index given a local index -int BoutMesh::XGLOBAL(BoutReal xloc, BoutReal& xglo) const { - xglo = xloc + PE_XIND * MXSUB; - return static_cast(xglo); +BoutReal BoutMesh::getGlobalXIndex(BoutReal xloc) const { + return xloc + (PE_XIND * MXSUB); } -int BoutMesh::getGlobalXIndex(int xlocal) const { return xlocal + PE_XIND * MXSUB; } +int BoutMesh::getGlobalXIndex(int xlocal) const { return xlocal + (PE_XIND * MXSUB); } int BoutMesh::getGlobalXIndexNoBoundaries(int xlocal) const { - return xlocal + PE_XIND * MXSUB - MXG; + return xlocal + (PE_XIND * MXSUB) - MXG; } -int BoutMesh::getLocalXIndex(int xglobal) const { return xglobal - PE_XIND * MXSUB; } +int BoutMesh::getLocalXIndex(int xglobal) const { return xglobal - (PE_XIND * MXSUB); } int BoutMesh::getLocalXIndexNoBoundaries(int xglobal) const { - return xglobal - PE_XIND * MXSUB + MXG; + return xglobal - (PE_XIND * MXSUB) + MXG; } -int BoutMesh::YGLOBAL(BoutReal yloc, BoutReal& yglo) const { - yglo = yloc + PE_YIND * MYSUB - MYG; - return static_cast(yglo); +BoutReal BoutMesh::getGlobalYIndex(BoutReal yloc) const { + return yloc + (PE_YIND * MYSUB) - MYG; } int BoutMesh::getGlobalYIndex(int ylocal) const { - int yglobal = ylocal + PE_YIND * MYSUB; - if (jyseps1_2 > jyseps2_1 and PE_YIND * MYSUB + 2 * MYG + 1 > ny_inner) { + int yglobal = ylocal + (PE_YIND * MYSUB); + if (jyseps1_2 > jyseps2_1 and (PE_YIND * MYSUB) + (2 * MYG) + 1 > ny_inner) { // Double null, and we are past the upper target yglobal += 2 * MYG; } @@ -1732,12 +1729,12 @@ int BoutMesh::getGlobalYIndex(int ylocal) const { } int BoutMesh::getGlobalYIndexNoBoundaries(int ylocal) const { - return ylocal + PE_YIND * MYSUB - MYG; + return ylocal + (PE_YIND * MYSUB) - MYG; } int BoutMesh::getLocalYIndex(int yglobal) const { - int ylocal = yglobal - PE_YIND * MYSUB; - if (jyseps1_2 > jyseps2_1 and PE_YIND * MYSUB + 2 * MYG + 1 > ny_inner) { + int ylocal = yglobal - (PE_YIND * MYSUB); + if (jyseps1_2 > jyseps2_1 and (PE_YIND * MYSUB) + (2 * MYG) + 1 > ny_inner) { // Double null, and we are past the upper target ylocal -= 2 * MYG; } @@ -1745,7 +1742,7 @@ int BoutMesh::getLocalYIndex(int yglobal) const { } int BoutMesh::getLocalYIndexNoBoundaries(int yglobal) const { - return yglobal - PE_YIND * MYSUB + MYG; + return yglobal - (PE_YIND * MYSUB) + MYG; } int BoutMesh::YGLOBAL(int yloc, int yproc) const { return yloc + (yproc * MYSUB) - MYG; } @@ -1754,14 +1751,14 @@ int BoutMesh::YLOCAL(int yglo, int yproc) const { return yglo - (yproc * MYSUB) int BoutMesh::getGlobalZIndex(int zlocal) const { return zlocal + (PE_ZIND * MZSUB); } -int BoutMesh::getGlobalZIndexNoBoundaries(int zlocal) const { return zlocal + (PE_ZIND * MZSUB) - MZG; } +int BoutMesh::getGlobalZIndexNoBoundaries(int zlocal) const { + return zlocal + (PE_ZIND * MZSUB) - MZG; +} int BoutMesh::getLocalZIndex(int zglobal) const { return zglobal; } -/// Returns the global Z index given a local index -int BoutMesh::ZGLOBAL(BoutReal zloc, BoutReal& zglo) const { - zglo = zloc + (PE_ZIND * MZSUB); - return static_cast(zglo); +BoutReal BoutMesh::getGlobalZIndex(BoutReal zloc) const { + return zloc + (PE_ZIND * MZSUB); } int BoutMesh::getLocalZIndexNoBoundaries(int zglobal) const { return zglobal; } @@ -3136,8 +3133,7 @@ BoutReal BoutMesh::GlobalX(int jx) const { BoutReal BoutMesh::GlobalX(BoutReal jx) const { // Get global X index as a BoutReal - BoutReal xglo; - XGLOBAL(jx, xglo); + const BoutReal xglo = getGlobalXIndex(jx); if (symmetricGlobalX) { // With this definition the boundary sits dx/2 away form the first/last inner points @@ -3191,8 +3187,7 @@ BoutReal BoutMesh::GlobalY(int jy) const { BoutReal BoutMesh::GlobalY(BoutReal jy) const { // Get global Y index as a BoutReal - BoutReal yglo; - YGLOBAL(jy, yglo); + BoutReal yglo = getGlobalYIndex(jy); if (symmetricGlobalY) { BoutReal yi = yglo; @@ -3245,8 +3240,7 @@ BoutReal BoutMesh::GlobalZ(int jz) const { BoutReal BoutMesh::GlobalZ(BoutReal jz) const { // Get global Z index as a BoutReal - BoutReal zglo; - ZGLOBAL(jz, zglo); + const BoutReal zglo = getGlobalZIndex(jz); if (symmetricGlobalZ) { // With this definition the boundary sits dx/2 away form the first/last inner points diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index fea3583c4b..3923c34511 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -310,9 +310,12 @@ private: /// Is this processor in the core region? bool MYPE_IN_CORE{false}; - int XGLOBAL(BoutReal xloc, BoutReal& xglo) const; - int YGLOBAL(BoutReal yloc, BoutReal& yglo) const; - int ZGLOBAL(BoutReal zloc, BoutReal& zglo) const; + /// Returns the global X index given a local index + BoutReal getGlobalXIndex(BoutReal xloc) const; + /// Returns the global Y index given a local index + BoutReal getGlobalYIndex(BoutReal yloc) const; + /// Returns the global Z index given a local index + BoutReal getGlobalZIndex(BoutReal zloc) const; // Topology int ixseps1, ixseps2, jyseps1_1, jyseps2_1, jyseps1_2, jyseps2_2; From 22a848f6f524d29f050717ea515faa0db3128e60 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 14:00:43 +0000 Subject: [PATCH 187/461] Fix a couple of typos in comments [skip ci] Co-authored-by: David Bold --- src/mesh/impls/bout/boutmesh.cxx | 2 +- src/mesh/parallel/shiftedmetricinterp.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 4e6e88bd20..0310b2e5bd 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -3243,7 +3243,7 @@ BoutReal BoutMesh::GlobalZ(BoutReal jz) const { const BoutReal zglo = getGlobalZIndex(jz); if (symmetricGlobalZ) { - // With this definition the boundary sits dx/2 away form the first/last inner points + // With this definition the boundary sits dz/2 away form the first/last inner points return (0.5 + zglo - (nz - MZ) * 0.5) / static_cast(MZ); } return zglo / static_cast(MZ); diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index 91a889af0f..fa94416da1 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -116,7 +116,7 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, interp_from_aligned->calcWeights(zt_prime_from); - // avoid overflow - no stencil need more than 5 points + // avoid overflow - no stencil needs more than 5 points const auto yvalid = static_cast(std::min(mesh.LocalNy - (2 * mesh.ystart), 20)); From b1ffc21666a88e14a140275b75dedf3fb1274c70 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 19 Jan 2026 14:02:20 +0000 Subject: [PATCH 188/461] Rename Z-serial assert function --- include/bout/fft.hxx | 6 +++--- src/field/field3d.cxx | 6 +++--- src/invert/fft_fftw.cxx | 2 +- src/invert/laplace/impls/cyclic/cyclic_laplace.cxx | 2 +- .../impls/iterative_parallel_tri/iterative_parallel_tri.cxx | 2 +- src/invert/laplace/impls/pcr/pcr.cxx | 2 +- src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx | 2 +- src/invert/laplace/impls/serial_band/serial_band.cxx | 2 +- src/invert/laplace/impls/serial_tri/serial_tri.cxx | 2 +- src/invert/laplace/impls/spt/spt.cxx | 2 +- src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx | 2 +- src/invert/parderiv/impls/cyclic/cyclic.cxx | 2 +- src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx | 2 +- src/mesh/boundary_standard.cxx | 6 +++--- src/mesh/parallel/shiftedmetric.cxx | 2 +- 15 files changed, 21 insertions(+), 21 deletions(-) diff --git a/include/bout/fft.hxx b/include/bout/fft.hxx index afc7bb8f36..b7d36d2166 100644 --- a/include/bout/fft.hxx +++ b/include/bout/fft.hxx @@ -121,10 +121,10 @@ Array irfft(const Array& in, int length); /// Generally, FFTs must be done over the full Z domain. Currently, most /// methods using FFTs don't handle parallelising in Z #if BOUT_CHECK_LEVEL > 0 -void checkZSerial(const Mesh& mesh, std::string_view name); +void assertZSerial(const Mesh& mesh, std::string_view name); #else -inline void checkZSerial([[maybe_unused]] const Mesh& mesh, - [[maybe_unused]] std::string_view name) {} +inline void assertZSerial([[maybe_unused]] const Mesh& mesh, + [[maybe_unused]] std::string_view name) {} #endif } // namespace fft } // namespace bout diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index c0209947b6..b3448238f5 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -604,7 +604,7 @@ FieldPerp pow(const Field3D& lhs, const FieldPerp& rhs, const std::string& rgn) Field3D filter(const Field3D& var, int N0, const std::string& rgn) { - bout::fft::checkZSerial(*var.getMesh(), "`filter`"); + bout::fft::assertZSerial(*var.getMesh(), "`filter`"); checkData(var); int ncz = var.getNz(); @@ -650,7 +650,7 @@ Field3D filter(const Field3D& var, int N0, const std::string& rgn) { // Fourier filter in z with zmin Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string& rgn) { - bout::fft::checkZSerial(*var.getMesh(), "`lowPass`"); + bout::fft::assertZSerial(*var.getMesh(), "`lowPass`"); checkData(var); int ncz = var.getNz(); @@ -699,7 +699,7 @@ Field3D lowPass(const Field3D& var, int zmax, bool keep_zonal, const std::string * Use FFT to shift by an angle in the Z direction */ void shiftZ(Field3D& var, int jx, int jy, double zangle) { - bout::fft::checkZSerial(*var.getMesh(), "`shiftZ`"); + bout::fft::assertZSerial(*var.getMesh(), "`shiftZ`"); checkData(var); var.allocate(); // Ensure that var is unique Mesh* localmesh = var.getMesh(); diff --git a/src/invert/fft_fftw.cxx b/src/invert/fft_fftw.cxx index 94dd528b27..2c633da6d5 100644 --- a/src/invert/fft_fftw.cxx +++ b/src/invert/fft_fftw.cxx @@ -535,7 +535,7 @@ Array irfft(const Array& in, int length) { } #if BOUT_CHECK_LEVEL > 0 -void checkZSerial(const Mesh& mesh, std::string_view name) { +void assertZSerial(const Mesh& mesh, std::string_view name) { if (mesh.getNZPE() != 1) { throw BoutException("{} uses FFTs which are currently incompatible with multiple " "processors in Z (using {})", diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index adb30def83..80b22e73f7 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -57,7 +57,7 @@ LaplaceCyclic::LaplaceCyclic(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), C1coef(1.0), C2coef(1.0), Dcoef(1.0) { - bout::fft::checkZSerial(*localmesh, "`cyclic` inversion"); + bout::fft::assertZSerial(*localmesh, "`cyclic` inversion"); Acoef.setLocation(location); C1coef.setLocation(location); diff --git a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx index 8743d59db2..e9219091e2 100644 --- a/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx +++ b/src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx @@ -67,7 +67,7 @@ LaplaceIPT::LaplaceIPT(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED au(ny, nmode), bu(ny, nmode), rl(nmode), ru(nmode), r1(ny, nmode), r2(ny, nmode), first_call(ny), x0saved(ny, 4, nmode), converged(nmode), fine_error(4, nmode) { - bout::fft::checkZSerial(*localmesh, "`ipt` inversion"); + bout::fft::assertZSerial(*localmesh, "`ipt` inversion"); A.setLocation(location); C.setLocation(location); diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index 29f007118c..a33bd7eef2 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -67,7 +67,7 @@ LaplacePCR::LaplacePCR(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED ncx(localmesh->LocalNx), ny(localmesh->LocalNy), avec(ny, nmode, ncx), bvec(ny, nmode, ncx), cvec(ny, nmode, ncx) { - bout::fft::checkZSerial(*localmesh, "`pcr` inversion"); + bout::fft::assertZSerial(*localmesh, "`pcr` inversion"); Acoef.setLocation(location); C1coef.setLocation(location); diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index 73b7d6ec5c..d471775990 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -65,7 +65,7 @@ LaplacePCR_THOMAS::LaplacePCR_THOMAS(Options* opt, CELL_LOC loc, Mesh* mesh_in, ncx(localmesh->LocalNx), ny(localmesh->LocalNy), avec(ny, nmode, ncx), bvec(ny, nmode, ncx), cvec(ny, nmode, ncx) { - bout::fft::checkZSerial(*localmesh, "`pcr_thomas` inversion"); + bout::fft::assertZSerial(*localmesh, "`pcr_thomas` inversion"); Acoef.setLocation(location); C1coef.setLocation(location); diff --git a/src/invert/laplace/impls/serial_band/serial_band.cxx b/src/invert/laplace/impls/serial_band/serial_band.cxx index 921f2af24d..0cf8d7259d 100644 --- a/src/invert/laplace/impls/serial_band/serial_band.cxx +++ b/src/invert/laplace/impls/serial_band/serial_band.cxx @@ -46,7 +46,7 @@ LaplaceSerialBand::LaplaceSerialBand(Options* opt, const CELL_LOC loc, Mesh* mes Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), Ccoef(1.0), Dcoef(1.0) { - bout::fft::checkZSerial(*localmesh, "`band` inversion"); + bout::fft::assertZSerial(*localmesh, "`band` inversion"); Acoef.setLocation(location); Ccoef.setLocation(location); diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.cxx b/src/invert/laplace/impls/serial_tri/serial_tri.cxx index 1a64984a3e..a14e0e4a26 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.cxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.cxx @@ -40,7 +40,7 @@ LaplaceSerialTri::LaplaceSerialTri(Options* opt, CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), A(0.0), C(1.0), D(1.0) { - bout::fft::checkZSerial(*localmesh, "`tri` inversion"); + bout::fft::assertZSerial(*localmesh, "`tri` inversion"); A.setLocation(location); C.setLocation(location); diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index 1a5cb5f23c..cd24ee1acf 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -46,7 +46,7 @@ LaplaceSPT::LaplaceSPT(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Solver* UNUSED(solver)) : Laplacian(opt, loc, mesh_in), Acoef(0.0), Ccoef(1.0), Dcoef(1.0) { - bout::fft::checkZSerial(*localmesh, "`spt` inversion"); + bout::fft::assertZSerial(*localmesh, "`spt` inversion"); Acoef.setLocation(location); Ccoef.setLocation(location); diff --git a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx index 56780eaf5c..b3e619df0c 100644 --- a/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx +++ b/src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx @@ -13,7 +13,7 @@ LaplaceXZcyclic::LaplaceXZcyclic(Mesh* m, Options* options, const CELL_LOC loc) : LaplaceXZ(m, options, loc) { // Note: `m` may be nullptr, but localmesh is set in LaplaceXZ base constructor - bout::fft::checkZSerial(*localmesh, "`cyclic` X-Z inversion"); + bout::fft::assertZSerial(*localmesh, "`cyclic` X-Z inversion"); // Number of Z Fourier modes, including DC nmode = (localmesh->LocalNz) / 2 + 1; diff --git a/src/invert/parderiv/impls/cyclic/cyclic.cxx b/src/invert/parderiv/impls/cyclic/cyclic.cxx index d20537a0f3..c32c3d4b2d 100644 --- a/src/invert/parderiv/impls/cyclic/cyclic.cxx +++ b/src/invert/parderiv/impls/cyclic/cyclic.cxx @@ -54,7 +54,7 @@ InvertParCR::InvertParCR(Options* opt, CELL_LOC location, Mesh* mesh_in) : InvertPar(opt, location, mesh_in), A(1.0), B(0.0), C(0.0), D(0.0), E(0.0) { - bout::fft::checkZSerial(*localmesh, "InvertParCR"); + bout::fft::assertZSerial(*localmesh, "InvertParCR"); // Number of k equations to solve for each x location nsys = 1 + (localmesh->LocalNz) / 2; diff --git a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx index 67ddef9d6f..aad01c5f2f 100644 --- a/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx +++ b/src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx @@ -53,7 +53,7 @@ InvertParDivCR::InvertParDivCR(Options* opt, CELL_LOC location, Mesh* mesh_in) : InvertParDiv(opt, location, mesh_in) { - bout::fft::checkZSerial(*localmesh, "InvertParDivCR"); + bout::fft::assertZSerial(*localmesh, "InvertParDivCR"); // Number of k equations to solve for each x location nsys = 1 + (localmesh->LocalNz) / 2; } diff --git a/src/mesh/boundary_standard.cxx b/src/mesh/boundary_standard.cxx index ae92e6165d..39a1ea3641 100644 --- a/src/mesh/boundary_standard.cxx +++ b/src/mesh/boundary_standard.cxx @@ -2631,7 +2631,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { #if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); - bout::fft::checkZSerial(*mesh, "Zero Laplace on Field3D"); + bout::fft::assertZSerial(*mesh, "Zero Laplace on Field3D"); int ncz = mesh->LocalNz; Coordinates* metric = f.getCoordinates(); @@ -2735,7 +2735,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { #if not(BOUT_USE_METRIC_3D) Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); - bout::fft::checkZSerial(*mesh, "Zero Laplace on Field3D"); + bout::fft::assertZSerial(*mesh, "Zero Laplace on Field3D"); const int ncz = mesh->LocalNz; ASSERT0(ncz % 2 == 0); // Allocation assumes even number @@ -2845,7 +2845,7 @@ void BoundaryNeumann_NonOrthogonal::apply(Field3D& f) { Mesh* mesh = bndry->localmesh; ASSERT1(mesh == f.getMesh()); - bout::fft::checkZSerial(*mesh, "Zero Laplace on Field3D"); + bout::fft::assertZSerial(*mesh, "Zero Laplace on Field3D"); Coordinates* metric = f.getCoordinates(); diff --git a/src/mesh/parallel/shiftedmetric.cxx b/src/mesh/parallel/shiftedmetric.cxx index 121c52fe3c..64c6d9a2ce 100644 --- a/src/mesh/parallel/shiftedmetric.cxx +++ b/src/mesh/parallel/shiftedmetric.cxx @@ -24,7 +24,7 @@ ShiftedMetric::ShiftedMetric(Mesh& m, CELL_LOC location_in, Field2D zShift_, ASSERT1(zShift.getLocation() == location); // check the coordinate system used for the grid data source ShiftedMetric::checkInputGrid(); - bout::fft::checkZSerial(m, "ShiftedMetric"); + bout::fft::assertZSerial(m, "ShiftedMetric"); cachePhases(); } From a058da43591c666ae4108977358a1ae5d3674959 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 30 Jan 2026 13:10:06 +0000 Subject: [PATCH 189/461] Don't include boundaries in `GlobalZ` --- src/mesh/impls/bout/boutmesh.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 0310b2e5bd..4aaa760c04 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -3232,9 +3232,11 @@ BoutReal BoutMesh::GlobalY(BoutReal jy) const { BoutReal BoutMesh::GlobalZ(int jz) const { if (symmetricGlobalZ) { // With this definition the boundary sits dz/2 away form the first/last inner points - return (0.5 + getGlobalZIndex(jz) - (nz - MZ) * 0.5) / static_cast(MZ); + return (0.5 + getGlobalZIndexNoBoundaries(jz) - (nz - MZ) * 0.5) + / static_cast(MZ); } - return static_cast(getGlobalZIndex(jz)) / static_cast(MZ); + return static_cast(getGlobalZIndexNoBoundaries(jz)) + / static_cast(MZ); } BoutReal BoutMesh::GlobalZ(BoutReal jz) const { From f03eabd8cfd1d9dc71622fd90e395f904b281ebb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 30 Jan 2026 13:10:38 +0000 Subject: [PATCH 190/461] Use global Z index functions in test-communications --- .../test-communications.cxx | 32 ++++++++++--------- 1 file changed, 17 insertions(+), 15 deletions(-) diff --git a/tests/integrated/test-communications/test-communications.cxx b/tests/integrated/test-communications/test-communications.cxx index 54c266ece2..5acbe3ee1a 100644 --- a/tests/integrated/test-communications/test-communications.cxx +++ b/tests/integrated/test-communications/test-communications.cxx @@ -11,10 +11,10 @@ int main(int argc, char** argv) { // interior cells BOUT_FOR(i, f.getRegion("RGN_NOBNDRY")) { - f[i] = mesh->GlobalNzNoBoundaries - * (mesh->GlobalNyNoBoundaries * mesh->getGlobalXIndexNoBoundaries(i.x()) - + mesh->getGlobalYIndexNoBoundaries(i.y())) - + i.z(); + f[i] = (mesh->GlobalNzNoBoundaries + * (mesh->GlobalNyNoBoundaries * mesh->getGlobalXIndexNoBoundaries(i.x()) + + mesh->getGlobalYIndexNoBoundaries(i.y()))) + + mesh->getGlobalZIndexNoBoundaries(i.z()); } // lower x-boundary cells @@ -25,10 +25,10 @@ int main(int argc, char** argv) { for (int y = mesh->ystart; y <= mesh->yend; y++) { for (int z = mesh->zstart; z <= mesh->zend; z++) { f(x, y, z) = startind - + mesh->GlobalNzNoBoundaries - * (mesh->GlobalNyNoBoundaries * x - + mesh->getGlobalYIndexNoBoundaries(y)) - + z; + + (mesh->GlobalNzNoBoundaries + * (mesh->GlobalNyNoBoundaries * x + + mesh->getGlobalYIndexNoBoundaries(y))) + + mesh->getGlobalZIndexNoBoundaries(z); } } } @@ -41,10 +41,10 @@ int main(int argc, char** argv) { for (int y = mesh->ystart; y <= mesh->yend; y++) { for (int z = mesh->zstart; z <= mesh->zend; z++) { f(mesh->xend + 1 + x, y, z) = startind - + mesh->GlobalNzNoBoundaries - * (mesh->GlobalNyNoBoundaries * x - + mesh->getGlobalYIndexNoBoundaries(y)) - + z; + + (mesh->GlobalNzNoBoundaries + * (mesh->GlobalNyNoBoundaries * x + + mesh->getGlobalYIndexNoBoundaries(y))) + + mesh->getGlobalZIndexNoBoundaries(z); } } } @@ -56,8 +56,9 @@ int main(int argc, char** argv) { int x = it.ind; for (int y = 0; y < mesh->ystart; y++) { for (int z = mesh->zstart; z <= mesh->zend; z++) { - f(x, y, z) = - startind + mesh->GlobalNzNoBoundaries * (mesh->getGlobalXIndex(x) + y) + z; + f(x, y, z) = startind + + (mesh->GlobalNzNoBoundaries * (mesh->getGlobalXIndex(x) + y)) + + mesh->getGlobalZIndexNoBoundaries(z); } } } @@ -69,7 +70,8 @@ int main(int argc, char** argv) { for (int y = 0; y < mesh->ystart; y++) { for (int z = mesh->zstart; z <= mesh->zend; z++) { f(x, mesh->yend + 1 + y, z) = - startind + mesh->GlobalNzNoBoundaries * (mesh->getGlobalXIndex(x) + y) + z; + startind + (mesh->GlobalNzNoBoundaries * (mesh->getGlobalXIndex(x) + y)) + + mesh->getGlobalZIndexNoBoundaries(z); } } } From 30c0efd74c1abfb5f32254debad3db5d2d960344 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 30 Jan 2026 13:12:49 +0000 Subject: [PATCH 191/461] Use global Z index functions in `ShiftedMetricInterp` --- src/mesh/parallel/shiftedmetricinterp.cxx | 34 +++++++++++++++-------- 1 file changed, 22 insertions(+), 12 deletions(-) diff --git a/src/mesh/parallel/shiftedmetricinterp.cxx b/src/mesh/parallel/shiftedmetricinterp.cxx index fa94416da1..c71618ab19 100644 --- a/src/mesh/parallel/shiftedmetricinterp.cxx +++ b/src/mesh/parallel/shiftedmetricinterp.cxx @@ -30,7 +30,10 @@ #include #include "shiftedmetricinterp.hxx" + +#include "bout/boutexception.hxx" #include "bout/constants.hxx" +#include "bout/field3d.hxx" #include "bout/parallel_boundary_region.hxx" ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, @@ -38,12 +41,19 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, Options* opt) : ParallelTransform(mesh, opt), location(location_in), zShift(std::move(zShift_in)), zlength(zlength_in), ydown_index(mesh.ystart) { + + if (mesh.getNZPE() > 1) { + throw BoutException("ShiftedMetricInterp only works with 1 processor in Z"); + } + // check the coordinate system used for the grid data source ShiftedMetricInterp::checkInputGrid(); // Allocate space for interpolator cache: y-guard cells in each direction parallel_slice_interpolators.resize(mesh.ystart * 2); + const BoutReal z_factor = static_cast(mesh.GlobalNzNoBoundaries) / zlength; + // Create the Interpolation objects and set whether they go up or down the // magnetic field auto& interp_options = options["zinterpolation"]; @@ -62,16 +72,16 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, // Find the index positions where the magnetic field line intersects the x-z plane // y_offset points up - Field3D zt_prime_up(&mesh), zt_prime_down(&mesh); + Field3D zt_prime_up(&mesh); + Field3D zt_prime_down(&mesh); zt_prime_up.allocate(); zt_prime_down.allocate(); for (const auto& i : zt_prime_up.getRegion(RGN_NOY)) { // Field line moves in z by an angle zShift(i,j+1)-zShift(i,j) when going // from j to j+1, but we want the shift in index-space - zt_prime_up[i] = static_cast(i.z()) - + (zShift[i.yp(y_offset + 1)] - zShift[i]) - * static_cast(mesh.GlobalNz) / zlength; + zt_prime_up[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + + ((zShift[i.yp(y_offset + 1)] - zShift[i]) * z_factor); } parallel_slice_interpolators[yup_index + y_offset]->calcWeights(zt_prime_up); @@ -79,9 +89,8 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, for (const auto& i : zt_prime_down.getRegion(RGN_NOY)) { // Field line moves in z by an angle -(zShift(i,j)-zShift(i,j-1)) when going // from j to j-1, but we want the shift in index-space - zt_prime_down[i] = static_cast(i.z()) - - (zShift[i] - zShift[i.ym(y_offset + 1)]) - * static_cast(mesh.GlobalNz) / zlength; + zt_prime_down[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + - ((zShift[i] - zShift[i.ym(y_offset + 1)]) * z_factor); } parallel_slice_interpolators[ydown_index + y_offset]->calcWeights(zt_prime_down); @@ -93,15 +102,16 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, interp_from_aligned = ZInterpolationFactory::getInstance().create(&interp_options, 0, &mesh); - Field3D zt_prime_to(&mesh), zt_prime_from(&mesh); + Field3D zt_prime_to(&mesh); + Field3D zt_prime_from(&mesh); zt_prime_to.allocate(); zt_prime_from.allocate(); for (const auto& i : zt_prime_to) { // Field line moves in z by an angle zShift(i,j) when going // from y0 to y(j), but we want the shift in index-space - zt_prime_to[i] = static_cast(i.z()) - + zShift[i] * static_cast(mesh.GlobalNz) / zlength; + zt_prime_to[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + + (zShift[i] * z_factor); } interp_to_aligned->calcWeights(zt_prime_to); @@ -110,8 +120,8 @@ ShiftedMetricInterp::ShiftedMetricInterp(Mesh& mesh, CELL_LOC location_in, // Field line moves in z by an angle zShift(i,j) when going // from y0 to y(j), but we want the shift in index-space. // Here we reverse the shift, so subtract zShift - zt_prime_from[i] = static_cast(i.z()) - - zShift[i] * static_cast(mesh.GlobalNz) / zlength; + zt_prime_from[i] = static_cast(mesh.getGlobalZIndexNoBoundaries(i.z())) + - (zShift[i] * z_factor); } interp_from_aligned->calcWeights(zt_prime_from); From ef95de8ed745f466cc6d355b38bcfac52f6b8ab8 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 30 Jan 2026 16:55:39 +0000 Subject: [PATCH 192/461] Add unit tests for `ShiftedMetricInterp` --- .../unit/mesh/parallel/test_shiftedmetric.cxx | 109 +++++++++++------- 1 file changed, 68 insertions(+), 41 deletions(-) diff --git a/tests/unit/mesh/parallel/test_shiftedmetric.cxx b/tests/unit/mesh/parallel/test_shiftedmetric.cxx index 7e111e9c4e..ecbce620c3 100644 --- a/tests/unit/mesh/parallel/test_shiftedmetric.cxx +++ b/tests/unit/mesh/parallel/test_shiftedmetric.cxx @@ -1,9 +1,14 @@ #include "bout/build_defines.hxx" #include "gtest/gtest.h" +#include +#include "../../src/mesh/parallel/shiftedmetricinterp.hxx" #include "test_extras.hxx" #include "bout/fft.hxx" +#include "bout/options_io.hxx" +#include "bout/output.hxx" +#include "bout/paralleltransform.hxx" #if BOUT_HAS_FFTW #include "fake_mesh.hxx" @@ -11,6 +16,7 @@ // The unit tests use the global mesh using namespace bout::globals; +template class ShiftedMetricTest : public ::testing::Test { public: ShiftedMetricTest() { @@ -41,7 +47,7 @@ class ShiftedMetricTest : public ::testing::Test { // No call to Coordinates::geometry() needed here auto coords = mesh->getCoordinates(); - coords->setParallelTransform(bout::utils::make_unique( + coords->setParallelTransform(bout::utils::make_unique( *mesh, CELL_CENTRE, zShift, coords->zlength()(0, 0))); Field3D input_temp{mesh}; @@ -90,7 +96,10 @@ class ShiftedMetricTest : public ::testing::Test { Field3D input; }; -TEST_F(ShiftedMetricTest, ToFieldAligned) { +using ShiftedMetricTypes = ::testing::Types; +TYPED_TEST_SUITE(ShiftedMetricTest, ShiftedMetricTypes); + +TYPED_TEST(ShiftedMetricTest, ToFieldAligned) { Field3D expected{mesh}; expected.setDirectionY(YDirectionType::Aligned); @@ -118,18 +127,18 @@ TEST_F(ShiftedMetricTest, ToFieldAligned) { {4., 5., 1., 3., 2.}, {2., 4., 3., 5., 1.}}}); - Field3D result = toFieldAligned(input); + Field3D result = toFieldAligned(this->input); EXPECT_TRUE(IsFieldEqual(result, expected, "RGN_ALL", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(fromFieldAligned(result), input)); + EXPECT_TRUE(IsFieldEqual(fromFieldAligned(result), this->input)); EXPECT_TRUE(areFieldsCompatible(result, expected)); - EXPECT_FALSE(areFieldsCompatible(result, input)); + EXPECT_FALSE(areFieldsCompatible(result, this->input)); } -TEST_F(ShiftedMetricTest, FromFieldAligned) { +TYPED_TEST(ShiftedMetricTest, FromFieldAligned) { // reset input.yDirectionType so that fromFieldAligned is not a null // operation - input.setDirectionY(YDirectionType::Aligned); + this->input.setDirectionY(YDirectionType::Aligned); Field3D expected{mesh, CELL_CENTRE}; expected.setDirectionY(YDirectionType::Standard); @@ -158,28 +167,33 @@ TEST_F(ShiftedMetricTest, FromFieldAligned) { {2., 4., 5., 1., 3.}, {5., 1., 2., 4., 3.}}}); - Field3D result = fromFieldAligned(input); + Field3D result = fromFieldAligned(this->input); + // Loosen tolerance a bit due to FFTs EXPECT_TRUE(IsFieldEqual(result, expected, "RGN_ALL", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(toFieldAligned(result), input, "RGN_ALL", FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(toFieldAligned(result), this->input, "RGN_ALL", FFTTolerance)); EXPECT_TRUE(areFieldsCompatible(result, expected)); - EXPECT_FALSE(areFieldsCompatible(result, input)); + EXPECT_FALSE(areFieldsCompatible(result, this->input)); } -TEST_F(ShiftedMetricTest, FromToFieldAligned) { - EXPECT_TRUE(IsFieldEqual(fromFieldAligned(toFieldAligned(input)), input, "RGN_ALL", +TYPED_TEST(ShiftedMetricTest, FromToFieldAligned) { + EXPECT_TRUE(IsFieldEqual(fromFieldAligned(toFieldAligned(this->input)), this->input, "RGN_ALL", FFTTolerance)); } -TEST_F(ShiftedMetricTest, ToFromFieldAligned) { - input.setDirectionY(YDirectionType::Aligned); +TYPED_TEST(ShiftedMetricTest, ToFromFieldAligned) { + this->input.setDirectionY(YDirectionType::Aligned); - EXPECT_TRUE(IsFieldEqual(toFieldAligned(fromFieldAligned(input)), input, "RGN_ALL", + EXPECT_TRUE(IsFieldEqual(toFieldAligned(fromFieldAligned(this->input)), this->input, "RGN_ALL", FFTTolerance)); } -TEST_F(ShiftedMetricTest, ToFieldAlignedFieldPerp) { +TYPED_TEST(ShiftedMetricTest, ToFieldAlignedFieldPerp) { + if constexpr (std::is_same_v) { + GTEST_SKIP_("Not implemented yet"); + } + Field3D expected{mesh}; expected.setDirectionY(YDirectionType::Aligned); @@ -207,22 +221,26 @@ TEST_F(ShiftedMetricTest, ToFieldAlignedFieldPerp) { {4., 5., 1., 3., 2.}, {2., 4., 3., 5., 1.}}}); - FieldPerp result = toFieldAligned(sliceXZ(input, 3), "RGN_NOX"); + FieldPerp result = toFieldAligned(sliceXZ(this->input, 3), "RGN_NOX"); // Note that the region argument does not do anything for FieldPerp, as // FieldPerp does not have a getRegion2D() method. Values are never set in // the x-guard or x-boundary cells EXPECT_TRUE(IsFieldEqual(result, sliceXZ(expected, 3), "RGN_NOBNDRY", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(fromFieldAligned(result, "RGN_NOX"), sliceXZ(input, 3), + EXPECT_TRUE(IsFieldEqual(fromFieldAligned(result, "RGN_NOX"), sliceXZ(this->input, 3), "RGN_NOBNDRY", FFTTolerance)); EXPECT_TRUE(areFieldsCompatible(result, sliceXZ(expected, 3))); - EXPECT_FALSE(areFieldsCompatible(result, sliceXZ(input, 3))); + EXPECT_FALSE(areFieldsCompatible(result, sliceXZ(this->input, 3))); } -TEST_F(ShiftedMetricTest, FromFieldAlignedFieldPerp) { - // reset input.yDirectionType so that fromFieldAligned is not a null +TYPED_TEST(ShiftedMetricTest, FromFieldAlignedFieldPerp) { + if constexpr (std::is_same_v) { + GTEST_SKIP_("Not implemented yet"); + } + + // reset this->input.yDirectionType so that fromFieldAligned is not a null // operation - input.setDirectionY(YDirectionType::Aligned); + this->input.setDirectionY(YDirectionType::Aligned); Field3D expected{mesh, CELL_CENTRE}; expected.setDirectionY(YDirectionType::Standard); @@ -251,43 +269,52 @@ TEST_F(ShiftedMetricTest, FromFieldAlignedFieldPerp) { {2., 4., 5., 1., 3.}, {5., 1., 2., 4., 3.}}}); - FieldPerp result = fromFieldAligned(sliceXZ(input, 4), "RGN_NOX"); + FieldPerp result = fromFieldAligned(sliceXZ(this->input, 4), "RGN_NOX"); // Note that the region argument does not do anything for FieldPerp, as // FieldPerp does not have a getRegion2D() method. Values are never set in // the x-guard or x-boundary cells EXPECT_TRUE(IsFieldEqual(result, sliceXZ(expected, 4), "RGN_NOBNDRY", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(toFieldAligned(result, "RGN_NOX"), sliceXZ(input, 4), + EXPECT_TRUE(IsFieldEqual(toFieldAligned(result, "RGN_NOX"), sliceXZ(this->input, 4), "RGN_NOBNDRY", FFTTolerance)); EXPECT_TRUE(areFieldsCompatible(result, sliceXZ(expected, 4))); - EXPECT_FALSE(areFieldsCompatible(result, sliceXZ(input, 4))); + EXPECT_FALSE(areFieldsCompatible(result, sliceXZ(this->input, 4))); } -TEST_F(ShiftedMetricTest, FromToFieldAlignedFieldPerp) { +TYPED_TEST(ShiftedMetricTest, FromToFieldAlignedFieldPerp) { + if constexpr (std::is_same_v) { + GTEST_SKIP_("Not implemented yet"); + } + // Note that the region argument does not do anything for FieldPerp, as // FieldPerp does not have a getRegion2D() method. Values are never set in // the x-guard or x-boundary cells EXPECT_TRUE(IsFieldEqual( - fromFieldAligned(toFieldAligned(sliceXZ(input, 2), "RGN_NOX"), "RGN_NOX"), - sliceXZ(input, 2), "RGN_NOBNDRY", FFTTolerance)); + fromFieldAligned(toFieldAligned(sliceXZ(this->input, 2), "RGN_NOX"), "RGN_NOX"), + sliceXZ(this->input, 2), "RGN_NOBNDRY", FFTTolerance)); } -TEST_F(ShiftedMetricTest, ToFromFieldAlignedFieldPerp) { +TYPED_TEST(ShiftedMetricTest, ToFromFieldAlignedFieldPerp) { + if constexpr (std::is_same_v) { + GTEST_SKIP_("Not implemented yet"); + } + // Note that the region argument does not do anything for FieldPerp, as // FieldPerp does not have a getRegion2D() method. Values are never set in // the x-guard or x-boundary cells - input.setDirectionY(YDirectionType::Aligned); + this->input.setDirectionY(YDirectionType::Aligned); EXPECT_TRUE(IsFieldEqual( - toFieldAligned(fromFieldAligned(sliceXZ(input, 6), "RGN_NOX"), "RGN_NOX"), - sliceXZ(input, 6), "RGN_NOBNDRY", FFTTolerance)); + toFieldAligned(fromFieldAligned(sliceXZ(this->input, 6), "RGN_NOX"), "RGN_NOX"), + sliceXZ(this->input, 6), "RGN_NOBNDRY", FFTTolerance)); } -TEST_F(ShiftedMetricTest, CalcParallelSlices) { +TYPED_TEST(ShiftedMetricTest, CalcParallelSlices) { + WithQuietOutput quiet_info{output_info}; + // We don't shift in the guard cells, and the parallel slices are // stored offset in y, therefore we need to make new regions that we // can compare the expected and actual outputs over - output_info.disable(); mesh->addRegion3D("RGN_YUP", Region(0, mesh->LocalNx - 1, mesh->ystart + 1, mesh->yend + 1, 0, mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); @@ -304,7 +331,7 @@ TEST_F(ShiftedMetricTest, CalcParallelSlices) { output_info.enable(); // Actual interesting bit here! - input.getCoordinates()->getParallelTransform().calcParallelSlices(input); + this->input.getCoordinates()->getParallelTransform().calcParallelSlices(this->input); // Expected output values Field3D expected_up_1{mesh}; @@ -386,9 +413,9 @@ TEST_F(ShiftedMetricTest, CalcParallelSlices) { {0., 0., 0., 0., 0.}, {0., 0., 0., 0., 0.}}}); - Field3D expected_down2{mesh}; + Field3D expected_down_2{mesh}; - fillField(expected_down2, {{{4., 5., 1., 2., 3.}, + fillField(expected_down_2, {{{4., 5., 1., 2., 3.}, {4., 5., 2., 1., 3.}, {4., 5., 1., 3., 2.}, {0., 0., 0., 0., 0.}, @@ -412,9 +439,9 @@ TEST_F(ShiftedMetricTest, CalcParallelSlices) { {0., 0., 0., 0., 0.}, {0., 0., 0., 0., 0.}}}); - EXPECT_TRUE(IsFieldEqual(input.ynext(1), expected_up_1, "RGN_YUP", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(input.ynext(2), expected_up_2, "RGN_YUP2", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(input.ynext(-1), expected_down_1, "RGN_YDOWN", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(input.ynext(-2), expected_down2, "RGN_YDOWN2", FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(this->input.ynext(1), expected_up_1, "RGN_YUP", FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(this->input.ynext(2), expected_up_2, "RGN_YUP2", FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(this->input.ynext(-1), expected_down_1, "RGN_YDOWN", FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(this->input.ynext(-2), expected_down_2, "RGN_YDOWN2", FFTTolerance)); } #endif From 81817bbce9c77c0b506e2e5f873ee262724cd0dc Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 30 Jan 2026 17:59:08 +0000 Subject: [PATCH 193/461] Don't check `ShiftedMetricInterp` results in any guard cells --- .../unit/mesh/parallel/test_shiftedmetric.cxx | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/tests/unit/mesh/parallel/test_shiftedmetric.cxx b/tests/unit/mesh/parallel/test_shiftedmetric.cxx index ecbce620c3..3e6f90104b 100644 --- a/tests/unit/mesh/parallel/test_shiftedmetric.cxx +++ b/tests/unit/mesh/parallel/test_shiftedmetric.cxx @@ -315,19 +315,35 @@ TYPED_TEST(ShiftedMetricTest, CalcParallelSlices) { // We don't shift in the guard cells, and the parallel slices are // stored offset in y, therefore we need to make new regions that we // can compare the expected and actual outputs over + + constexpr bool is_shifted_interp = std::is_same_v; + + // ShiftedMetricInterp doesn't seem to store interpolated values in _any_ of the + // guards, so we can only check the interior X point + const int xstart = is_shifted_interp ? mesh->xstart : 0; + const int xend = is_shifted_interp ? mesh->xend : mesh->LocalNx - 1; + + // It also means we can't check in the _y_ guards either. + // TODO(peter): Is this a bug? + const int yup_1_end = is_shifted_interp ? mesh->yend : mesh->yend + 1; + const int yup_2_end = is_shifted_interp ? mesh->yend : mesh->yend + 2; + + const int ydown_1_start = is_shifted_interp ? mesh->ystart : mesh->ystart - 1; + const int ydown_2_start = is_shifted_interp ? mesh->ystart : mesh->ystart - 2; + mesh->addRegion3D("RGN_YUP", - Region(0, mesh->LocalNx - 1, mesh->ystart + 1, mesh->yend + 1, - 0, mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); + Region(xstart, xend, mesh->ystart + 1, yup_1_end, 0, + mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); mesh->addRegion3D("RGN_YUP2", - Region(0, mesh->LocalNx - 1, mesh->ystart + 2, mesh->yend + 2, - 0, mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); + Region(xstart, xend, mesh->ystart + 2, yup_2_end, 0, + mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); mesh->addRegion3D("RGN_YDOWN", - Region(0, mesh->LocalNx - 1, mesh->ystart - 1, mesh->yend - 1, - 0, mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); + Region(xstart, xend, ydown_1_start, mesh->yend - 1, 0, + mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); mesh->addRegion3D("RGN_YDOWN2", - Region(0, mesh->LocalNx - 1, mesh->ystart - 2, mesh->yend - 2, - 0, mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); + Region(xstart, xend, ydown_2_start, mesh->yend - 2, 0, + mesh->LocalNz - 1, mesh->LocalNy, mesh->LocalNz)); output_info.enable(); // Actual interesting bit here! From a71cad2dd6ace5741a754e2ca7daacd4bb094e0e Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Fri, 30 Jan 2026 18:20:30 +0000 Subject: [PATCH 194/461] [bot] Apply format changes --- .../unit/mesh/parallel/test_shiftedmetric.cxx | 62 ++++++++++--------- 1 file changed, 32 insertions(+), 30 deletions(-) diff --git a/tests/unit/mesh/parallel/test_shiftedmetric.cxx b/tests/unit/mesh/parallel/test_shiftedmetric.cxx index 3e6f90104b..579e6fdd7b 100644 --- a/tests/unit/mesh/parallel/test_shiftedmetric.cxx +++ b/tests/unit/mesh/parallel/test_shiftedmetric.cxx @@ -169,7 +169,6 @@ TYPED_TEST(ShiftedMetricTest, FromFieldAligned) { Field3D result = fromFieldAligned(this->input); - // Loosen tolerance a bit due to FFTs EXPECT_TRUE(IsFieldEqual(result, expected, "RGN_ALL", FFTTolerance)); EXPECT_TRUE(IsFieldEqual(toFieldAligned(result), this->input, "RGN_ALL", FFTTolerance)); @@ -178,15 +177,15 @@ TYPED_TEST(ShiftedMetricTest, FromFieldAligned) { } TYPED_TEST(ShiftedMetricTest, FromToFieldAligned) { - EXPECT_TRUE(IsFieldEqual(fromFieldAligned(toFieldAligned(this->input)), this->input, "RGN_ALL", - FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(fromFieldAligned(toFieldAligned(this->input)), this->input, + "RGN_ALL", FFTTolerance)); } TYPED_TEST(ShiftedMetricTest, ToFromFieldAligned) { this->input.setDirectionY(YDirectionType::Aligned); - EXPECT_TRUE(IsFieldEqual(toFieldAligned(fromFieldAligned(this->input)), this->input, "RGN_ALL", - FFTTolerance)); + EXPECT_TRUE(IsFieldEqual(toFieldAligned(fromFieldAligned(this->input)), this->input, + "RGN_ALL", FFTTolerance)); } TYPED_TEST(ShiftedMetricTest, ToFieldAlignedFieldPerp) { @@ -432,32 +431,35 @@ TYPED_TEST(ShiftedMetricTest, CalcParallelSlices) { Field3D expected_down_2{mesh}; fillField(expected_down_2, {{{4., 5., 1., 2., 3.}, - {4., 5., 2., 1., 3.}, - {4., 5., 1., 3., 2.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}}, - - {{1., 3., 4., 5., 2.}, - {3., 2., 4., 5., 1.}, - {2., 4., 3., 5., 1.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}}, - - {{5., 1., 3., 2., 4.}, - {5., 1., 2., 4., 3.}, - {4., 1., 2., 3., 5.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}, - {0., 0., 0., 0., 0.}}}); + {4., 5., 2., 1., 3.}, + {4., 5., 1., 3., 2.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}}, + + {{1., 3., 4., 5., 2.}, + {3., 2., 4., 5., 1.}, + {2., 4., 3., 5., 1.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}}, + + {{5., 1., 3., 2., 4.}, + {5., 1., 2., 4., 3.}, + {4., 1., 2., 3., 5.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}, + {0., 0., 0., 0., 0.}}}); EXPECT_TRUE(IsFieldEqual(this->input.ynext(1), expected_up_1, "RGN_YUP", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(this->input.ynext(2), expected_up_2, "RGN_YUP2", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(this->input.ynext(-1), expected_down_1, "RGN_YDOWN", FFTTolerance)); - EXPECT_TRUE(IsFieldEqual(this->input.ynext(-2), expected_down_2, "RGN_YDOWN2", FFTTolerance)); + EXPECT_TRUE( + IsFieldEqual(this->input.ynext(2), expected_up_2, "RGN_YUP2", FFTTolerance)); + EXPECT_TRUE( + IsFieldEqual(this->input.ynext(-1), expected_down_1, "RGN_YDOWN", FFTTolerance)); + EXPECT_TRUE( + IsFieldEqual(this->input.ynext(-2), expected_down_2, "RGN_YDOWN2", FFTTolerance)); } #endif From ae9bb9caead818448456ad117da4b839fd7f2cd4 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Fri, 30 Jan 2026 18:20:31 +0000 Subject: [PATCH 195/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 5e90d36c14..a8bd688f08 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -3,3 +3,4 @@ d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 # Added by the bot 4b010b7634aee1045743be80c268d4644522cd29 +a71cad2dd6ace5741a754e2ca7daacd4bb094e0e From e5f2d373ff594776d6f878f598647fc5905551ba Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 11:49:51 +0100 Subject: [PATCH 196/461] do not TRACK names --- src/field/gen_fieldops.jinja | 7 ------- src/field/gen_fieldops.py | 15 ++++++--------- 2 files changed, 6 insertions(+), 16 deletions(-) diff --git a/src/field/gen_fieldops.jinja b/src/field/gen_fieldops.jinja index 58b1ae28ba..5476242d50 100644 --- a/src/field/gen_fieldops.jinja +++ b/src/field/gen_fieldops.jinja @@ -61,10 +61,6 @@ } {% endif %} -#if BOUT_USE_TRACK - {{out.name}}.name = fmt::format("{:s} {{operator}} {:s}", {{'"BR"' if lhs == "BoutReal" else lhs.name + ".name"}} - , {{'"BR"' if rhs == "BoutReal" else rhs.name + ".name"}}); -#endif checkData({{out.name}}); return {{out.name}}; } @@ -136,9 +132,6 @@ {% if lhs == "Field3D" %} track(rhs, "operator{{operator}}="); {% endif %} -#if BOUT_USE_TRACK - name = fmt::format("{:s} {{operator}}= {:s}", this->name, {{'"BR"' if rhs == "BoutReal" else rhs.name + ".name"}}); -#endif checkData(*this); diff --git a/src/field/gen_fieldops.py b/src/field/gen_fieldops.py index 30c8101e97..3e07d6fec4 100755 --- a/src/field/gen_fieldops.py +++ b/src/field/gen_fieldops.py @@ -64,15 +64,12 @@ def smart_open(filename, mode="r"): ) header = """// This file is autogenerated - see gen_fieldops.py -#include "bout/build_defines.hxx" -#include "bout/field2d.hxx" -#include "bout/field3d.hxx" -#include "bout/globals.hxx" -#include "bout/interpolation.hxx" -#include "bout/mesh.hxx" -#include "bout/region.hxx" - -#include "fmt/format.h" +#include +#include +#include +#include +#include +#include """ From 751abc463ec6f9dd53eb3cc334b15cbf16ba7bc1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 11:50:05 +0100 Subject: [PATCH 197/461] Remove setName again --- include/bout/field.hxx | 16 ---------------- src/mesh/coordinates.cxx | 4 ++-- src/solver/impls/euler/euler.cxx | 1 - src/solver/impls/pvode/pvode.cxx | 1 - 4 files changed, 2 insertions(+), 20 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index c188a0d8eb..4e41aa3632 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -35,7 +35,6 @@ class Field; #include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" -#include "bout/build_defines.hxx" #include "bout/field_data.hxx" #include "bout/region.hxx" #include "bout/traits.hxx" @@ -668,19 +667,4 @@ inline T floor(const T& var, BoutReal f, const std::string& rgn = "RGN_ALL") { #undef FIELD_FUNC -template , class... Types> -inline void setName(T& f, const std::string& name, Types... args) { -#if BOUT_USE_TRACK - f.name = fmt::format(name, args...); -#endif -} - -template , class... Types> -inline T setName(T&& f, const std::string& name, Types... args) { -#if BOUT_USE_TRACK - f.name = fmt::format(name, args...); -#endif - return std::forward(f); -} - #endif /* FIELD_H */ diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index ade7ac158a..b8b0f9ea55 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1531,7 +1531,7 @@ Field3D Coordinates::Grad_par(const Field3D& var, CELL_LOC outloc, ASSERT1(location == outloc || outloc == CELL_DEFAULT); - return setName(::DDY(var, outloc, method) * invSg(), "Grad_par({:s})", var.name); + return ::DDY(var, outloc, method) * invSg(); } ///////////////////////////////////////////////////////// @@ -1590,7 +1590,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, f_B.yup(i) = f.yup(i) / Bxy_floc.yup(i); f_B.ydown(i) = f.ydown(i) / Bxy_floc.ydown(i); } - return setName(Bxy * Grad_par(f_B, outloc, method), "C:Div_par({:s})", f.name); + return xy * Grad_par(f_B, outloc, method); } ///////////////////////////////////////////////////////// diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 78721d2e8f..6bdceab516 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -150,7 +150,6 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star Options& debug = *debug_ptr; for (auto& f : f3d) { f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug_ptr); - setName(*f.var, f.name); debug[fmt::format("pre_{:s}", f.name)] = *f.var; f.var->allocate(); } diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 59f7b3867f..4cc90af8fd 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -393,7 +393,6 @@ BoutReal PvodeSolver::run(BoutReal tout) { for (auto& f : f3d) { f.F_var->enableTracking(fmt::format("ddt_{:s}", f.name), debug_ptr); - setName(*f.var, f.name); } run_rhs(simtime); From 3cc061bb7181cd2cd42f74f39fe864c08d4f3a90 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 11:51:42 +0100 Subject: [PATCH 198/461] Update generated source code --- src/field/generated_fieldops.cxx | 327 +------------------------------ 1 file changed, 6 insertions(+), 321 deletions(-) diff --git a/src/field/generated_fieldops.cxx b/src/field/generated_fieldops.cxx index f37a870c74..24bfde425c 100644 --- a/src/field/generated_fieldops.cxx +++ b/src/field/generated_fieldops.cxx @@ -1,13 +1,10 @@ // This file is autogenerated - see gen_fieldops.py -#include "bout/build_defines.hxx" -#include "bout/field2d.hxx" -#include "bout/field3d.hxx" -#include "bout/globals.hxx" -#include "bout/interpolation.hxx" -#include "bout/mesh.hxx" -#include "bout/region.hxx" - -#include "fmt/format.h" +#include +#include +#include +#include +#include +#include // Provide the C++ wrapper for multiplication of Field3D and Field3D Field3D operator*(const Field3D& lhs, const Field3D& rhs) { @@ -23,9 +20,6 @@ Field3D operator*(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -49,9 +43,6 @@ Field3D& Field3D::operator*=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } track(rhs, "operator*="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -76,9 +67,6 @@ Field3D operator/(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -102,9 +90,6 @@ Field3D& Field3D::operator/=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } track(rhs, "operator/="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -129,9 +114,6 @@ Field3D operator+(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -155,9 +137,6 @@ Field3D& Field3D::operator+=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } track(rhs, "operator+="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -182,9 +161,6 @@ Field3D operator-(const Field3D& lhs, const Field3D& rhs) { result[index] = lhs[index] - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -208,9 +184,6 @@ Field3D& Field3D::operator-=(const Field3D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } track(rhs, "operator-="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -240,9 +213,6 @@ Field3D operator*(const Field3D& lhs, const Field2D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -269,9 +239,6 @@ Field3D& Field3D::operator*=(const Field2D& rhs) { } track(rhs, "operator*="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -302,9 +269,6 @@ Field3D operator/(const Field3D& lhs, const Field2D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -332,9 +296,6 @@ Field3D& Field3D::operator/=(const Field2D& rhs) { } track(rhs, "operator/="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -364,9 +325,6 @@ Field3D operator+(const Field3D& lhs, const Field2D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -393,9 +351,6 @@ Field3D& Field3D::operator+=(const Field2D& rhs) { } track(rhs, "operator+="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -425,9 +380,6 @@ Field3D operator-(const Field3D& lhs, const Field2D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -454,9 +406,6 @@ Field3D& Field3D::operator-=(const Field2D& rhs) { } track(rhs, "operator-="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, rhs.name); -#endif checkData(*this); @@ -483,9 +432,6 @@ FieldPerp operator*(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -506,9 +452,6 @@ FieldPerp operator/(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -529,9 +472,6 @@ FieldPerp operator+(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -552,9 +492,6 @@ FieldPerp operator-(const Field3D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -572,9 +509,6 @@ Field3D operator*(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] * rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -595,9 +529,6 @@ Field3D& Field3D::operator*=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } track(rhs, "operator*="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, "BR"); -#endif checkData(*this); @@ -622,9 +553,6 @@ Field3D operator/(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] * tmp; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -646,9 +574,6 @@ Field3D& Field3D::operator/=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } track(rhs, "operator/="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, "BR"); -#endif checkData(*this); @@ -672,9 +597,6 @@ Field3D operator+(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] + rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -695,9 +617,6 @@ Field3D& Field3D::operator+=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } track(rhs, "operator+="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, "BR"); -#endif checkData(*this); @@ -721,9 +640,6 @@ Field3D operator-(const Field3D& lhs, const BoutReal rhs) { result[index] = lhs[index] - rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -744,9 +660,6 @@ Field3D& Field3D::operator-=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } track(rhs, "operator-="); -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, "BR"); -#endif checkData(*this); @@ -776,9 +689,6 @@ Field3D operator*(const Field2D& lhs, const Field3D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -802,9 +712,6 @@ Field3D operator/(const Field2D& lhs, const Field3D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -828,9 +735,6 @@ Field3D operator+(const Field2D& lhs, const Field3D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -854,9 +758,6 @@ Field3D operator-(const Field2D& lhs, const Field3D& rhs) { } } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -873,9 +774,6 @@ Field2D operator*(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -892,10 +790,6 @@ Field2D& Field2D::operator*=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -916,9 +810,6 @@ Field2D operator/(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -935,10 +826,6 @@ Field2D& Field2D::operator/=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -959,9 +846,6 @@ Field2D operator+(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -978,10 +862,6 @@ Field2D& Field2D::operator+=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1002,9 +882,6 @@ Field2D operator-(const Field2D& lhs, const Field2D& rhs) { result[index] = lhs[index] - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1021,10 +898,6 @@ Field2D& Field2D::operator-=(const Field2D& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1049,9 +922,6 @@ FieldPerp operator*(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1072,9 +942,6 @@ FieldPerp operator/(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1095,9 +962,6 @@ FieldPerp operator+(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1118,9 +982,6 @@ FieldPerp operator-(const Field2D& lhs, const FieldPerp& rhs) { result[index] = lhs[base_ind] - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1136,9 +997,6 @@ Field2D operator*(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] * rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1154,10 +1012,6 @@ Field2D& Field2D::operator*=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -1178,9 +1032,6 @@ Field2D operator/(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] * tmp; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1197,10 +1048,6 @@ Field2D& Field2D::operator/=(const BoutReal rhs) { const auto tmp = 1.0 / rhs; BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= tmp; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -1220,9 +1067,6 @@ Field2D operator+(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] + rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1238,10 +1082,6 @@ Field2D& Field2D::operator+=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -1261,9 +1101,6 @@ Field2D operator-(const Field2D& lhs, const BoutReal rhs) { result[index] = lhs[index] - rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1279,10 +1116,6 @@ Field2D& Field2D::operator-=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -1307,9 +1140,6 @@ FieldPerp operator*(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] * rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1332,10 +1162,6 @@ FieldPerp& FieldPerp::operator*=(const Field3D& rhs) { (*this)[index] *= rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1360,9 +1186,6 @@ FieldPerp operator/(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] / rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1385,10 +1208,6 @@ FieldPerp& FieldPerp::operator/=(const Field3D& rhs) { (*this)[index] /= rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1413,9 +1232,6 @@ FieldPerp operator+(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] + rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1438,10 +1254,6 @@ FieldPerp& FieldPerp::operator+=(const Field3D& rhs) { (*this)[index] += rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1466,9 +1278,6 @@ FieldPerp operator-(const FieldPerp& lhs, const Field3D& rhs) { result[index] = lhs[index] - rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1491,10 +1300,6 @@ FieldPerp& FieldPerp::operator-=(const Field3D& rhs) { (*this)[index] -= rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1519,9 +1324,6 @@ FieldPerp operator*(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] * rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1544,10 +1346,6 @@ FieldPerp& FieldPerp::operator*=(const Field2D& rhs) { (*this)[index] *= rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1572,9 +1370,6 @@ FieldPerp operator/(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] / rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1597,10 +1392,6 @@ FieldPerp& FieldPerp::operator/=(const Field2D& rhs) { (*this)[index] /= rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1625,9 +1416,6 @@ FieldPerp operator+(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] + rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1650,10 +1438,6 @@ FieldPerp& FieldPerp::operator+=(const Field2D& rhs) { (*this)[index] += rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1678,9 +1462,6 @@ FieldPerp operator-(const FieldPerp& lhs, const Field2D& rhs) { result[index] = lhs[index] - rhs[base_ind]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1703,10 +1484,6 @@ FieldPerp& FieldPerp::operator-=(const Field2D& rhs) { (*this)[index] -= rhs[base_ind]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1727,9 +1504,6 @@ FieldPerp operator*(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1746,10 +1520,6 @@ FieldPerp& FieldPerp::operator*=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1770,9 +1540,6 @@ FieldPerp operator/(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1789,10 +1556,6 @@ FieldPerp& FieldPerp::operator/=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1813,9 +1576,6 @@ FieldPerp operator+(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1832,10 +1592,6 @@ FieldPerp& FieldPerp::operator+=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1856,9 +1612,6 @@ FieldPerp operator-(const FieldPerp& lhs, const FieldPerp& rhs) { result[index] = lhs[index] - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, rhs.name); -#endif checkData(result); return result; } @@ -1875,10 +1628,6 @@ FieldPerp& FieldPerp::operator-=(const FieldPerp& rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs[index]; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, rhs.name); -#endif - checkData(*this); } else { @@ -1898,9 +1647,6 @@ FieldPerp operator*(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] * rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1916,10 +1662,6 @@ FieldPerp& FieldPerp::operator*=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] *= rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} *= {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -1940,9 +1682,6 @@ FieldPerp operator/(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] * tmp; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1958,10 +1697,6 @@ FieldPerp& FieldPerp::operator/=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] /= rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} /= {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -1981,9 +1716,6 @@ FieldPerp operator+(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] + rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -1999,10 +1731,6 @@ FieldPerp& FieldPerp::operator+=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] += rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} += {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -2022,9 +1750,6 @@ FieldPerp operator-(const FieldPerp& lhs, const BoutReal rhs) { result[index] = lhs[index] - rhs; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", lhs.name, "BR"); -#endif checkData(result); return result; } @@ -2040,10 +1765,6 @@ FieldPerp& FieldPerp::operator-=(const BoutReal rhs) { BOUT_FOR(index, this->getRegion("RGN_ALL")) { (*this)[index] -= rhs; } -#if BOUT_USE_TRACK - name = fmt::format("{:s} -= {:s}", this->name, "BR"); -#endif - checkData(*this); } else { @@ -2065,9 +1786,6 @@ Field3D operator*(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2085,9 +1803,6 @@ Field3D operator/(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2105,9 +1820,6 @@ Field3D operator+(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2125,9 +1837,6 @@ Field3D operator-(const BoutReal lhs, const Field3D& rhs) { result[index] = lhs - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2143,9 +1852,6 @@ Field2D operator*(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2161,9 +1867,6 @@ Field2D operator/(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2179,9 +1882,6 @@ Field2D operator+(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2197,9 +1897,6 @@ Field2D operator-(const BoutReal lhs, const Field2D& rhs) { result[index] = lhs - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2215,9 +1912,6 @@ FieldPerp operator*(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs * rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} * {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2233,9 +1927,6 @@ FieldPerp operator/(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs / rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} / {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2251,9 +1942,6 @@ FieldPerp operator+(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs + rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} + {:s}", "BR", rhs.name); -#endif checkData(result); return result; } @@ -2269,9 +1957,6 @@ FieldPerp operator-(const BoutReal lhs, const FieldPerp& rhs) { result[index] = lhs - rhs[index]; } -#if BOUT_USE_TRACK - result.name = fmt::format("{:s} - {:s}", "BR", rhs.name); -#endif checkData(result); return result; } From 52301380586fdbf890f620c04f689b08d89a6c34 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Mon, 2 Feb 2026 10:52:25 +0000 Subject: [PATCH 199/461] [bot] Apply format changes --- examples/blob2d/blob_velocity.py | 154 ++++++------ examples/boutpp/simulation.py | 5 +- examples/conduction-snb/fit_temperature.py | 24 +- examples/conduction-snb/sinusoid.py | 30 ++- examples/conduction-snb/step.py | 31 ++- examples/conduction/generate.py | 4 +- examples/eigen-box/eigenvals.py | 35 +-- examples/elm-pb/Python/2dprofile.py | 91 ++++--- examples/elm-pb/Python/analysis.py | 16 +- examples/elm-pb/Python/elm_size.py | 179 +++++++------ examples/elm-pb/Python/fftall.py | 9 +- examples/elm-pb/Python/fftall2.py | 10 +- examples/elm-pb/Python/grate.py | 9 +- examples/elm-pb/Python/grate2.py | 45 ++-- examples/elm-pb/Python/plotcollapse.py | 47 ++-- examples/elm-pb/Python/plotmode.py | 93 ++++--- examples/elm-pb/Python/plotmode2.py | 94 ++++--- examples/elm-pb/Python/plotphase.py | 45 ++-- examples/elm-pb/Python/polslice.py | 26 +- examples/elm-pb/Python/post.py | 142 +++++++---- examples/elm-pb/Python/read_elmsize.py | 46 ++-- examples/elm-pb/Python/showpolslice.py | 36 +-- examples/elm-pb/Python/sprofiles.py | 41 +-- examples/elm-pb/runexample.py | 28 ++- examples/fci-wave/compare-density.py | 45 ++-- examples/finite-volume/diffusion/mms.py | 14 +- examples/finite-volume/fluid/mms.py | 20 +- examples/laplace-petsc3d/plotcheck.py | 16 +- examples/orszag-tang/generate.py | 116 +++++---- .../performance/bracket/scaling_parser.py | 5 +- .../performance/ddx/new_scaling_parser.py | 32 +-- .../performance/ddy/new_scaling_parser.py | 32 +-- .../performance/ddz/new_scaling_parser.py | 32 +-- .../performance/iterator/scaling_parser.py | 5 +- examples/staggered_grid/generate.py | 6 +- examples/subsampling/show.py | 10 +- examples/wave-slab/generate.py | 2 +- .../figure_creators/LaplacianMatrices.ipynb | 236 ++++++++++-------- .../bout_runners_folder_structure.py | 97 +++---- tests/MMS/advection/runtest | 2 +- 40 files changed, 1039 insertions(+), 871 deletions(-) diff --git a/examples/blob2d/blob_velocity.py b/examples/blob2d/blob_velocity.py index d044946320..6de0937d0f 100644 --- a/examples/blob2d/blob_velocity.py +++ b/examples/blob2d/blob_velocity.py @@ -3,96 +3,100 @@ import pickle try: - from past.utils import old_div + from past.utils import old_div except ImportError: - def old_div(a,b): - return a/b - -def blob_velocity(n,**kwargs): - - from boututils import calculus as Calc - # Calculate blob velocity in normalized time and normalized grid spacing - # - # Input: Blob density as a 3D vector in the form n[t,x,z] where t is time and x,z are the perpendicular spatial coordinates - # - # Keywords: - # - # type='peak' -> Calculate velocity of the peak density - # type='COM' -> Calculate centre of mass velocity - # Index=True -> return indices used to create velocity - # - # Default: Peak velocity with no index returning - - size = n.shape - - try: - v_type = kwargs['type'] - except: - v_type = 'peak' #Default to peak velocity calculation - try: - return_index = kwargs['Index'] - except: - return_index = False #Default to no index returning - - - if v_type == 'peak': - x = np.zeros(size[0]) - z = np.zeros(size[0]) - for i in np.arange(size[0]): - nmax,nmin = np.amax((n[i,:,:])),np.amin((n[i,:,:])) - xpos,zpos = np.where(n[i,:,:]==nmax) - x[i] = xpos[0] - z[i] = zpos[0] - - if v_type == 'COM': - x = np.zeros(size[0]) - z = np.zeros(size[0]) - for i in np.arange(size[0]): - data = n[i,:,:] - n[0,0,0] #use corner cell rather than nmin - ntot = np.sum(data[:,:]) - - z[i] = old_div(np.sum(np.sum(data[:,:],axis=0)*(np.arange(size[2]))),ntot) - x[i] = old_div(np.sum(np.sum(data[:,:],axis=1)*(np.arange(size[1]))),ntot) - - vx = Calc.deriv(x) - vz = Calc.deriv(z) - - if return_index: - return vx,vz,x,z - else: - return vx,vz - - - -data='data' + def old_div(a, b): + return a / b + + +def blob_velocity(n, **kwargs): + from boututils import calculus as Calc + # Calculate blob velocity in normalized time and normalized grid spacing + # + # Input: Blob density as a 3D vector in the form n[t,x,z] where t is time and x,z are the perpendicular spatial coordinates + # + # Keywords: + # + # type='peak' -> Calculate velocity of the peak density + # type='COM' -> Calculate centre of mass velocity + # Index=True -> return indices used to create velocity + # + # Default: Peak velocity with no index returning + + size = n.shape + + try: + v_type = kwargs["type"] + except: + v_type = "peak" # Default to peak velocity calculation + try: + return_index = kwargs["Index"] + except: + return_index = False # Default to no index returning + + if v_type == "peak": + x = np.zeros(size[0]) + z = np.zeros(size[0]) + for i in np.arange(size[0]): + nmax, nmin = np.amax((n[i, :, :])), np.amin((n[i, :, :])) + xpos, zpos = np.where(n[i, :, :] == nmax) + x[i] = xpos[0] + z[i] = zpos[0] + + if v_type == "COM": + x = np.zeros(size[0]) + z = np.zeros(size[0]) + for i in np.arange(size[0]): + data = n[i, :, :] - n[0, 0, 0] # use corner cell rather than nmin + ntot = np.sum(data[:, :]) + + z[i] = old_div( + np.sum(np.sum(data[:, :], axis=0) * (np.arange(size[2]))), ntot + ) + x[i] = old_div( + np.sum(np.sum(data[:, :], axis=1) * (np.arange(size[1]))), ntot + ) + + vx = Calc.deriv(x) + vz = Calc.deriv(z) + + if return_index: + return vx, vz, x, z + else: + return vx, vz + + +data = "data" if True: - import sys - if len(sys.argv) > 1: - data=sys.argv[1] + import sys + + if len(sys.argv) > 1: + data = sys.argv[1] -n = collect('n', path=data, info=False) +n = collect("n", path=data, info=False) -vx,vy,xx,yy = blob_velocity(n[:,:,0,:],type='COM',Index=True) +vx, vy, xx, yy = blob_velocity(n[:, :, 0, :], type="COM", Index=True) -f = open('Velocity.dat','wb') -pickle.dump(vx,f) +f = open("Velocity.dat", "wb") +pickle.dump(vx, f) f.close() -f = open('Position.dat','wb') -pickle.dump(xx,f) +f = open("Position.dat", "wb") +pickle.dump(xx, f) f.close() -f = open('Velocity.dat','rb') +f = open("Velocity.dat", "rb") vx = pickle.load(f) f.close() try: - import matplotlib.pyplot as plt - plt.plot(vx) - plt.show() + import matplotlib.pyplot as plt + + plt.plot(vx) + plt.show() except ImportError: - pass + pass diff --git a/examples/boutpp/simulation.py b/examples/boutpp/simulation.py index 1b57ff8b33..9948bd9b03 100755 --- a/examples/boutpp/simulation.py +++ b/examples/boutpp/simulation.py @@ -3,12 +3,13 @@ bc.init("mesh:n=48") + class Model(bc.PhysicsModel): - def init(self,restart): + def init(self, restart): self.dens = bc.create3D("sin(x)") self.solve_for(n=self.dens) - def rhs(self,time): + def rhs(self, time): self.dens.ddt(bc.DDX(self.dens)) diff --git a/examples/conduction-snb/fit_temperature.py b/examples/conduction-snb/fit_temperature.py index 1e7f3ddf94..81faa9cf93 100644 --- a/examples/conduction-snb/fit_temperature.py +++ b/examples/conduction-snb/fit_temperature.py @@ -3,23 +3,31 @@ import matplotlib.pyplot as plt Te_ref = np.loadtxt("temperature.csv", delimiter=",") -Te_ref[:,0] *= 1e-4 # Convert X axis to m +Te_ref[:, 0] *= 1e-4 # Convert X axis to m + def te_function(ypos, mid, wwid, w0, w1, w2, Tmax, Tmin, clip=False): - width = w0 + ((ypos - mid)*w1 + (ypos - mid)**2 * w2) * np.exp(-((ypos - mid)/wwid)**2) + width = w0 + ((ypos - mid) * w1 + (ypos - mid) ** 2 * w2) * np.exp( + -(((ypos - mid) / wwid) ** 2) + ) if clip: width = np.clip(width, 1e-10, None) - return Tmax - 0.5 * (1 + np.tanh((ypos - mid)/width)) * (Tmax - Tmin) + return Tmax - 0.5 * (1 + np.tanh((ypos - mid) / width)) * (Tmax - Tmin) + -popt, pcov = optimize.curve_fit(te_function, Te_ref[:,0], Te_ref[:,1], - p0 = [2.2e-4, 1e-4, 1e-4, 0.0, 0.0, 0.960, 0.190]) +popt, pcov = optimize.curve_fit( + te_function, + Te_ref[:, 0], + Te_ref[:, 1], + p0=[2.2e-4, 1e-4, 1e-4, 0.0, 0.0, 0.960, 0.190], +) print(popt) -xfit = np.linspace(Te_ref[0,0], Te_ref[-1,0], 100) +xfit = np.linspace(Te_ref[0, 0], Te_ref[-1, 0], 100) -plt.plot(xfit, te_function(xfit, *popt, clip=True), '-k') -plt.plot(Te_ref[:,0], Te_ref[:,1], 'or') +plt.plot(xfit, te_function(xfit, *popt, clip=True), "-k") +plt.plot(Te_ref[:, 0], Te_ref[:, 1], "or") plt.show() diff --git a/examples/conduction-snb/sinusoid.py b/examples/conduction-snb/sinusoid.py index 10c81923a3..439530383e 100644 --- a/examples/conduction-snb/sinusoid.py +++ b/examples/conduction-snb/sinusoid.py @@ -11,7 +11,7 @@ build_and_log("Sinusoidal SNB") # Electron temperature in eV -Telist = 10 ** np.linspace(0,3,20) +Telist = 10 ** np.linspace(0, 3, 20) # Electron density in m^-3 Ne = 1e20 @@ -19,29 +19,30 @@ # Length of the domain in m length = 1.0 -c = 299792458 -mu0 = 4.e-7*np.pi -e0 = 1/(c*c*mu0) +c = 299792458 +mu0 = 4.0e-7 * np.pi +e0 = 1 / (c * c * mu0) qe = 1.602176634e-19 me = 9.10938356e-31 -thermal_speed = np.sqrt(2.*qe * Telist / me) -Y = (qe**2 / (e0 * me))**2 / (4 * np.pi) +thermal_speed = np.sqrt(2.0 * qe * Telist / me) +Y = (qe**2 / (e0 * me)) ** 2 / (4 * np.pi) coulomb_log = 6.6 - 0.5 * np.log(Ne * 1e-20) + 1.5 * np.log(Telist) -lambda_ee_T = thermal_speed**4 / (Y * Ne * coulomb_log) +lambda_ee_T = thermal_speed**4 / (Y * Ne * coulomb_log) beta_max_list = [5, 10, 20, 40] -colors = ['k','b','g','r'] +colors = ["k", "b", "g", "r"] ngroups_list = [20, 40, 80] -syms = ['x', 'o', 'D'] +syms = ["x", "o", "D"] for beta_max, color in zip(beta_max_list, colors): for ngroups, sym in zip(ngroups_list, syms): - flux_ratio = [] for Te in Telist: - cmd = "./conduction-snb \"Te={0}+0.01*sin(y)\" Ne={1} mesh:length={2} snb:beta_max={3} snb:ngroups={4}".format(Te, Ne, length, beta_max, ngroups) + cmd = './conduction-snb "Te={0}+0.01*sin(y)" Ne={1} mesh:length={2} snb:beta_max={3} snb:ngroups={4}'.format( + Te, Ne, length, beta_max, ngroups + ) # Run the case s, out = launch_safe(cmd, nproc=1, mthread=1, pipe=True) @@ -54,7 +55,12 @@ flux_ratio.append(div_q[ind] / div_q_SH[ind]) - plt.plot(lambda_ee_T / length, flux_ratio, '-'+sym+color, label=r"$\beta_{{max}}={0}, N_g={1}$".format(beta_max,ngroups)) + plt.plot( + lambda_ee_T / length, + flux_ratio, + "-" + sym + color, + label=r"$\beta_{{max}}={0}, N_g={1}$".format(beta_max, ngroups), + ) plt.legend() plt.xlabel(r"$\lambda_{ee,T} / L$") diff --git a/examples/conduction-snb/step.py b/examples/conduction-snb/step.py index 1f8933e66b..63a13149f4 100644 --- a/examples/conduction-snb/step.py +++ b/examples/conduction-snb/step.py @@ -3,7 +3,7 @@ # # Uses a step in the temperature, intended for comparison to VFP results -length = 6e-4 # Domain length in m +length = 6e-4 # Domain length in m qe = 1.602176634e-19 @@ -40,37 +40,37 @@ # Read reference values Te_ref = np.loadtxt("temperature.csv", delimiter=",") -Te_ref[:,0] *= 1e-4 # Convert X axis to m +Te_ref[:, 0] *= 1e-4 # Convert X axis to m SH_ref = np.loadtxt("spitzer-harm.csv", delimiter=",") -SH_ref[:,0] *= 1e-4 +SH_ref[:, 0] *= 1e-4 SNB_ref = np.loadtxt("snb.csv", delimiter=",") -SNB_ref[:,0] *= 1e-4 +SNB_ref[:, 0] *= 1e-4 VFP_ref = np.loadtxt("vfp.csv", delimiter=",") -VFP_ref[:,0] *= 1e-4 +VFP_ref[:, 0] *= 1e-4 ######################################### fig, ax1 = plt.subplots() -color='tab:red' +color = "tab:red" ax1.plot(position, Te * 1e-3, color=color, label="Te") -ax1.plot(Te_ref[:,0], Te_ref[:,1], color=color, marker="o", label="Reference Te") +ax1.plot(Te_ref[:, 0], Te_ref[:, 1], color=color, marker="o", label="Reference Te") ax1.set_xlabel("position [m]") ax1.set_ylabel("Electron temperature [keV]", color=color) -ax1.set_ylim(0,1) -ax1.tick_params(axis='y', colors=color) +ax1.set_ylim(0, 1) +ax1.tick_params(axis="y", colors=color) ax2 = ax1.twinx() -ax2.plot(position, q_SH * 1e-4, '-k', label="Spitzer-Harm") -ax2.plot(SH_ref[:,0], SH_ref[:,1], '--k', label="Reference SH") +ax2.plot(position, q_SH * 1e-4, "-k", label="Spitzer-Harm") +ax2.plot(SH_ref[:, 0], SH_ref[:, 1], "--k", label="Reference SH") -ax2.plot(position, q * 1e-4, '-b', label="SNB") -ax2.plot(SNB_ref[:,0], SNB_ref[:,1], '--b', label="Reference SNB") +ax2.plot(position, q * 1e-4, "-b", label="SNB") +ax2.plot(SNB_ref[:, 0], SNB_ref[:, 1], "--b", label="Reference SNB") -ax2.plot(VFP_ref[:,0], VFP_ref[:,1], '--g', label="Reference VFP") +ax2.plot(VFP_ref[:, 0], VFP_ref[:, 1], "--g", label="Reference VFP") ax2.set_ylabel("Heat flux W/cm^2") ax2.set_ylim(bottom=0.0) @@ -78,8 +78,7 @@ plt.legend() fig.tight_layout() -plt.xlim(0,3.5e-4) +plt.xlim(0, 3.5e-4) plt.savefig("snb-step.png") plt.show() - diff --git a/examples/conduction/generate.py b/examples/conduction/generate.py index 7138026898..4a2a2acfc0 100755 --- a/examples/conduction/generate.py +++ b/examples/conduction/generate.py @@ -4,9 +4,9 @@ # Generate an input mesh # -from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries +from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries -nx = 5 # Minimum is 5: 2 boundary, one evolved +nx = 5 # Minimum is 5: 2 boundary, one evolved ny = 64 # Minimum 5. Should be divisible by number of processors (so powers of 2 nice) f = DataFile() diff --git a/examples/eigen-box/eigenvals.py b/examples/eigen-box/eigenvals.py index 7b52fc60f2..5a71aabfc6 100755 --- a/examples/eigen-box/eigenvals.py +++ b/examples/eigen-box/eigenvals.py @@ -27,7 +27,8 @@ def plot_eigenvals(eigenvalues, eigenvectors=None): raise ValueError("Expecting eigenvectors to be 2D") if eigenvectors.shape[0] != len(eigenvalues): raise ValueError( - "First dimension of eigenvectors must match length of eigenvalues") + "First dimension of eigenvectors must match length of eigenvalues" + ) # If no eigenvectors supplied, only plot eigenvalues, otherwise # eigenvalues and eigenvectors @@ -44,18 +45,18 @@ def plot_eigenvals(eigenvalues, eigenvectors=None): range_r = amax(eigs_r) - amin(eigs_r) range_i = amax(eigs_i) - amin(eigs_i) - ax[0].plot(eigs_r, eigs_i, 'x') + ax[0].plot(eigs_r, eigs_i, "x") ax[0].set_xlabel("Real component") ax[0].set_ylabel("Imaginary component") ax[0].set_title("Eigenvalue") - overplot, = ax[0].plot([], [], 'ok') + (overplot,) = ax[0].plot([], [], "ok") if eigenvectors is not None: # Add a eigenvectors plot - vector_r, = ax[1].plot([], [], '-k', label="Real") - vector_i, = ax[1].plot([], [], '-r', label="Imag") - ax[1].legend(loc='upper right') + (vector_r,) = ax[1].plot([], [], "-k", label="Real") + (vector_i,) = ax[1].plot([], [], "-r", label="Imag") + ax[1].legend(loc="upper right") ax[1].set_xlabel("X") ax[1].set_ylabel("Amplitude") ax[1].set_title("Eigenvector") @@ -68,33 +69,33 @@ def onclick(event): # Find closest eigenvectors point, but stretch axes so # real and imaginary components are weighted equally - if(range_r == 0): - dist = ((eigs_i - event.ydata)/range_i)**2 - elif(range_i == 0): - dist = ((eigs_r - event.xdata)/range_r)**2 + if range_r == 0: + dist = ((eigs_i - event.ydata) / range_i) ** 2 + elif range_i == 0: + dist = ((eigs_r - event.xdata) / range_r) ** 2 else: - dist = ((eigs_r - event.xdata)/range_r)**2 + \ - ((eigs_i - event.ydata)/range_i)**2 + dist = ((eigs_r - event.xdata) / range_r) ** 2 + ( + (eigs_i - event.ydata) / range_i + ) ** 2 ind = argmin(dist) # Update the highlight plot overplot.set_data([eigs_r[ind]], [eigs_i[ind]]) - print("Eigenvalue number: %d (%e,%e)" % - (ind, eigs_r[ind], eigs_i[ind])) + print("Eigenvalue number: %d (%e,%e)" % (ind, eigs_r[ind], eigs_i[ind])) if eigenvectors is not None: # Update plots nx = eigenvectors.shape[1] - vector_r.set_data(arange(nx), eigenvectors[2*ind, :]) - vector_i.set_data(arange(nx), eigenvectors[2*ind+1, :]) + vector_r.set_data(arange(nx), eigenvectors[2 * ind, :]) + vector_i.set_data(arange(nx), eigenvectors[2 * ind + 1, :]) ax[1].relim() ax[1].autoscale_view() fig.canvas.draw() - fig.canvas.mpl_connect('button_press_event', onclick) + fig.canvas.mpl_connect("button_press_event", onclick) plt.show() diff --git a/examples/elm-pb/Python/2dprofile.py b/examples/elm-pb/Python/2dprofile.py index 5bb01972f9..e95709ab5b 100644 --- a/examples/elm-pb/Python/2dprofile.py +++ b/examples/elm-pb/Python/2dprofile.py @@ -4,81 +4,92 @@ import numpy as np import matplotlib.pyplot as plt from boututils.datafile import DataFile -from matplotlib.ticker import FixedFormatter, FormatStrFormatter, AutoLocator, AutoMinorLocator +from matplotlib.ticker import ( + FixedFormatter, + FormatStrFormatter, + AutoLocator, + AutoMinorLocator, +) with DataFile("./cbm18_dens8.grid_nx68ny64.nc") as f: g = {v: f.read(v) for v in f.keys()} -majorLocator = AutoLocator() -majorFormatter = FormatStrFormatter('%3.0e') -minorLocator = AutoMinorLocator() -Fm = FixedFormatter(['0','$1 \\times 10^4$','$2 \\times 10^4$','$3 \\times 10^4$','$4 \\times 10^4$']) -Fm2 = FixedFormatter(['0','$2 \\times 10^5$','$4 \\times 10^5$','$6 \\times 10^5$']) - -bxy=g.get('Bxy') -p=g.get('pressure') -jpar0=g.get('Jpar0') -psixy=g.get('psixy') -btxy=g.get('Btxy') -shiftangle=g.get('ShiftAngle') - -nx=g.get('nx') -ny=g.get('ny') +majorLocator = AutoLocator() +majorFormatter = FormatStrFormatter("%3.0e") +minorLocator = AutoMinorLocator() +Fm = FixedFormatter( + [ + "0", + "$1 \\times 10^4$", + "$2 \\times 10^4$", + "$3 \\times 10^4$", + "$4 \\times 10^4$", + ] +) +Fm2 = FixedFormatter(["0", "$2 \\times 10^5$", "$4 \\times 10^5$", "$6 \\times 10^5$"]) + +bxy = g.get("Bxy") +p = g.get("pressure") +jpar0 = g.get("Jpar0") +psixy = g.get("psixy") +btxy = g.get("Btxy") +shiftangle = g.get("ShiftAngle") + +nx = g.get("nx") +ny = g.get("ny") q = np.zeros((nx, ny)) for i in range(ny): - q[:,i] = old_div(- shiftangle, (2 * np.pi)) - - + q[:, i] = old_div(-shiftangle, (2 * np.pi)) -xarr = psixy[:,0] +xarr = psixy[:, 0] xarr = old_div((xarr + 0.854856), (0.854856 + 0.0760856)) -fig=plt.figure() -plt.plot(xarr,q[:,32]) -plt.xlabel('normalized $\psi$', fontsize=25) -plt.ylabel('$q$',rotation='horizontal',fontsize=25) +fig = plt.figure() +plt.plot(xarr, q[:, 32]) +plt.xlabel("normalized $\psi$", fontsize=25) +plt.ylabel("$q$", rotation="horizontal", fontsize=25) fig.set_tight_layout(True) fig, ax1 = plt.subplots() -ax1.plot(xarr, p[:,32], 'r-', markevery=1, linewidth=3) -ax1.set_xlabel('normalized $\psi$',fontsize=25) +ax1.plot(xarr, p[:, 32], "r-", markevery=1, linewidth=3) +ax1.set_xlabel("normalized $\psi$", fontsize=25) # Make the y-axis label and tick labels match the line color. -ax1.set_ylabel('Pressure [Pa]', color='k',fontsize=25) +ax1.set_ylabel("Pressure [Pa]", color="k", fontsize=25) -#set y limit -ax1.set_ylim(0,40000,10000) +# set y limit +ax1.set_ylim(0, 40000, 10000) -#define ticks# +# define ticks# ax1.yaxis.set_ticks(np.arange(0, 40000, 10000)) -#ax1.yaxis.set_major_locator(majorLocator) -#ax1.yaxis.set_major_formatter(majorFormatter) +# ax1.yaxis.set_major_locator(majorLocator) +# ax1.yaxis.set_major_formatter(majorFormatter) ax1.yaxis.set_major_formatter(Fm) -#for the minor ticks, use no labels; default NullFormatter +# for the minor ticks, use no labels; default NullFormatter ax1.xaxis.set_minor_locator(AutoMinorLocator()) ax1.yaxis.set_minor_locator(AutoMinorLocator(10)) -#format tick labels +# format tick labels for tl in ax1.get_yticklabels(): - tl.set_color('k') + tl.set_color("k") ax2 = ax1.twinx() s2 = -jpar0 -ax2.plot(xarr, s2[:,32], 'r-',markevery=1,linewidth=3) -ax2.set_ylabel('$J_\parallel [A/m^2]$', color='k',fontsize=25) -ax2.set_ylim(0,600000) +ax2.plot(xarr, s2[:, 32], "r-", markevery=1, linewidth=3) +ax2.set_ylabel("$J_\parallel [A/m^2]$", color="k", fontsize=25) +ax2.set_ylim(0, 600000) ax2.yaxis.set_ticks(np.arange(0, 600000, 200000)) ax2.yaxis.set_major_formatter(Fm2) for tl in ax2.get_yticklabels(): - tl.set_color('k') + tl.set_color("k") fig.set_tight_layout(True) @@ -86,4 +97,4 @@ plt.show() -#plt.savefig('2d.png', transparent=True) +# plt.savefig('2d.png', transparent=True) diff --git a/examples/elm-pb/Python/analysis.py b/examples/elm-pb/Python/analysis.py index 4faf85f8ce..0e4a3acfdd 100644 --- a/examples/elm-pb/Python/analysis.py +++ b/examples/elm-pb/Python/analysis.py @@ -6,24 +6,24 @@ import pylab as plt from boutdata.collect import collect -path='./data/' -var=collect('P', path=path) +path = "./data/" +var = collect("P", path=path) -dcvar=np.mean(var, axis=3) -rmsvar=np.sqrt(np.mean(var**2,axis=3)-dcvar**2) +dcvar = np.mean(var, axis=3) +rmsvar = np.sqrt(np.mean(var**2, axis=3) - dcvar**2) plt.figure() -plt.plot(rmsvar[:,34,32]) +plt.plot(rmsvar[:, 34, 32]) plt.show(block=False) -fvar=np.fft.rfft(var,axis=3) +fvar = np.fft.rfft(var, axis=3) plt.figure() -plt.plot(abs(fvar[:,34,32,1:10])) +plt.plot(abs(fvar[:, 34, 32, 1:10])) plt.show(block=False) plt.figure() -plt.semilogy(abs(fvar[:,34,32,1:7])) +plt.semilogy(abs(fvar[:, 34, 32, 1:7])) plt.show(block=False) plt.show() diff --git a/examples/elm-pb/Python/elm_size.py b/examples/elm-pb/Python/elm_size.py index 0d5c105870..6ad2ed6a0d 100644 --- a/examples/elm-pb/Python/elm_size.py +++ b/examples/elm-pb/Python/elm_size.py @@ -4,87 +4,126 @@ from past.utils import old_div import numpy as np -def elm_size(dcp,p0,uedge,xmin=None,xmax=None,yind=None,Bbar=None): - - lis=[dcp,p0,uedge] - if np.size(lis) != 3 : + +def elm_size(dcp, p0, uedge, xmin=None, xmax=None, yind=None, Bbar=None): + lis = [dcp, p0, uedge] + if np.size(lis) != 3: print("lack of parameters") return 0 - - if xmin == None : xmin=0 - if xmax == None : xmax=327 - if yind == None : yind=63 # choose the poloidal location for 1D size - if Bbar == None : Bbar=1.992782 # the normalized magnetic field + if xmin == None: + xmin = 0 + if xmax == None: + xmax = 327 + if yind == None: + yind = 63 # choose the poloidal location for 1D size + if Bbar == None: + Bbar = 1.992782 # the normalized magnetic field - mydcp=dcp - myp0=p0 - g=uedge + mydcp = dcp + myp0 = p0 + g = uedge PI = 3.1415926 - MU0 = 4.0e-7*PI + MU0 = 4.0e-7 * PI - s=np.shape(mydcp) + s = np.shape(mydcp) - if np.ndim(mydcp) != 3 : + if np.ndim(mydcp) != 3: print("dcp should be 3D(t,x,y)") - - - nt=s[0] - nx=s[1] - ny=s[2] - - Dtheta=g['dy'] #using correct poloidal angle - psixy=g['psixy'] - R=g['Rxy'] - Bp=g['Bpxy'] - hthe=g['hthe'] - - Dpsi=np.zeros((nx,ny)) - Dpsi[0,:]=psixy[1,:]-psixy[0,:] - Dpsi[nx-1,:]=psixy[nx-1,:]-psixy[nx-2,:] - for i in range(1,nx-2): - Dpsi[i,:]=old_div((psixy[i+1,:]-psixy[i-1,:]),2) - - - Ddcp1=np.zeros(nt) - Ddcp2=np.zeros(nt) - Ddcp3=np.zeros(nt) - Tp01=0. - Tp02=0. - Tp03=0. - - for t in range(nt) : - Ddcp3[t]=2.0*PI*np.sum(mydcp[t,xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/Bp[xmin:xmax,:]) - Ddcp2[t]=np.sum(mydcp[t,xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/(R[xmin:xmax,:]*Bp[xmin:xmax,:])) - Ddcp1[t]=np.sum(mydcp[t,xmin:xmax,yind]*Dpsi[xmin:xmax,yind]/(R[xmin:xmax,yind]*Bp[xmin:xmax,yind])) - - - Tp03=2.0*PI*np.sum(myp0[xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/Bp[xmin:xmax,:]) - Tp02=np.sum(myp0[xmin:xmax,:]*hthe[xmin:xmax,:]*Dtheta[xmin:xmax,:]*Dpsi[xmin:xmax,:]/(R[xmin:xmax,:]*Bp[xmin:xmax,:])) - Tp01=np.sum(myp0[xmin:xmax,yind]*Dpsi[xmin:xmax,yind]/(R[xmin:xmax,yind]*Bp[xmin:xmax,yind])) - - s1=np.zeros(nt) - s2=np.zeros(nt) - s3=np.zeros(nt) - E_loss=np.zeros(nt) - - s1=old_div(-Ddcp1,Tp01) #1D elm size - s2=old_div(-Ddcp2,Tp02) #2D elm size - s3=old_div(-Ddcp3,Tp03) #3D elm size - - E_loss=-Ddcp3*(0.5*Bbar*Bbar/MU0) #energy loss, unit J - E_total=Tp03*(0.5*Bbar*Bbar/MU0) #total energy, unit J + + nt = s[0] + nx = s[1] + ny = s[2] + + Dtheta = g["dy"] # using correct poloidal angle + psixy = g["psixy"] + R = g["Rxy"] + Bp = g["Bpxy"] + hthe = g["hthe"] + + Dpsi = np.zeros((nx, ny)) + Dpsi[0, :] = psixy[1, :] - psixy[0, :] + Dpsi[nx - 1, :] = psixy[nx - 1, :] - psixy[nx - 2, :] + for i in range(1, nx - 2): + Dpsi[i, :] = old_div((psixy[i + 1, :] - psixy[i - 1, :]), 2) + + Ddcp1 = np.zeros(nt) + Ddcp2 = np.zeros(nt) + Ddcp3 = np.zeros(nt) + Tp01 = 0.0 + Tp02 = 0.0 + Tp03 = 0.0 + + for t in range(nt): + Ddcp3[t] = ( + 2.0 + * PI + * np.sum( + mydcp[t, xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / Bp[xmin:xmax, :] + ) + ) + Ddcp2[t] = np.sum( + mydcp[t, xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / (R[xmin:xmax, :] * Bp[xmin:xmax, :]) + ) + Ddcp1[t] = np.sum( + mydcp[t, xmin:xmax, yind] + * Dpsi[xmin:xmax, yind] + / (R[xmin:xmax, yind] * Bp[xmin:xmax, yind]) + ) + + Tp03 = ( + 2.0 + * PI + * np.sum( + myp0[xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / Bp[xmin:xmax, :] + ) + ) + Tp02 = np.sum( + myp0[xmin:xmax, :] + * hthe[xmin:xmax, :] + * Dtheta[xmin:xmax, :] + * Dpsi[xmin:xmax, :] + / (R[xmin:xmax, :] * Bp[xmin:xmax, :]) + ) + Tp01 = np.sum( + myp0[xmin:xmax, yind] + * Dpsi[xmin:xmax, yind] + / (R[xmin:xmax, yind] * Bp[xmin:xmax, yind]) + ) + + s1 = np.zeros(nt) + s2 = np.zeros(nt) + s3 = np.zeros(nt) + E_loss = np.zeros(nt) + + s1 = old_div(-Ddcp1, Tp01) # 1D elm size + s2 = old_div(-Ddcp2, Tp02) # 2D elm size + s3 = old_div(-Ddcp3, Tp03) # 3D elm size + + E_loss = -Ddcp3 * (0.5 * Bbar * Bbar / MU0) # energy loss, unit J + E_total = Tp03 * (0.5 * Bbar * Bbar / MU0) # total energy, unit J class ELM: pass - elmsize=ELM() - elmsize.s1=s1 - elmsize.s2=s2 - elmsize.s3=s3 - elmsize.E_loss=E_loss - elmsize.E_total=E_total + + elmsize = ELM() + elmsize.s1 = s1 + elmsize.s2 = s2 + elmsize.s3 = s3 + elmsize.E_loss = E_loss + elmsize.E_total = E_total return elmsize - - diff --git a/examples/elm-pb/Python/fftall.py b/examples/elm-pb/Python/fftall.py index e0a4385764..c6d5b87ded 100644 --- a/examples/elm-pb/Python/fftall.py +++ b/examples/elm-pb/Python/fftall.py @@ -2,8 +2,7 @@ from numpy import * from scipy.io import readsav -print('Calculating P..') -a=transpose(readsav('phi.idl.dat')['phi']) -fa=fft.fft(a,axis=2) -save('fp',fa) - +print("Calculating P..") +a = transpose(readsav("phi.idl.dat")["phi"]) +fa = fft.fft(a, axis=2) +save("fp", fa) diff --git a/examples/elm-pb/Python/fftall2.py b/examples/elm-pb/Python/fftall2.py index d864f356d0..64b08c437a 100644 --- a/examples/elm-pb/Python/fftall2.py +++ b/examples/elm-pb/Python/fftall2.py @@ -2,9 +2,9 @@ from numpy import * from boutdata.collect import collect -path='./data/' -data=collect('P',path=path) +path = "./data/" +data = collect("P", path=path) -print('Saving P..') -fa=fft.fft(data,axis=3) -save('fp',rollaxis(fa,0,4)) +print("Saving P..") +fa = fft.fft(data, axis=3) +save("fp", rollaxis(fa, 0, 4)) diff --git a/examples/elm-pb/Python/grate.py b/examples/elm-pb/Python/grate.py index 003f38ff8e..65d6da6449 100644 --- a/examples/elm-pb/Python/grate.py +++ b/examples/elm-pb/Python/grate.py @@ -8,12 +8,11 @@ from boututils.moment_xyzt import moment_xyzt +path = "./data/" -path='./data/' +p = collect("P", path=path) +rmsp_f = moment_xyzt(p[:, 34:35, 32:33, :], "RMS").rms -p=collect('P',path=path) -rmsp_f=moment_xyzt(p[:,34:35,32:33,:], 'RMS').rms - -print(np.gradient(np.log(rmsp_f[:,0,0]))[-1]) +print(np.gradient(np.log(rmsp_f[:, 0, 0]))[-1]) diff --git a/examples/elm-pb/Python/grate2.py b/examples/elm-pb/Python/grate2.py index f8172bcf08..af0768803a 100644 --- a/examples/elm-pb/Python/grate2.py +++ b/examples/elm-pb/Python/grate2.py @@ -2,6 +2,7 @@ from __future__ import division from builtins import range from past.utils import old_div + ### # computes average growth rate for all points at the final timestep # computes average growth rate for points in the mead plane at the final timestep @@ -10,40 +11,44 @@ from boutdata.collect import collect from boututils.moment_xyzt import moment_xyzt -path='./data/' +path = "./data/" + +p = collect("P", path=path) -p=collect('P',path=path) +nmpy = old_div(p.shape[2], 2) # define mead plane -nmpy=old_div(p.shape[2],2) # define mead plane +ik = 50 # disregard the first ik timesteps -ik = 50 # disregard the first ik timesteps def gr(p): - rmsp_f=moment_xyzt(p, 'RMS').rms + rmsp_f = moment_xyzt(p, "RMS").rms - ni=np.shape(rmsp_f)[1] - nj=np.shape(rmsp_f)[2] + ni = np.shape(rmsp_f)[1] + nj = np.shape(rmsp_f)[2] - growth=np.zeros((ni,nj)) + growth = np.zeros((ni, nj)) - for i in range(ni): - for j in range(nj): - growth[i,j]=np.gradient(np.log(rmsp_f[ik::,i,j]))[-1] + for i in range(ni): + for j in range(nj): + growth[i, j] = np.gradient(np.log(rmsp_f[ik::, i, j]))[-1] - return growth + return growth -growth=gr(p) +growth = gr(p) -d=np.ma.masked_array(growth,np.isnan(growth)) +d = np.ma.masked_array(growth, np.isnan(growth)) # masked arrays # http://stackoverflow.com/questions/5480694/numpy-calculate-averages-with-nans-removed -print('Total mean value = ', np.mean(np.ma.masked_array(d,np.isinf(d)))) -mm=np.ma.masked_array(growth[:,nmpy],np.isnan(growth[:,nmpy])) -if np.isinf(np.mean(mm)) : - print('There is an Inf value in the mead plane') - print('Mean value of floating numbers in mead plane is = ', np.mean(np.ma.masked_array(mm,np.isinf(mm)))) +print("Total mean value = ", np.mean(np.ma.masked_array(d, np.isinf(d)))) +mm = np.ma.masked_array(growth[:, nmpy], np.isnan(growth[:, nmpy])) +if np.isinf(np.mean(mm)): + print("There is an Inf value in the mead plane") + print( + "Mean value of floating numbers in mead plane is = ", + np.mean(np.ma.masked_array(mm, np.isinf(mm))), + ) else: - print('Mean value in mead plane= ', np.mean(mm)) + print("Mean value in mead plane= ", np.mean(mm)) diff --git a/examples/elm-pb/Python/plotcollapse.py b/examples/elm-pb/Python/plotcollapse.py index ee64ca381f..f68e7b06aa 100755 --- a/examples/elm-pb/Python/plotcollapse.py +++ b/examples/elm-pb/Python/plotcollapse.py @@ -10,49 +10,52 @@ import os from pathlib import Path -#Dynamic matplotlib settings +# Dynamic matplotlib settings from matplotlib import rcParams -rcParams['font.size'] = 20. -rcParams['legend.fontsize'] = 'small' -rcParams['lines.linewidth'] = 2 -if not os.path.exists('image'): - os.makedirs('image') +rcParams["font.size"] = 20.0 +rcParams["legend.fontsize"] = "small" +rcParams["lines.linewidth"] = 2 + +if not os.path.exists("image"): + os.makedirs("image") filename = Path(__file__).with_name("cbm18_dens8.grid_nx68ny64.nc") with DataFile(str(filename)) as f: g = {v: f.read(v) for v in f.keys()} -psi = old_div((g['psixy'][:, 32] - g['psi_axis']), (g['psi_bndry'] - g['psi_axis'])) +psi = old_div((g["psixy"][:, 32] - g["psi_axis"]), (g["psi_bndry"] - g["psi_axis"])) -path = './data' +path = "./data" plt.figure() -p0=collect('P0', path=path) +p0 = collect("P0", path=path) -p=collect('P', path=path) -res = moment_xyzt(p,'RMS','DC') +p = collect("P", path=path) +res = moment_xyzt(p, "RMS", "DC") rmsp = res.rms dcp = res.dc nt = dcp.shape[0] -plt.plot(psi, p0[:, 32], 'k--', label='t=0') -plt.plot(psi, p0[:, 32] + dcp[nt//4, :, 32], 'r-', label='t='+np.str(nt//4)) -plt.plot(psi, p0[:, 32] + dcp[nt//2, :, 32], 'g-', label='t='+np.str(nt//2)) -plt.plot(psi, p0[:, 32] + dcp[3*nt//4, :, 32], 'b-', label='t='+np.str(3*nt//4)) -plt.plot(psi, p0[:, 32] + dcp[-1, :, 32], 'c-', label='t='+np.str(nt)) +plt.plot(psi, p0[:, 32], "k--", label="t=0") +plt.plot(psi, p0[:, 32] + dcp[nt // 4, :, 32], "r-", label="t=" + np.str(nt // 4)) +plt.plot(psi, p0[:, 32] + dcp[nt // 2, :, 32], "g-", label="t=" + np.str(nt // 2)) +plt.plot( + psi, p0[:, 32] + dcp[3 * nt // 4, :, 32], "b-", label="t=" + np.str(3 * nt // 4) +) +plt.plot(psi, p0[:, 32] + dcp[-1, :, 32], "c-", label="t=" + np.str(nt)) plt.legend() -#plt.xlim(0.6, 1.0) -plt.xlabel(r'Normalized poloidal flux ($\psi$)') -plt.ylabel(r'$\langle p\rangle_\xi$') -plt.title(r'Pressure') +# plt.xlim(0.6, 1.0) +plt.xlabel(r"Normalized poloidal flux ($\psi$)") +plt.ylabel(r"$\langle p\rangle_\xi$") +plt.title(r"Pressure") xmin, xmax = plt.xlim() ymin, ymax = plt.ylim() -#plt.savefig('image/plotcollapse.png', bbox_inches='tight') -#plt.savefig('image/plotcollapse.eps', bbox_inches='tight') +# plt.savefig('image/plotcollapse.png', bbox_inches='tight') +# plt.savefig('image/plotcollapse.eps', bbox_inches='tight') plt.tight_layout() diff --git a/examples/elm-pb/Python/plotmode.py b/examples/elm-pb/Python/plotmode.py index d89fdaf940..9325ee8764 100644 --- a/examples/elm-pb/Python/plotmode.py +++ b/examples/elm-pb/Python/plotmode.py @@ -4,60 +4,57 @@ from builtins import range from past.utils import old_div -from numpy import *; -#from scipy.io import readsav; -import matplotlib.pyplot as plt; +from numpy import * + +# from scipy.io import readsav; +import matplotlib.pyplot as plt # Dynamic matplotlib settings -from matplotlib import rcParams; -rcParams['font.size'] = 20; -rcParams['legend.fontsize'] = 'small'; -rcParams['legend.labelspacing'] = 0.1; -rcParams['lines.linewidth'] = 2; -rcParams['savefig.bbox'] = 'tight'; +from matplotlib import rcParams +rcParams["font.size"] = 20 +rcParams["legend.fontsize"] = "small" +rcParams["legend.labelspacing"] = 0.1 +rcParams["lines.linewidth"] = 2 +rcParams["savefig.bbox"] = "tight" # Create image directory if not exists -import os; -if not os.path.exists('image'): - os.makedirs('image'); - -#fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; -fphi = load('fp.npy') - -plt.figure(); -for i in range(1, 9): - print("Growth rate for mode number", i) - print(gradient(log(abs(fphi[34, 32, i, :])))) - plt.semilogy(((abs(fphi[34, 32, i, :]))), label = 'n=' + str(i * 5)); +import os -plt.legend(loc=2); -plt.xlabel('Time'); -plt.savefig('image/plotmode.png'); -plt.savefig('image/plotmode.eps'); +if not os.path.exists("image"): + os.makedirs("image") +# fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; +fphi = load("fp.npy") -plt.show(block=False); -plt.figure(); +plt.figure() for i in range(1, 9): - plt.plot(abs(fphi[:, 32, i, -1]), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodeamp.png'); -plt.savefig('image/plotmodeamp.eps'); - -plt.show(block=False); - -plt.figure(); + print("Growth rate for mode number", i) + print(gradient(log(abs(fphi[34, 32, i, :])))) + plt.semilogy((abs(fphi[34, 32, i, :])), label="n=" + str(i * 5)) + +plt.legend(loc=2) +plt.xlabel("Time") +plt.savefig("image/plotmode.png") +plt.savefig("image/plotmode.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(old_div(abs(fphi[:, 32, i, -1]),abs(fphi[:, 32, i, -1]).max()), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodenorm.png'); -plt.savefig('image/plotmodenorm.eps'); - -plt.show(); - + plt.plot(abs(fphi[:, 32, i, -1]), label="n=" + str(i * 5)) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodeamp.png") +plt.savefig("image/plotmodeamp.eps") +plt.show(block=False) +plt.figure() +for i in range(1, 9): + plt.plot( + old_div(abs(fphi[:, 32, i, -1]), abs(fphi[:, 32, i, -1]).max()), + label="n=" + str(i * 5), + ) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodenorm.png") +plt.savefig("image/plotmodenorm.eps") +plt.show() diff --git a/examples/elm-pb/Python/plotmode2.py b/examples/elm-pb/Python/plotmode2.py index d0c63f32d9..c298a3a5ef 100644 --- a/examples/elm-pb/Python/plotmode2.py +++ b/examples/elm-pb/Python/plotmode2.py @@ -4,63 +4,61 @@ from builtins import range from past.utils import old_div -from numpy import *; -#from scipy.io import readsav; -import matplotlib.pyplot as plt; +from numpy import * + +# from scipy.io import readsav; +import matplotlib.pyplot as plt from boutdata.collect import collect # Dynamic matplotlib settings -from matplotlib import rcParams; -rcParams['font.size'] = 20; -rcParams['legend.fontsize'] = 'small'; -rcParams['legend.labelspacing'] = 0.1; -rcParams['lines.linewidth'] = 2; -rcParams['savefig.bbox'] = 'tight'; +from matplotlib import rcParams +rcParams["font.size"] = 20 +rcParams["legend.fontsize"] = "small" +rcParams["legend.labelspacing"] = 0.1 +rcParams["lines.linewidth"] = 2 +rcParams["savefig.bbox"] = "tight" # Create image directory if not exists -import os; -if not os.path.exists('image'): - os.makedirs('image'); +import os + +if not os.path.exists("image"): + os.makedirs("image") -path='./data/' -data=collect('P',path=path) +path = "./data/" +data = collect("P", path=path) -#fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; +# fphi = transpose(readsav('fphi.idl.dat')['fphi'])[:,:,:,]; fphi = fft.fft(data, axis=3) -plt.figure(); +plt.figure() for i in range(1, 9): - print("Growth rate for mode number", i) - print(gradient(log(abs(fphi[:,34, 32, i])))) - plt.semilogy(((abs(fphi[:,34, 32, i]))), label = 'n=' + str(i * 5)); - -plt.legend(loc=2); -plt.xlabel('Time'); -plt.savefig('image/plotmode.png'); -plt.savefig('image/plotmode.eps'); - - -plt.show(block=False); -plt.figure(); + print("Growth rate for mode number", i) + print(gradient(log(abs(fphi[:, 34, 32, i])))) + plt.semilogy((abs(fphi[:, 34, 32, i])), label="n=" + str(i * 5)) + +plt.legend(loc=2) +plt.xlabel("Time") +plt.savefig("image/plotmode.png") +plt.savefig("image/plotmode.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(abs(fphi[-1, :, 32, i]), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodeamp.png'); -plt.savefig('image/plotmodeamp.eps'); - -plt.show(block=False); - -plt.figure(); + plt.plot(abs(fphi[-1, :, 32, i]), label="n=" + str(i * 5)) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodeamp.png") +plt.savefig("image/plotmodeamp.eps") +plt.show(block=False) +plt.figure() for i in range(1, 9): - plt.plot(old_div(abs(fphi[-1, :, 32, i]),abs(fphi[-1, :, 32, i]).max()), label = 'n=' + str(i * 5)); - -plt.legend(); -plt.xlabel('X index'); - -plt.savefig('image/plotmodenorm.png'); -plt.savefig('image/plotmodenorm.eps'); - -plt.show(); + plt.plot( + old_div(abs(fphi[-1, :, 32, i]), abs(fphi[-1, :, 32, i]).max()), + label="n=" + str(i * 5), + ) + +plt.legend() +plt.xlabel("X index") +plt.savefig("image/plotmodenorm.png") +plt.savefig("image/plotmodenorm.eps") +plt.show() diff --git a/examples/elm-pb/Python/plotphase.py b/examples/elm-pb/Python/plotphase.py index 9225e498ae..10f4279cf4 100755 --- a/examples/elm-pb/Python/plotphase.py +++ b/examples/elm-pb/Python/plotphase.py @@ -4,34 +4,33 @@ from numpy import save, load, angle import matplotlib.pyplot as plt -fphi = load('fphi.npy') +fphi = load("fphi.npy") -fte = load('fte.npy') -phase_te = angle(old_div(fphi,fte)) -save('phase_te', phase_te) +fte = load("fte.npy") +phase_te = angle(old_div(fphi, fte)) +save("phase_te", phase_te) plt.figure() -plt.plot(mean(mean(phase_te[:,:,3,:],axis=1),axis=1)) -plt.title('Te') -plt.savefig('image/phase_te.png') -plt.savefig('image/phase_te.eps') +plt.plot(mean(mean(phase_te[:, :, 3, :], axis=1), axis=1)) +plt.title("Te") +plt.savefig("image/phase_te.png") +plt.savefig("image/phase_te.eps") -fti = load('fti.npy') -phase_ti = angle(old_div(fphi,fti)) -save('phase_ti', phase_ti) +fti = load("fti.npy") +phase_ti = angle(old_div(fphi, fti)) +save("phase_ti", phase_ti) plt.figure() -plt.plot(mean(mean(phase_ti[:,:,3,:],axis=1),axis=1)) -plt.title('ti') -plt.savefig('image/phase_ti.png') -plt.savefig('image/phase_ti.eps') +plt.plot(mean(mean(phase_ti[:, :, 3, :], axis=1), axis=1)) +plt.title("ti") +plt.savefig("image/phase_ti.png") +plt.savefig("image/phase_ti.eps") -fni = load('fni.npy') -phase_ni = angle(old_div(fphi,fni)) -save('phase_ni', phase_ni) +fni = load("fni.npy") +phase_ni = angle(old_div(fphi, fni)) +save("phase_ni", phase_ni) plt.figure() -plt.plot(mean(mean(phase_ni[:,:,3,:],axis=1),axis=1)) -plt.title('ni') -plt.savefig('image/phase_ni.png') -plt.savefig('image/phase_ni.eps') +plt.plot(mean(mean(phase_ni[:, :, 3, :], axis=1), axis=1)) +plt.title("ni") +plt.savefig("image/phase_ni.png") +plt.savefig("image/phase_ni.eps") plt.show() - diff --git a/examples/elm-pb/Python/polslice.py b/examples/elm-pb/Python/polslice.py index fe78495b5a..6a1179b2ae 100644 --- a/examples/elm-pb/Python/polslice.py +++ b/examples/elm-pb/Python/polslice.py @@ -11,31 +11,33 @@ # Specify parameters -path='./data/' +path = "./data/" -variable="P" +variable = "P" p = collect(variable, path=path) -period=15 +period = 15 -grid='../cbm18_dens8.grid_nx68ny64.nc' +grid = "../cbm18_dens8.grid_nx68ny64.nc" ######################################################## # Call plotpolslice once to get extended poloidal grid -r,z,fun=plotpolslice(p[0,:,:,:],grid,period=period,rz=1) +r, z, fun = plotpolslice(p[0, :, :, :], grid, period=period, rz=1) -nx=r.shape[0] # number of points in r -ny=r.shape[1] # number of points in z -nt=p.shape[0] # time intervals +nx = r.shape[0] # number of points in r +ny = r.shape[1] # number of points in z +nt = p.shape[0] # time intervals -fm=np.zeros((nt,nx,ny)) # array to store the time sequence of the poloidal cross section +fm = np.zeros( + (nt, nx, ny) +) # array to store the time sequence of the poloidal cross section -#Compute all time frames +# Compute all time frames for k in range(nt): - fm[k,:,:]=plotpolslice(p[k,:,:,:],grid,period=period,rz=0) + fm[k, :, :] = plotpolslice(p[k, :, :, :], grid, period=period, rz=0) -np.savez('pslice',fm=fm, z=z, r=r) +np.savez("pslice", fm=fm, z=z, r=r) diff --git a/examples/elm-pb/Python/post.py b/examples/elm-pb/Python/post.py index 9bb67347f0..cdaefa98cf 100644 --- a/examples/elm-pb/Python/post.py +++ b/examples/elm-pb/Python/post.py @@ -15,12 +15,12 @@ from mayavi import mlab -path0="./data0/" -path1="./data/" +path0 = "./data0/" +path1 = "./data/" -period=15 +period = 15 -gfile='./cbm18_dens8.grid_nx68ny64.nc' +gfile = "./cbm18_dens8.grid_nx68ny64.nc" with DataFile(gfile) as f: @@ -28,71 +28,115 @@ Dphi0 = collect("Dphi0", path=path0) -phi0 = collect("phi0", path=path1) # needs diamagnetic effects +phi0 = collect("phi0", path=path1) # needs diamagnetic effects # -psixy=g.get('psixy') -PSI_AXIS=g.get('psi_axis') -PSI_BNDRY=g.get('psi_bndry') +psixy = g.get("psixy") +PSI_AXIS = g.get("psi_axis") +PSI_BNDRY = g.get("psi_bndry") # -psix=old_div((psixy[:,32]-PSI_AXIS),(PSI_BNDRY-PSI_AXIS)) -Epsi=-deriv(phi0[:,32],psix) +psix = old_div((psixy[:, 32] - PSI_AXIS), (PSI_BNDRY - PSI_AXIS)) +Epsi = -deriv(phi0[:, 32], psix) # # -fig=figure() -plot(psix,-Dphi0[:,32], 'r', linewidth=5) -plot(psix,Epsi,'k',linewidth=5) -annotate('w/o flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -annotate('w/ flow', xy=(.7, .4), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='r', size=30) -xlabel('Radial $\psi$',fontsize=25) -ylabel('$\Omega(\psi)/\omega_A$',fontsize=25) -ylim([-.05,0]) -xlim([0.4,1.2]) +fig = figure() +plot(psix, -Dphi0[:, 32], "r", linewidth=5) +plot(psix, Epsi, "k", linewidth=5) +annotate( + "w/o flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +annotate( + "w/ flow", + xy=(0.7, 0.4), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="r", + size=30, +) +xlabel("Radial $\psi$", fontsize=25) +ylabel("$\Omega(\psi)/\omega_A$", fontsize=25) +ylim([-0.05, 0]) +xlim([0.4, 1.2]) fig.set_tight_layout(True) show(block=False) p_f0 = collect("P", path=path0) p_f = collect("P", path=path1) # -rmsp_f0=moment_xyzt(p_f0, 'RMS').rms -rmsp_f=moment_xyzt(p_f, 'RMS').rms +rmsp_f0 = moment_xyzt(p_f0, "RMS").rms +rmsp_f = moment_xyzt(p_f, "RMS").rms # -fig=figure(figsize=(10, 8)) -plot(np.gradient(np.log(rmsp_f0[:,34,32])), color='k',linewidth=3) -plot(np.gradient(np.log(rmsp_f[:,34,32])),color='red',linewidth=3) - -ylabel('$\gamma / \omega_A$',fontsize=25) -xlabel('Time$(\\tau_A)$',fontsize=25) -annotate('w/o flow', xy=(.5, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -annotate('w/ flow', xy=(.5, .4), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='r', size=30) -ylim([0,0.5]) -xlim([0,100]) +fig = figure(figsize=(10, 8)) +plot(np.gradient(np.log(rmsp_f0[:, 34, 32])), color="k", linewidth=3) +plot(np.gradient(np.log(rmsp_f[:, 34, 32])), color="red", linewidth=3) + +ylabel("$\gamma / \omega_A$", fontsize=25) +xlabel("Time$(\\tau_A)$", fontsize=25) +annotate( + "w/o flow", + xy=(0.5, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +annotate( + "w/ flow", + xy=(0.5, 0.4), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="r", + size=30, +) +ylim([0, 0.5]) +xlim([0, 100]) fig.set_tight_layout(True) show(block=False) - -plotpolslice(p_f0[50,:,:,:],gfile,period=period, fig=1) -mlab.text(.01,.99,"w/o flow") - -plotpolslice(p_f[50,:,:,:],gfile,period=period, fig=1) -mlab.text(.01,.99,"w/ flow") - -fig=figure() -mode_structure(p_f0[50,:,:,:], g, period=period) -plot([40,40],[0,.014],'k--',linewidth=5) -annotate('w/o flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', size=30) -ylim([0,0.014]) -xlim([0,80]) +plotpolslice(p_f0[50, :, :, :], gfile, period=period, fig=1) +mlab.text(0.01, 0.99, "w/o flow") + +plotpolslice(p_f[50, :, :, :], gfile, period=period, fig=1) +mlab.text(0.01, 0.99, "w/ flow") + +fig = figure() +mode_structure(p_f0[50, :, :, :], g, period=period) +plot([40, 40], [0, 0.014], "k--", linewidth=5) +annotate( + "w/o flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + size=30, +) +ylim([0, 0.014]) +xlim([0, 80]) fig.set_tight_layout(True) show(block=False) figure() -mode_structure(p_f[50,:,:,:], g, period=period) -plot([40,40],[0,.014],'k--',linewidth=5) -annotate('w/ flow', xy=(.3, .7), xycoords='axes fraction',horizontalalignment='center', verticalalignment='center', color='k', size=30) -ylim([0,0.0001]) -xlim([0,80]) +mode_structure(p_f[50, :, :, :], g, period=period) +plot([40, 40], [0, 0.014], "k--", linewidth=5) +annotate( + "w/ flow", + xy=(0.3, 0.7), + xycoords="axes fraction", + horizontalalignment="center", + verticalalignment="center", + color="k", + size=30, +) +ylim([0, 0.0001]) +xlim([0, 80]) show(block=False) show() diff --git a/examples/elm-pb/Python/read_elmsize.py b/examples/elm-pb/Python/read_elmsize.py index fcd3d24e53..340f57ce7b 100644 --- a/examples/elm-pb/Python/read_elmsize.py +++ b/examples/elm-pb/Python/read_elmsize.py @@ -4,12 +4,12 @@ from pylab import save, figure, plot, title, xlabel, ylabel, show, tight_layout from elm_size import elm_size -path='./data' +path = "./data" -t_array=collect('t_array', path=path) -save('t_array.dat', t_array) -p0=collect('P0', path=path) -save('p0.dat', p0) +t_array = collect("t_array", path=path) +save("t_array.dat", t_array) +p0 = collect("P0", path=path) +save("p0.dat", p0) # n0=collect('n0', path=path) @@ -22,27 +22,27 @@ with DataFile("./cbm18_dens8.grid_nx68ny64.nc") as f: gfile = {v: f.read(v) for v in f.keys()} -p=collect('P', path=path) -save('p.dat', p) -res=moment_xyzt(p,'RMS','DC') -rmsp=res.rms -dcp=res.dc -save('rmsp.dat', rmsp) -save('dcp.dat', dcp) -elmsp=elm_size(dcp,p0,gfile,yind=32,Bbar=gfile['bmag']) -save('elmsp.dat', elmsp) +p = collect("P", path=path) +save("p.dat", p) +res = moment_xyzt(p, "RMS", "DC") +rmsp = res.rms +dcp = res.dc +save("rmsp.dat", rmsp) +save("dcp.dat", dcp) +elmsp = elm_size(dcp, p0, gfile, yind=32, Bbar=gfile["bmag"]) +save("elmsp.dat", elmsp) figure(0) -plot(t_array,elmsp.s2, 'k-') -xlabel('t/Ta') -ylabel('Elm size') -title('Elm size, P') +plot(t_array, elmsp.s2, "k-") +xlabel("t/Ta") +ylabel("Elm size") +title("Elm size, P") tight_layout() show() -phi=collect('phi', path=path ) -save('phi.dat', phi) -res=moment_xyzt( phi, 'DC', 'RMS') -save('dcphi.dat',res.dc) -save('rmsphi.dat', res.rms) +phi = collect("phi", path=path) +save("phi.dat", phi) +res = moment_xyzt(phi, "DC", "RMS") +save("dcphi.dat", res.dc) +save("rmsphi.dat", res.rms) diff --git a/examples/elm-pb/Python/showpolslice.py b/examples/elm-pb/Python/showpolslice.py index d292575e35..072c53dcd6 100644 --- a/examples/elm-pb/Python/showpolslice.py +++ b/examples/elm-pb/Python/showpolslice.py @@ -6,10 +6,12 @@ import numpy as np from tvtk.tools import visual + try: from enthought.mayavi import mlab except ImportError: - try: from mayavi import mlab + try: + from mayavi import mlab except ImportError: print("No mlab available") @@ -17,40 +19,42 @@ ########################### # Read polslice array -npzfile=np.load('pslice.npz') -r=npzfile['r'] -z=npzfile['z'] -fm=npzfile['fm'] +npzfile = np.load("pslice.npz") +r = npzfile["r"] +z = npzfile["z"] +fm = npzfile["fm"] ######################################################## # Set up the window -f = mlab.figure(size=(800,600)) +f = mlab.figure(size=(800, 600)) # Tell visual to use this as the viewer. visual.set_viewer(f) ######################################################## # Do the appropriate graph -#s = mlab.contour_surf(r,z,fun, contours=30, line_width=.5, transparent=True) -#s=mlab.surf(r,z,fun, colormap='Spectral') -s = mlab.mesh(r,z,fm[0,:,:], scalars=fm[0,:,:], colormap='PuOr')#, wrap_scale='true')#, representation='wireframe') -s.enable_contours=True -s.contour.filled_contours=True +# s = mlab.contour_surf(r,z,fun, contours=30, line_width=.5, transparent=True) +# s=mlab.surf(r,z,fun, colormap='Spectral') +s = mlab.mesh( + r, z, fm[0, :, :], scalars=fm[0, :, :], colormap="PuOr" +) # , wrap_scale='true')#, representation='wireframe') +s.enable_contours = True +s.contour.filled_contours = True # Define perspective and optional attributes. You can also implement from the window afterwards -mlab.view(0,0) -#mlab.view(-94.159958841373324, +mlab.view(0, 0) +# mlab.view(-94.159958841373324, # 53.777002382688906, # 8.2311808018087582) mlab.draw(f) mlab.colorbar(orientation="vertical") -#mlab.axes() -#mlab.outline() +# mlab.axes() +# mlab.outline() ######################################################## # mlab animation -anim(s,fm, save=True) +anim(s, fm, save=True) diff --git a/examples/elm-pb/Python/sprofiles.py b/examples/elm-pb/Python/sprofiles.py index 9d8eea48de..244599f8af 100644 --- a/examples/elm-pb/Python/sprofiles.py +++ b/examples/elm-pb/Python/sprofiles.py @@ -16,45 +16,46 @@ with DataFile(gfile) as f: g = {v: f.read(v) for v in f.keys()} -var=collect("P", path=path) +var = collect("P", path=path) -sol=surface_average(var, g) -#sol=np.mean(var,axis=3) +sol = surface_average(var, g) +# sol=np.mean(var,axis=3) -p0av=collect("P0", path=path) +p0av = collect("P0", path=path) -q=np.zeros(sol.shape) +q = np.zeros(sol.shape) for i in range(sol.shape[1]): - q[:,i]=sol[:,i]+p0av[:,0] + q[:, i] = sol[:, i] + p0av[:, 0] -psixy=g.get('psixy') -psi0=g.get('psi_axis') -psix=g.get('psi_bndry') +psixy = g.get("psixy") +psi0 = g.get("psi_axis") +psix = g.get("psi_bndry") -xarr = psixy[:,0] -xarr = old_div((xarr - psi0), (-psi0 + psix)) #for this grid +xarr = psixy[:, 0] +xarr = old_div((xarr - psi0), (-psi0 + psix)) # for this grid -fig=figure() +fig = figure() -nt=q.shape[1] +nt = q.shape[1] -plot(xarr, p0av,'k',label='t=0') -plot(xarr,q[:,nt/4],'r',label='t='+np.str(nt/4)) -plot(xarr,q[:,nt/2],'b',label='t='+np.str(nt/2)) -plot(xarr,q[:,3*nt/4],'g',label='t='+np.str(3*nt/4)) -plot(xarr, q[:,-1],'k',label='t='+np.str(nt)) +plot(xarr, p0av, "k", label="t=0") +plot(xarr, q[:, nt / 4], "r", label="t=" + np.str(nt / 4)) +plot(xarr, q[:, nt / 2], "b", label="t=" + np.str(nt / 2)) +plot(xarr, q[:, 3 * nt / 4], "g", label="t=" + np.str(3 * nt / 4)) +plot(xarr, q[:, -1], "k", label="t=" + np.str(nt)) from collections import OrderedDict + handles, labels = gca().get_legend_handles_labels() by_label = OrderedDict(list(zip(labels, handles))) legend(list(by_label.values()), list(by_label.keys())) -xlabel(r"$\psi$",fontsize=25) -ylabel(r"$2 \mu_0 / B^2$",fontsize=25) +xlabel(r"$\psi$", fontsize=25) +ylabel(r"$2 \mu_0 / B^2$", fontsize=25) fig.set_tight_layout(True) diff --git a/examples/elm-pb/runexample.py b/examples/elm-pb/runexample.py index f7ebc01028..b49902d29c 100755 --- a/examples/elm-pb/runexample.py +++ b/examples/elm-pb/runexample.py @@ -28,18 +28,22 @@ # Calculate RMS in toroidal direction prms = np.sqrt(np.mean(p**2, axis=3)) -growth = np.gradient(np.log(prms[:,42,32])) +growth = np.gradient(np.log(prms[:, 42, 32])) # Final growth-rate gamma = growth[-2] import matplotlib.pyplot as plt -plt.plot(tarr, prms[:,42,32], label='Outboard midplane') -plt.plot( [tarr[0], tarr[-1]], - [prms[-1,42,32]*np.exp(gamma*(tarr[0] - tarr[-1])), prms[-1,42,32]], '--', label=r'$\gamma =$'+str(gamma)) +plt.plot(tarr, prms[:, 42, 32], label="Outboard midplane") +plt.plot( + [tarr[0], tarr[-1]], + [prms[-1, 42, 32] * np.exp(gamma * (tarr[0] - tarr[-1])), prms[-1, 42, 32]], + "--", + label=r"$\gamma =$" + str(gamma), +) -plt.yscale('log') +plt.yscale("log") plt.grid() plt.xlabel(r"Time [$1/\tau_A$]") @@ -57,19 +61,21 @@ # Take a poloidal slice at fixed toroidal angle from boutdata.pol_slice import pol_slice -p2d = pol_slice(p[-1,:,:,:], 'cbm18_dens8.grid_nx68ny64.nc', n=15, zangle=0.0) + +p2d = pol_slice(p[-1, :, :, :], "cbm18_dens8.grid_nx68ny64.nc", n=15, zangle=0.0) # Read grid file to get coordinates from boututils.datafile import DataFile -g = DataFile('cbm18_dens8.grid_nx68ny64.nc') -Rxy = g.read("Rxy") # Major radius [m] -Zxy = g.read("Zxy") # Height [m] +g = DataFile("cbm18_dens8.grid_nx68ny64.nc") + +Rxy = g.read("Rxy") # Major radius [m] +Zxy = g.read("Zxy") # Height [m] plt.contourf(Rxy, Zxy, p2d, 30) -plt.axis('equal') # Maintain aspect ratio +plt.axis("equal") # Maintain aspect ratio -plt.colorbar() # Plot a bar down the side with a color scale +plt.colorbar() # Plot a bar down the side with a color scale plt.savefig("poloidal_slice.pdf") diff --git a/examples/fci-wave/compare-density.py b/examples/fci-wave/compare-density.py index c039c250b6..16e620a08f 100644 --- a/examples/fci-wave/compare-density.py +++ b/examples/fci-wave/compare-density.py @@ -1,4 +1,3 @@ - import matplotlib.pyplot as plt from boutdata import collect import numpy as np @@ -9,60 +8,61 @@ # Note: Data from fci-wave-logn examples commented out. data_noboundary = [ - ("div", "Model 1 (density, point interpolation)") - ,("div-integrate", "Model 2 (density, area integration)") - ,("logn", "Model 3 (log density, area integration)") - #,("../fci-wave-logn/div-integrate", "Model 5 (velocity, log density, area integration)") + ("div", "Model 1 (density, point interpolation)"), + ("div-integrate", "Model 2 (density, area integration)"), + ("logn", "Model 3 (log density, area integration)"), + # ,("../fci-wave-logn/div-integrate", "Model 5 (velocity, log density, area integration)") ] data_boundary = [ - ("boundary", "Model 2 (density, momentum)") - ,("boundary-logn", "Model 3 (log density, momentum)") - #,("../fci-wave-logn/boundary", "Model 5 (log density, velocity)") - ] + ("boundary", "Model 2 (density, momentum)"), + ("boundary-logn", "Model 3 (log density, momentum)"), + # ,("../fci-wave-logn/boundary", "Model 5 (log density, velocity)") +] # Change this to select no boundary or boundary cases data = data_noboundary if run: from boututils.run_wrapper import shell_safe, launch_safe + shell_safe("make > make.log") - for path,label in data: - launch_safe("./fci-wave -d "+path, nproc=nproc, pipe=False) + for path, label in data: + launch_safe("./fci-wave -d " + path, nproc=nproc, pipe=False) -# Collect the results into a dictionary +# Collect the results into a dictionary sum_n_B = {} -for path,label in data: +for path, label in data: n = collect("n", path=path) Bxyz = collect("Bxyz", path=path) time = collect("t_array", path=path) - + nt, nx, ny, nz = n.shape - + n_B = np.ndarray(nt) for t in range(nt): - n_B[t] = np.sum(n[t,:,:,:] / Bxyz) + n_B[t] = np.sum(n[t, :, :, :] / Bxyz) sum_n_B[path] = (time, n_B) # Plot the density at the final time - + plt.figure() - plt.contourf(n[-1,:,0,:].T, 100) + plt.contourf(n[-1, :, 0, :].T, 100) plt.colorbar() plt.xlabel("Major radius") plt.ylabel("Height") - plt.title("Density n, "+label) - plt.savefig(path+".pdf") + plt.title("Density n, " + label) + plt.savefig(path + ".pdf") plt.show() # Make a plot comparing total sum density / B - + plt.figure() -for path,label in data: +for path, label in data: time, n_B = sum_n_B[path] plt.plot(time, n_B, label=label) plt.legend() @@ -71,4 +71,3 @@ plt.savefig("compare-density.pdf") plt.show() - diff --git a/examples/finite-volume/diffusion/mms.py b/examples/finite-volume/diffusion/mms.py index 2c609ca82e..31b7714727 100644 --- a/examples/finite-volume/diffusion/mms.py +++ b/examples/finite-volume/diffusion/mms.py @@ -5,26 +5,26 @@ from math import pi # Length of the y domain -Ly = 10. +Ly = 10.0 # metric tensor metric = Metric() # Identity # Define solution in terms of input x,y,z -f = 1 + 0.1*sin(2*y - t) -k = 1 + 0.1*sin(y) +f = 1 + 0.1 * sin(2 * y - t) +k = 1 + 0.1 * sin(y) # Turn solution into real x and z coordinates -replace = [ (y, metric.y*2*pi/Ly) ] +replace = [(y, metric.y * 2 * pi / Ly)] f = f.subs(replace) -k = k.subs(replace) +k = k.subs(replace) ############################## # Calculate time derivatives -dfdt = Div_par( k * Grad_par(f) ) +dfdt = Div_par(k * Grad_par(f)) ############################# # Calculate sources @@ -32,7 +32,7 @@ Sf = diff(f, t) - dfdt # Substitute back to get input y coordinates -replace = [ (metric.y, y*Ly/(2*pi) ) ] +replace = [(metric.y, y * Ly / (2 * pi))] k = k.subs(replace) f = f.subs(replace) diff --git a/examples/finite-volume/fluid/mms.py b/examples/finite-volume/fluid/mms.py index 8ed8fba517..a31782e2c7 100644 --- a/examples/finite-volume/fluid/mms.py +++ b/examples/finite-volume/fluid/mms.py @@ -5,19 +5,19 @@ from math import pi # Length of the y domain -Ly = 10. +Ly = 10.0 # metric tensor metric = Metric() # Identity # Define solution in terms of input x,y,z -n = 1 + 0.1*sin(2*y - t) -p = 1 + 0.1*cos(3*y + t) -nv = 0.1*sin(y + 2*t) +n = 1 + 0.1 * sin(2 * y - t) +p = 1 + 0.1 * cos(3 * y + t) +nv = 0.1 * sin(y + 2 * t) # Turn solution into real x and z coordinates -replace = [ (y, metric.y*2*pi/Ly) ] +replace = [(y, metric.y * 2 * pi / Ly)] n = n.subs(replace) p = p.subs(replace) @@ -27,16 +27,16 @@ # Calculate time derivatives v = nv / n -gamma = 5./3 +gamma = 5.0 / 3 # Density equation -dndt = - Div_par(nv) +dndt = -Div_par(nv) # Pressure equation -dpdt = - Div_par(p*v) - (gamma-1.0)*p*Div_par(v) +dpdt = -Div_par(p * v) - (gamma - 1.0) * p * Div_par(v) # Momentum equation -dnvdt = - Div_par(nv*v) - Grad_par(p) +dnvdt = -Div_par(nv * v) - Grad_par(p) ############################# # Calculate sources @@ -46,7 +46,7 @@ Snv = diff(nv, t) - dnvdt # Substitute back to get input y coordinates -replace = [ (metric.y, y*Ly/(2*pi) ) ] +replace = [(metric.y, y * Ly / (2 * pi))] n = n.subs(replace) p = p.subs(replace) diff --git a/examples/laplace-petsc3d/plotcheck.py b/examples/laplace-petsc3d/plotcheck.py index 707e5db1ee..7f2758dd81 100755 --- a/examples/laplace-petsc3d/plotcheck.py +++ b/examples/laplace-petsc3d/plotcheck.py @@ -10,27 +10,27 @@ xg = 2 -f = collect('f', path=datadir)[xg:-xg, :, :] -rhs = collect('rhs', path=datadir)[xg:-xg, :, :] -rhs_check = collect('rhs_check', path=datadir)[xg:-xg, :, :] -error = collect('error', path=datadir)[xg:-xg, :, :] +f = collect("f", path=datadir)[xg:-xg, :, :] +rhs = collect("rhs", path=datadir)[xg:-xg, :, :] +rhs_check = collect("rhs_check", path=datadir)[xg:-xg, :, :] +error = collect("error", path=datadir)[xg:-xg, :, :] pyplot.subplot(221) pyplot.pcolormesh(f[:, yind, :]) pyplot.colorbar() -pyplot.title('f') +pyplot.title("f") pyplot.subplot(222) pyplot.pcolormesh(rhs[:, yind, :]) pyplot.colorbar() -pyplot.title('rhs') +pyplot.title("rhs") pyplot.subplot(223) pyplot.pcolormesh(rhs_check[:, yind, :]) pyplot.colorbar() -pyplot.title('rhs_check') +pyplot.title("rhs_check") pyplot.subplot(224) pyplot.pcolormesh(error[:, yind, :]) pyplot.colorbar() -pyplot.title('error') +pyplot.title("error") pyplot.show() diff --git a/examples/orszag-tang/generate.py b/examples/orszag-tang/generate.py index 3ba6dbbea9..acce7497c3 100644 --- a/examples/orszag-tang/generate.py +++ b/examples/orszag-tang/generate.py @@ -2,14 +2,16 @@ from __future__ import division from builtins import range from past.utils import old_div + # the Scientific Python netCDF 3 interface # http://dirac.cnrs-orleans.fr/ScientificPython/ -#from Scientific.IO.NetCDF import NetCDFFile as Dataset +# from Scientific.IO.NetCDF import NetCDFFile as Dataset # the 'classic' version of the netCDF4 python interface # http://code.google.com/p/netcdf4-python/ import numpy as np from netCDF4 import Dataset -from numpy import dtype # array module from http://numpy.scipy.org +from numpy import dtype # array module from http://numpy.scipy.org + """ This example writes some surface pressure and temperatures The companion program sfc_pres_temp_rd.py shows how to read the netCDF @@ -30,44 +32,45 @@ # # the output array to write will be nx x ny -ny = 100; nx = ny + 4 +ny = 100 +nx = ny + 4 # dy of grid dy = old_div(1.0, np.float(ny)) dx = dy # create grid -dxarr=np.zeros((nx,ny),dtype='float32')+dx -dyarr=np.zeros((nx,ny),dtype='float32')+dy +dxarr = np.zeros((nx, ny), dtype="float32") + dx +dyarr = np.zeros((nx, ny), dtype="float32") + dy -xarr=np.arange(0.,np.float(nx),1.,dtype='float32')*dx -yarr=np.arange(0.,np.float(ny),1.,dtype='float32')*dy +xarr = np.arange(0.0, np.float(nx), 1.0, dtype="float32") * dx +yarr = np.arange(0.0, np.float(ny), 1.0, dtype="float32") * dy # compute initial variables -rho=np.zeros((nx,ny),dtype='float32')+old_div(25.,(36.*np.pi)) -p=np.zeros((nx,ny),dtype='float32')+old_div(5.,(12.*np.pi)) +rho = np.zeros((nx, ny), dtype="float32") + old_div(25.0, (36.0 * np.pi)) +p = np.zeros((nx, ny), dtype="float32") + old_div(5.0, (12.0 * np.pi)) -rho=1. -p=old_div(rho,3.) +rho = 1.0 +p = old_div(rho, 3.0) -v_x=np.zeros((nx,ny),dtype='float32') -Bx=np.zeros((nx,ny),dtype='float32') +v_x = np.zeros((nx, ny), dtype="float32") +Bx = np.zeros((nx, ny), dtype="float32") for y in range(ny): - v_x[:,y]=-np.sin(2.*np.pi*yarr[y]) - Bx[:,y]=-np.sin(2.*np.pi*yarr[y]) - -#Bx=Bx/np.sqrt(4.*np.pi) + v_x[:, y] = -np.sin(2.0 * np.pi * yarr[y]) + Bx[:, y] = -np.sin(2.0 * np.pi * yarr[y]) +# Bx=Bx/np.sqrt(4.*np.pi) -v_y=np.zeros((nx,ny),dtype='float32') -By=np.zeros((nx,ny),dtype='float32') + +v_y = np.zeros((nx, ny), dtype="float32") +By = np.zeros((nx, ny), dtype="float32") for x in range(nx): - v_y[x,:]=np.sin(2.*np.pi*xarr[x]) - By[x,:]=np.sin(4.*np.pi*xarr[x]) - -#By=By/np.sqrt(4.*np.pi) + v_y[x, :] = np.sin(2.0 * np.pi * xarr[x]) + By[x, :] = np.sin(4.0 * np.pi * xarr[x]) + +# By=By/np.sqrt(4.*np.pi) # Domain inside core (periodic) @@ -76,55 +79,62 @@ ixseps2 = nx # open a new netCDF file for writing. -ncfile = Dataset('otv.grd.128.nc','w', format='NETCDF3_CLASSIC') +ncfile = Dataset("otv.grd.128.nc", "w", format="NETCDF3_CLASSIC") # output data. # create the nx and ny dimensions. -ncfile.createDimension('x',nx) -ncfile.createDimension('y',ny) -ncfile.createDimension('single',1) +ncfile.createDimension("x", nx) +ncfile.createDimension("y", ny) +ncfile.createDimension("single", 1) # create and write nx,ny variables -nxx=ncfile.createVariable('nx','i4',('single')) -nyy=ncfile.createVariable('ny','i4',('single')) +nxx = ncfile.createVariable("nx", "i4", ("single")) +nyy = ncfile.createVariable("ny", "i4", ("single")) -nxx[:]=nx -nyy[:]=ny +nxx[:] = nx +nyy[:] = ny # Define the coordinate variables. They will hold the coordinate # information, that is, xarr,yarr -dx = ncfile.createVariable('dx',dtype('float32').char,('x','y')) -dy = ncfile.createVariable('dy',dtype('float32').char,('x','y',)) +dx = ncfile.createVariable("dx", dtype("float32").char, ("x", "y")) +dy = ncfile.createVariable( + "dy", + dtype("float32").char, + ( + "x", + "y", + ), +) # write data to coordinate vars. -dx[:,:] = dxarr -dy[:,:] = dyarr +dx[:, :] = dxarr +dy[:, :] = dyarr # create and write ixseps* dimensions. -ix1=ncfile.createVariable('ixseps1','i4',('single')) -ix2=ncfile.createVariable('ixseps2','i4',('single')) +ix1 = ncfile.createVariable("ixseps1", "i4", ("single")) +ix2 = ncfile.createVariable("ixseps2", "i4", ("single")) -ix1[:]=ixseps1 -ix2[:]=ixseps2 +ix1[:] = ixseps1 +ix2[:] = ixseps2 -# create the corresponding variables -rho0 = ncfile.createVariable('rho0',dtype('float32').char,('x','y')) -p0 = ncfile.createVariable('p0',dtype('float32').char,('x','y')) -v0_x = ncfile.createVariable('v0_x',dtype('float32').char,('x','y')) -v0_y = ncfile.createVariable('v0_y',dtype('float32').char,('x','y')) -B0x = ncfile.createVariable('B0x',dtype('float32').char,('x','y')) -B0y = ncfile.createVariable('B0y',dtype('float32').char,('x','y')) +# create the corresponding variables +rho0 = ncfile.createVariable("rho0", dtype("float32").char, ("x", "y")) +p0 = ncfile.createVariable("p0", dtype("float32").char, ("x", "y")) +v0_x = ncfile.createVariable("v0_x", dtype("float32").char, ("x", "y")) +v0_y = ncfile.createVariable("v0_y", dtype("float32").char, ("x", "y")) +B0x = ncfile.createVariable("B0x", dtype("float32").char, ("x", "y")) +B0y = ncfile.createVariable("B0y", dtype("float32").char, ("x", "y")) # write data to variables. -rho0[:,:]=rho -p0[:,:]=p -v0_x[:,:]=v_x -v0_y[:,:]=v_y -B0x[:,:]=Bx -B0y[:,:]=By +rho0[:, :] = rho +p0[:, :] = p +v0_x[:, :] = v_x +v0_y[:, :] = v_y +B0x[:, :] = Bx +B0y[:, :] = By ncfile.close() -print('*** SUCCESS writing file otv.grd.py.nc!') +print("*** SUCCESS writing file otv.grd.py.nc!") diff --git a/examples/performance/bracket/scaling_parser.py b/examples/performance/bracket/scaling_parser.py index d7a23842d4..15f0ea8780 100644 --- a/examples/performance/bracket/scaling_parser.py +++ b/examples/performance/bracket/scaling_parser.py @@ -2,8 +2,7 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): @@ -13,7 +12,7 @@ def read_file(filename): for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} diff --git a/examples/performance/ddx/new_scaling_parser.py b/examples/performance/ddx/new_scaling_parser.py index d2a5fee191..5c0c92ff5e 100644 --- a/examples/performance/ddx/new_scaling_parser.py +++ b/examples/performance/ddx/new_scaling_parser.py @@ -2,19 +2,19 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): continue from collections import OrderedDict - case_lines = OrderedDict() #{} + + case_lines = OrderedDict() # {} for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} @@ -25,12 +25,13 @@ def read_file(filename): for title, col in zip(titles, line[:-1]): cases_weak[title].append(float(col)) - axis = cases_weak['Local grid'] + axis = cases_weak["Local grid"] data = [cases_weak[x] for x in case_lines] labels = [case_lines[x] for x in case_lines] - return {'axis':axis, 'data':data, 'labels':labels} + return {"axis": axis, "data": data, "labels": labels} + -def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): +def getScan(baseName="timing_{n}.txt", nthreads=(1, 2, 4, 8, 16, 32)): from numpy import zeros dataS = [] @@ -38,17 +39,16 @@ def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): f = baseName.format(n=n) dataS.append(read_file(f)) - nnt = len(dataS) - nlines = len(dataS[0]['data']) - nval = len(dataS[0]['data'][0]) - rawDat = zeros([nnt,nval,nlines]) + nlines = len(dataS[0]["data"]) + nval = len(dataS[0]["data"][0]) + rawDat = zeros([nnt, nval, nlines]) for i, dat in enumerate(dataS): - print(len(dat['data'])) - - rawDat[i,:,:] = dat['data'] + print(len(dat["data"])) + + rawDat[i, :, :] = dat["data"] - axes = [nthreads, dataS[0]['axis']] + axes = [nthreads, dataS[0]["axis"]] - return axes, rawDat, dataS[0]['labels'] + return axes, rawDat, dataS[0]["labels"] diff --git a/examples/performance/ddy/new_scaling_parser.py b/examples/performance/ddy/new_scaling_parser.py index d2a5fee191..5c0c92ff5e 100644 --- a/examples/performance/ddy/new_scaling_parser.py +++ b/examples/performance/ddy/new_scaling_parser.py @@ -2,19 +2,19 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): continue from collections import OrderedDict - case_lines = OrderedDict() #{} + + case_lines = OrderedDict() # {} for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} @@ -25,12 +25,13 @@ def read_file(filename): for title, col in zip(titles, line[:-1]): cases_weak[title].append(float(col)) - axis = cases_weak['Local grid'] + axis = cases_weak["Local grid"] data = [cases_weak[x] for x in case_lines] labels = [case_lines[x] for x in case_lines] - return {'axis':axis, 'data':data, 'labels':labels} + return {"axis": axis, "data": data, "labels": labels} + -def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): +def getScan(baseName="timing_{n}.txt", nthreads=(1, 2, 4, 8, 16, 32)): from numpy import zeros dataS = [] @@ -38,17 +39,16 @@ def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): f = baseName.format(n=n) dataS.append(read_file(f)) - nnt = len(dataS) - nlines = len(dataS[0]['data']) - nval = len(dataS[0]['data'][0]) - rawDat = zeros([nnt,nval,nlines]) + nlines = len(dataS[0]["data"]) + nval = len(dataS[0]["data"][0]) + rawDat = zeros([nnt, nval, nlines]) for i, dat in enumerate(dataS): - print(len(dat['data'])) - - rawDat[i,:,:] = dat['data'] + print(len(dat["data"])) + + rawDat[i, :, :] = dat["data"] - axes = [nthreads, dataS[0]['axis']] + axes = [nthreads, dataS[0]["axis"]] - return axes, rawDat, dataS[0]['labels'] + return axes, rawDat, dataS[0]["labels"] diff --git a/examples/performance/ddz/new_scaling_parser.py b/examples/performance/ddz/new_scaling_parser.py index d2a5fee191..5c0c92ff5e 100644 --- a/examples/performance/ddz/new_scaling_parser.py +++ b/examples/performance/ddz/new_scaling_parser.py @@ -2,19 +2,19 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): continue from collections import OrderedDict - case_lines = OrderedDict() #{} + + case_lines = OrderedDict() # {} for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} @@ -25,12 +25,13 @@ def read_file(filename): for title, col in zip(titles, line[:-1]): cases_weak[title].append(float(col)) - axis = cases_weak['Local grid'] + axis = cases_weak["Local grid"] data = [cases_weak[x] for x in case_lines] labels = [case_lines[x] for x in case_lines] - return {'axis':axis, 'data':data, 'labels':labels} + return {"axis": axis, "data": data, "labels": labels} + -def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): +def getScan(baseName="timing_{n}.txt", nthreads=(1, 2, 4, 8, 16, 32)): from numpy import zeros dataS = [] @@ -38,17 +39,16 @@ def getScan(baseName = "timing_{n}.txt", nthreads = (1,2,4,8,16,32)): f = baseName.format(n=n) dataS.append(read_file(f)) - nnt = len(dataS) - nlines = len(dataS[0]['data']) - nval = len(dataS[0]['data'][0]) - rawDat = zeros([nnt,nval,nlines]) + nlines = len(dataS[0]["data"]) + nval = len(dataS[0]["data"][0]) + rawDat = zeros([nnt, nval, nlines]) for i, dat in enumerate(dataS): - print(len(dat['data'])) - - rawDat[i,:,:] = dat['data'] + print(len(dat["data"])) + + rawDat[i, :, :] = dat["data"] - axes = [nthreads, dataS[0]['axis']] + axes = [nthreads, dataS[0]["axis"]] - return axes, rawDat, dataS[0]['labels'] + return axes, rawDat, dataS[0]["labels"] diff --git a/examples/performance/iterator/scaling_parser.py b/examples/performance/iterator/scaling_parser.py index d7a23842d4..15f0ea8780 100644 --- a/examples/performance/iterator/scaling_parser.py +++ b/examples/performance/iterator/scaling_parser.py @@ -2,8 +2,7 @@ def read_file(filename): - reader = csv.reader(open(filename, 'r'), delimiter='\t', - skipinitialspace=True) + reader = csv.reader(open(filename, "r"), delimiter="\t", skipinitialspace=True) # Skip header for _, _ in zip(range(4), reader): @@ -13,7 +12,7 @@ def read_file(filename): for line in reader: if line == []: break - case_lines[line[0].rstrip('.')] = line[1] + case_lines[line[0].rstrip(".")] = line[1] titles = next(reader) cases_weak = {col.strip(): [] for col in titles[:-1]} diff --git a/examples/staggered_grid/generate.py b/examples/staggered_grid/generate.py index da84e9c2e6..a5a0120612 100755 --- a/examples/staggered_grid/generate.py +++ b/examples/staggered_grid/generate.py @@ -4,11 +4,11 @@ # Generate an input mesh # -from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries +from boututils.datafile import DataFile # Wrapper around NetCDF4 libraries -nx = 5 # Minimum is 5: 2 boundary, one evolved +nx = 5 # Minimum is 5: 2 boundary, one evolved ny = 32 # Minimum 5. Should be divisible by number of processors (so powers of 2 nice) -dy = 1. # distance between points in y, in m/g22/lengthunit +dy = 1.0 # distance between points in y, in m/g22/lengthunit ixseps1 = -1 ixseps2 = -1 diff --git a/examples/subsampling/show.py b/examples/subsampling/show.py index 34c6ee3084..b79c5e2964 100755 --- a/examples/subsampling/show.py +++ b/examples/subsampling/show.py @@ -11,14 +11,14 @@ for pack in monitors: filename, data_name = pack - t = DataFile(path+'/'+filename+'.dmp.0.nc').read('t_array') - data = DataFile(path+'/'+filename+'.dmp.0.nc').read(data_name).flatten() + t = DataFile(path + "/" + filename + ".dmp.0.nc").read("t_array") + data = DataFile(path + "/" + filename + ".dmp.0.nc").read(data_name).flatten() plt.plot(t, data, label="{} {}".format(filename, data_name)) -time = DataFile(path+'/BOUT.dmp.0.nc').read('t_array') -data = DataFile(path+'/BOUT.dmp.0.nc').read("T")[:, 2, 2, 0] +time = DataFile(path + "/BOUT.dmp.0.nc").read("t_array") +data = DataFile(path + "/BOUT.dmp.0.nc").read("T")[:, 2, 2, 0] -plt.plot(time, data, marker='+', label="BOUT++ T") +plt.plot(time, data, marker="+", label="BOUT++ T") plt.xlabel("Time") plt.legend() diff --git a/examples/wave-slab/generate.py b/examples/wave-slab/generate.py index 0a2e8f3049..12698d1e24 100755 --- a/examples/wave-slab/generate.py +++ b/examples/wave-slab/generate.py @@ -51,7 +51,7 @@ for x in range(nx): Bpxy[x, :] = Bpx[x] -Bxy = sqrt(Bpxy ** 2 + Bt ** 2) +Bxy = sqrt(Bpxy**2 + Bt**2) # Calculate change in poloidal flux dr = Lx / nx # Constant mesh spacing in radius diff --git a/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb b/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb index c3c39a3c12..4e940b998f 100644 --- a/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb +++ b/manual/sphinx/figs/figure_creators/LaplacianMatrices.ipynb @@ -40,10 +40,10 @@ "outputs": [], "source": [ "# Note, these can be max 9 due to the current index convention\n", - "nx = 3 # Not including ghost points\n", + "nx = 3 # Not including ghost points\n", "nz = 3\n", "\n", - "startXIndex = 10 # The x indices are the slowest growing indices\n", + "startXIndex = 10 # The x indices are the slowest growing indices\n", "startZIndex = 1 # The z indices are the fastest growing indices" ] }, @@ -85,13 +85,13 @@ "for z in range(nz):\n", " f.append([])\n", " xStart = startZIndex\n", - " xEnd = startXIndex*(nx+1) # +1 due to ghost point\n", + " xEnd = startXIndex * (nx + 1) # +1 due to ghost point\n", " # +startXIndex in the range as the range does not include endpoint\n", - " for xInd in range(xStart, xEnd+startXIndex, 10):\n", - " ind = str(xInd+z)\n", - " if (xInd+z) < startXIndex:\n", - " ind = '0'+str(xInd+z)\n", - " f[z].append(symbols('f_' + ind))\n", + " for xInd in range(xStart, xEnd + startXIndex, 10):\n", + " ind = str(xInd + z)\n", + " if (xInd + z) < startXIndex:\n", + " ind = \"0\" + str(xInd + z)\n", + " f[z].append(symbols(\"f_\" + ind))\n", "\n", "mesh = Matrix(f[::-1])\n", "display(mesh)" @@ -195,21 +195,21 @@ } ], "source": [ - "xVec=[]\n", - "bVec=[]\n", + "xVec = []\n", + "bVec = []\n", "# Do the inner loop, so start ranges at 1\n", "# (nx+1) to include outer ghost point, +1 in the range as the range does not include endpoint\n", - "for x in range(1, (nx+1)+1):\n", - " for z in range(1,nz+1):\n", - " xVec.append(symbols('x_'+str(x)+'_'+str(z)))\n", - " bVec.append(symbols('b_'+str(x)+'_'+str(z)))\n", + "for x in range(1, (nx + 1) + 1):\n", + " for z in range(1, nz + 1):\n", + " xVec.append(symbols(\"x_\" + str(x) + \"_\" + str(z)))\n", + " bVec.append(symbols(\"b_\" + str(x) + \"_\" + str(z)))\n", "\n", "# Do the inner ghost points\n", "# Must count backwards since we are inserting in the front\n", - "for ind in range(nz,0,-1):\n", - " xVec.insert(0, symbols('x_0_'+str(ind)))\n", - " bVec.insert(0, symbols('b_0_'+str(ind)))\n", - " \n", + "for ind in range(nz, 0, -1):\n", + " xVec.insert(0, symbols(\"x_0_\" + str(ind)))\n", + " bVec.insert(0, symbols(\"b_0_\" + str(ind)))\n", + "\n", "display(Matrix(xVec))\n", "display(Matrix(bVec))" ] @@ -274,7 +274,7 @@ "globInd = []\n", "for rows in range(len(xVec)):\n", " cols = []\n", - " for col in range(rows*len(xVec), (rows+1)*len(xVec)):\n", + " for col in range(rows * len(xVec), (rows + 1) * len(xVec)):\n", " cols.append(col)\n", " globInd.append(cols)\n", "\n", @@ -305,10 +305,14 @@ "source": [ "c = []\n", "for x in range(nx):\n", - " indexStart = (startXIndex+1)+(startXIndex*x) # Multiply by 10 due to index system\n", - " indexEnd = (startXIndex+nz+1)+(startXIndex*x) # Multiply by 10 due to index system\n", + " indexStart = (startXIndex + 1) + (\n", + " startXIndex * x\n", + " ) # Multiply by 10 due to index system\n", + " indexEnd = (startXIndex + nz + 1) + (\n", + " startXIndex * x\n", + " ) # Multiply by 10 due to index system\n", " for ind in range(indexStart, indexEnd):\n", - " c.append(symbols('c_'+str(ind)))" + " c.append(symbols(\"c_\" + str(ind)))" ] }, { @@ -328,11 +332,11 @@ "source": [ "# The inner ghost\n", "innerGhostStart = startZIndex\n", - "innerGhostEnd = nz\n", + "innerGhostEnd = nz\n", "ig = []\n", "# +1 in the range as last point is not included\n", - "for z in range(innerGhostStart, innerGhostEnd+1):\n", - " ig.append(symbols('ig_0_'+str(z)))" + "for z in range(innerGhostStart, innerGhostEnd + 1):\n", + " ig.append(symbols(\"ig_0_\" + str(z)))" ] }, { @@ -345,12 +349,12 @@ "source": [ "# The outer ghost\n", "# nx+1 as we want to go past the last inner x grid point\n", - "outerGhostStart = startXIndex*(nx+1) + startZIndex\n", - "outerGhostEnd = startXIndex*(nx+1) + nz\n", + "outerGhostStart = startXIndex * (nx + 1) + startZIndex\n", + "outerGhostEnd = startXIndex * (nx + 1) + nz\n", "og = []\n", "# +1 in the range as last point is not included\n", - "for z in range(outerGhostStart, outerGhostEnd+1):\n", - " og.append(symbols('og_'+str(z)))" + "for z in range(outerGhostStart, outerGhostEnd + 1):\n", + " og.append(symbols(\"og_\" + str(z)))" ] }, { @@ -396,21 +400,27 @@ "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 is there as the matrix indices count from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz-1)-1 # Ending row-1 before the last z-index (last z will be wrapped around)\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz - 1) - 1\n", + " ) # Ending row-1 before the last z-index (last z will be wrapped around)\n", " # +1 in range as last point is not included\n", - " rows = range(startRow, endRow+1)\n", - " cols = range(startRow+1, endRow+1) # Column is shifted +1 from the diagonal\n", - " \n", + " rows = range(startRow, endRow + 1)\n", + " cols = range(startRow + 1, endRow + 1) # Column is shifted +1 from the diagonal\n", + "\n", " # The indices referring to the spatial point in the grid\n", " # The last \"+1\" is fue to the fact that the column is shifted +1 from the diagonal\n", - " startInd = (startXIndex+startZIndex) + (startXIndex*x) + 1\n", - " endInd = (startXIndex+startZIndex) + (nz-1) + (startXIndex*x) + 1 # Wrap around last point\n", + " startInd = (startXIndex + startZIndex) + (startXIndex * x) + 1\n", + " endInd = (\n", + " (startXIndex + startZIndex) + (nz - 1) + (startXIndex * x) + 1\n", + " ) # Wrap around last point\n", " # +1 in range as last point is not included\n", - " inds = range(startInd, endInd+1)\n", - " \n", + " inds = range(startInd, endInd + 1)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('zp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"zp_\" + str(ind))" ] }, { @@ -423,15 +433,17 @@ "source": [ "# The wrap around\n", "# The index referring to the spatial point in the grid\n", - "startInd = startXIndex+startZIndex\n", + "startInd = startXIndex + startZIndex\n", "# The indices referring to the matrix index\n", - "# Last -1 as the matrix indices are counted from 0 \n", - "startRow = (nz+1) + (nz-1) - 1 # nz+1 below from the ghost sub matrix, nz-1 below after that\n", - "startCol = (nz+1)-1 # nz+1 left of the ghost sub matrix\n", + "# Last -1 as the matrix indices are counted from 0\n", + "startRow = (\n", + " (nz + 1) + (nz - 1) - 1\n", + ") # nz+1 below from the ghost sub matrix, nz-1 below after that\n", + "startCol = (nz + 1) - 1 # nz+1 left of the ghost sub matrix\n", "for wrap in range(nx):\n", - " row = startRow+wrap*nz\n", - " col = startCol+wrap*nz\n", - " InvM[row, col] = symbols('zp_'+str(startInd+startXIndex*wrap))" + " row = startRow + wrap * nz\n", + " col = startCol + wrap * nz\n", + " InvM[row, col] = symbols(\"zp_\" + str(startInd + startXIndex * wrap))" ] }, { @@ -452,20 +464,26 @@ "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 is there as the matrix indices count from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz-1)-1 # Ending row-1 before the last z-index (last z will be wrapped around)\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz - 1) - 1\n", + " ) # Ending row-1 before the last z-index (last z will be wrapped around)\n", " # +1 in range as last point is not included\n", - " rows = range(startRow+1, endRow+1) # Row is shifted +1 from the diagonal\n", - " cols = range(startRow, endRow+1)\n", - " \n", + " rows = range(startRow + 1, endRow + 1) # Row is shifted +1 from the diagonal\n", + " cols = range(startRow, endRow + 1)\n", + "\n", " # The indices referring to the spatial point in the grid\n", - " startInd = (startXIndex+startZIndex) + (startXIndex*x)\n", - " endInd = (startXIndex+startZIndex) + (nz-1) + (startXIndex*x) # Wrap around last point\n", + " startInd = (startXIndex + startZIndex) + (startXIndex * x)\n", + " endInd = (\n", + " (startXIndex + startZIndex) + (nz - 1) + (startXIndex * x)\n", + " ) # Wrap around last point\n", " # +1 in range as last point is not included\n", - " inds = range(startInd, endInd+1)\n", - " \n", + " inds = range(startInd, endInd + 1)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('zm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"zm_\" + str(ind))" ] }, { @@ -478,15 +496,19 @@ "source": [ "# The wrap around\n", "# The index referring to the spatial point in the grid\n", - "startInd = startXIndex+startZIndex+(nz-1) # +(nz-1) as this will be the last z point for the current x\n", + "startInd = (\n", + " startXIndex + startZIndex + (nz - 1)\n", + ") # +(nz-1) as this will be the last z point for the current x\n", "# The indices referring to the matrix index\n", - "# Last -1 as the matrix indices are counted from 0 \n", - "startRow = (nz+1)-1 # nz+1 below the ghost sub matrix\n", - "startCol = (nz+1) + (nz-1) - 1 # nz+1 left from the ghost sub matrix, nz-1 left after that\n", + "# Last -1 as the matrix indices are counted from 0\n", + "startRow = (nz + 1) - 1 # nz+1 below the ghost sub matrix\n", + "startCol = (\n", + " (nz + 1) + (nz - 1) - 1\n", + ") # nz+1 left from the ghost sub matrix, nz-1 left after that\n", "for wrap in range(nx):\n", - " row = startRow+wrap*nz\n", - " col = startCol+wrap*nz\n", - " InvM[row, col] = symbols('zm_'+str(startInd+startXIndex*wrap))" + " row = startRow + wrap * nz\n", + " col = startCol + wrap * nz\n", + " InvM[row, col] = symbols(\"zm_\" + str(startInd + startXIndex * wrap))" ] }, { @@ -505,23 +527,29 @@ "outputs": [], "source": [ "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex*2 + startZIndex # *2 as we start at the second inner x-index\n", - "endInd = startInd + (startZIndex*nz) # *nz as this is the last z-index in the current x-index\n", + "startInd = startXIndex * 2 + startZIndex # *2 as we start at the second inner x-index\n", + "endInd = startInd + (\n", + " startZIndex * nz\n", + ") # *nz as this is the last z-index in the current x-index\n", "\n", "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # The last -1 as the matrix indices counts from 0\n", - " startRow = (nz+1)+(x*nz)-1 # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = (nz+1)+(x*nz)+(nz)-1 # Ending at the row referring to the last z-index\n", + " startRow = (\n", + " (nz + 1) + (x * nz) - 1\n", + " ) # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = (\n", + " (nz + 1) + (x * nz) + (nz) - 1\n", + " ) # Ending at the row referring to the last z-index\n", " # Not +1 in range as we do not want to include last point\n", - " rows = range(startRow, endRow)\n", - " cols = range(startRow+nz, endRow+nz) # Start at first index after last z-index\n", - " \n", + " rows = range(startRow, endRow)\n", + " cols = range(startRow + nz, endRow + nz) # Start at first index after last z-index\n", + "\n", " # Indices referring to the spatial points in the grid\n", - " inds = range(startInd+startXIndex*x, endInd+startXIndex*x)\n", - " \n", + " inds = range(startInd + startXIndex * x, endInd + startXIndex * x)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('xp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"xp_\" + str(ind))" ] }, { @@ -534,22 +562,22 @@ "source": [ "# x+1 for inner ghost point\n", "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex + startZIndex # First inner point for first z\n", - "endInd = startInd + (startZIndex*nz) # First inner point for last z\n", + "startInd = startXIndex + startZIndex # First inner point for first z\n", + "endInd = startInd + (startZIndex * nz) # First inner point for last z\n", "\n", "# The indices referring to the matrix index\n", "# The last -1 as the matrix indices counts from 0\n", - "startRow = startZIndex-1 # Starting at first row\n", - "endRow = startZIndex+nz-1 # Ending at the row referring to the last z-index\n", + "startRow = startZIndex - 1 # Starting at first row\n", + "endRow = startZIndex + nz - 1 # Ending at the row referring to the last z-index\n", "# Not +1 in range as we do not want to include last point\n", - "rows = range(startRow, endRow)\n", - "cols = range(startRow+nz, endRow+nz) # Start at first index after last z-index\n", - " \n", + "rows = range(startRow, endRow)\n", + "cols = range(startRow + nz, endRow + nz) # Start at first index after last z-index\n", + "\n", "# Indices referring to the spatial points in the grid\n", - "inds = range(startInd, endInd)\n", - " \n", + "inds = range(startInd, endInd)\n", + "\n", "for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('igxp_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"igxp_\" + str(ind))" ] }, { @@ -569,25 +597,25 @@ "source": [ "# Indices referring to the spatial points in the grid\n", "startInd = startZIndex\n", - "endInd = startInd + nz\n", + "endInd = startInd + nz\n", "\n", "for x in range(nx):\n", " # The indices referring to the matrix index\n", " # Note that x starts counting from zero, so we must add 1 to x in the rows\n", - " startRow = ((x+1)*nz) # Starting at row+1 after inner ghost point sub-matrix\n", - " endRow = ((x+1)*nz)+(nz) # Ending at the row referring to the last z-index\n", + " startRow = (x + 1) * nz # Starting at row+1 after inner ghost point sub-matrix\n", + " endRow = ((x + 1) * nz) + (nz) # Ending at the row referring to the last z-index\n", " # Not +1 in range as we do not want to include last point\n", - " rows = range(startRow, endRow)\n", - " cols = range(startRow-nz, endRow-nz) # Start at first index after last z-index\n", - " \n", + " rows = range(startRow, endRow)\n", + " cols = range(startRow - nz, endRow - nz) # Start at first index after last z-index\n", + "\n", " # Indices referring to the spatial points in the grid\n", - " inds = range(startInd+startXIndex*x, endInd+startXIndex*x)\n", - " \n", + " inds = range(startInd + startXIndex * x, endInd + startXIndex * x)\n", + "\n", " for rInd, cInd, ind in zip(rows, cols, inds):\n", " if (ind) < startXIndex:\n", - " ind = '0'+str(ind)\n", + " ind = \"0\" + str(ind)\n", "\n", - " InvM[rInd, cInd] = symbols('xm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"xm_\" + str(ind))" ] }, { @@ -600,22 +628,24 @@ "source": [ "# x-1 for inner ghost point\n", "# Indices referring to the spatial points in the grid\n", - "startInd = startXIndex*nx + startZIndex # Last inner point for first z\n", - "endInd = startInd + (startZIndex*nz) # Last inner point for last z\n", + "startInd = startXIndex * nx + startZIndex # Last inner point for first z\n", + "endInd = startInd + (startZIndex * nz) # Last inner point for last z\n", "\n", "# The indices referring to the matrix index\n", "# The last -1 as the matrix indices counts from 0\n", - "startRow = len(xVec)-nz-1 # Starting at last inner point row\n", - "endRow = len(xVec)-1 # Ending at the last row\n", + "startRow = len(xVec) - nz - 1 # Starting at last inner point row\n", + "endRow = len(xVec) - 1 # Ending at the last row\n", "# +1 in range as last point is not included\n", - "rows = range(startRow+1, endRow+1)\n", - "cols = range(startRow-nz+1, endRow-nz+1) # Start at first index after last z-index\n", - " \n", + "rows = range(startRow + 1, endRow + 1)\n", + "cols = range(\n", + " startRow - nz + 1, endRow - nz + 1\n", + ") # Start at first index after last z-index\n", + "\n", "# Indices referring to the spatial points in the grid\n", - "inds = range(startInd, endInd)\n", - " \n", + "inds = range(startInd, endInd)\n", + "\n", "for rInd, cInd, ind in zip(rows, cols, inds):\n", - " InvM[rInd, cInd] = symbols('ogxm_'+str(ind))" + " InvM[rInd, cInd] = symbols(\"ogxm_\" + str(ind))" ] }, { diff --git a/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py b/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py index 8a95a13436..5a4a2a0843 100644 --- a/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py +++ b/manual/sphinx/figs/figure_creators/bout_runners_folder_structure.py @@ -10,10 +10,10 @@ mmag@fysik.dtu.dk """ -__authors__ = 'Michael Loeiten' -__email__ = 'mmag@fysik.dtu.dk' -__version__ = '1.0' -__date__ = '21.01.2016' +__authors__ = "Michael Loeiten" +__email__ = "mmag@fysik.dtu.dk" +__version__ = "1.0" +__date__ = "21.01.2016" import pygraphviz as pgv @@ -21,113 +21,118 @@ tree = pgv.AGraph() # Appendable lists -files = [] +files = [] dead_ends = [] # Default node attributes -tree.node_attr['shape']='box' -tree.node_attr['style']='bold' +tree.node_attr["shape"] = "box" +tree.node_attr["style"] = "bold" # Adding nodes and edges -l0 = 'project' -l1 = ['data', 'source\nfiles', 'driver.py'] +l0 = "project" +l1 = ["data", "source\nfiles", "driver.py"] # Append the files files.append(l1[1]) files.append(l1[2]) # Add the boxes to the mother node for box in l1: - tree.add_edge(l0,box) + tree.add_edge(l0, box) -l2 = ['solver1', 'solver2',\ - 'BOUT.inp', 'run_log.txt'] +l2 = ["solver1", "solver2", "BOUT.inp", "run_log.txt"] # Append the files files.append(l2[2]) files.append(l2[3]) # Add the boxes to the mother node for box in l2: - tree.add_edge('data', box) -tree.add_edge('solver2', 'solver2/...') + tree.add_edge("data", box) +tree.add_edge("solver2", "solver2/...") # Append the dead_end -de = l2[1] + '/...' +de = l2[1] + "/..." dead_ends.append(de) -l3 = ['method1', 'method2', 'solver1/...'] +l3 = ["method1", "method2", "solver1/..."] for box in l3: - tree.add_edge('solver1', box) -tree.add_edge('method2', 'method2/...') + tree.add_edge("solver1", box) +tree.add_edge("method2", "method2/...") # Append the dead_end de = l3[2] dead_ends.append(de) -de = l3[1] + '/...' +de = l3[1] + "/..." dead_ends.append(de) -l4 = ['nout\ntimestep1', 'nout\ntimestep2', 'method1/...'] +l4 = ["nout\ntimestep1", "nout\ntimestep2", "method1/..."] for box in l4: - tree.add_edge('method1', box) -tree.add_edge('nout\ntimestep2', 'nout\ntimestep2/...') + tree.add_edge("method1", box) +tree.add_edge("nout\ntimestep2", "nout\ntimestep2/...") # Append the dead_end de = l4[2] dead_ends.append(de) -de = l4[1] + '/...' +de = l4[1] + "/..." dead_ends.append(de) -l5 = ['mesh1', 'mesh2', 'nout\ntimestep1/...'] +l5 = ["mesh1", "mesh2", "nout\ntimestep1/..."] for box in l5: - tree.add_edge('nout\ntimestep1', box) -tree.add_edge('mesh2', 'mesh2/...') + tree.add_edge("nout\ntimestep1", box) +tree.add_edge("mesh2", "mesh2/...") # Append the dead_end de = l5[2] dead_ends.append(de) -de = l5[1] + '/...' +de = l5[1] + "/..." dead_ends.append(de) -l6 = ['additional1', 'additional2', 'mesh1/...'] +l6 = ["additional1", "additional2", "mesh1/..."] for box in l6: - tree.add_edge('mesh1', box) -tree.add_edge('additional2', 'additional2/...') + tree.add_edge("mesh1", box) +tree.add_edge("additional2", "additional2/...") # Append the dead_end de = l6[2] dead_ends.append(de) -de = l6[1] + '/...' +de = l6[1] + "/..." dead_ends.append(de) -l7 = ['grid_file1', 'grid_file2', 'additional1/...'] +l7 = ["grid_file1", "grid_file2", "additional1/..."] for box in l7: - tree.add_edge('additional1', box) -tree.add_edge('grid_file2', 'grid_file2/...') + tree.add_edge("additional1", box) +tree.add_edge("grid_file2", "grid_file2/...") # Append the dead_end de = l7[2] dead_ends.append(de) -de = l7[1] + '/...' +de = l7[1] + "/..." dead_ends.append(de) -l8 = ['BOUT.inp\n(copy)', 'BOUT.log', 'BOUT.dmp',\ - 'BOUT.restart', '(source_files\n(copy))', '(grid_file\n(copy))'] +l8 = [ + "BOUT.inp\n(copy)", + "BOUT.log", + "BOUT.dmp", + "BOUT.restart", + "(source_files\n(copy))", + "(grid_file\n(copy))", +] # Add l8 to the files list for cur_file in l8: files.append(cur_file) # Append them to the mother node for box in l8: - tree.add_edge('grid_file1', box) + tree.add_edge("grid_file1", box) # Change colors for the files for the_file in files: - member=tree.get_node(the_file) -# member.attr['fontcolor'] = 'limegreen' - member.attr['color'] = 'limegreen' + member = tree.get_node(the_file) + # member.attr['fontcolor'] = 'limegreen' + member.attr["color"] = "limegreen" # Change colors for the dead_ends for dead_end in dead_ends: - member=tree.get_node(dead_end) -# member.attr['fontcolor'] = 'darksalmon' - member.attr['color'] = 'darksalmon' + member = tree.get_node(dead_end) + # member.attr['fontcolor'] = 'darksalmon' + member.attr["color"] = "darksalmon" # Print the graph print(tree.string()) # Set layout -tree.layout('dot') +tree.layout("dot") # Write to file -tree.draw('folder_tree.svg') +tree.draw("folder_tree.svg") diff --git a/tests/MMS/advection/runtest b/tests/MMS/advection/runtest index 9a0a691287..5109c47815 100755 --- a/tests/MMS/advection/runtest +++ b/tests/MMS/advection/runtest @@ -50,7 +50,7 @@ def run_mms(options, exit=True): dx = 2.0 * pi / (nx) - args = f"{opts} mesh:nx={nx+4} mesh:dx={dx} MZ={nx}" + args = f"{opts} mesh:nx={nx + 4} mesh:dx={dx} MZ={nx}" print(" Running with " + args) From 03283f45a50a4f883ca0f1a95c906474b7f4e4d5 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Mon, 2 Feb 2026 10:52:26 +0000 Subject: [PATCH 200/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 4990f2586e..a6948a0684 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,3 +1,4 @@ # Clang-format whole repo d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 +52301380586fdbf890f620c04f689b08d89a6c34 From 26651cc10565d47b381febf3b863f3ee79dca597 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 12:15:45 +0100 Subject: [PATCH 201/461] Revert accidential typo --- src/mesh/coordinates.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index b8b0f9ea55..086fa2e23e 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1590,7 +1590,7 @@ Field3D Coordinates::Div_par(const Field3D& f, CELL_LOC outloc, f_B.yup(i) = f.yup(i) / Bxy_floc.yup(i); f_B.ydown(i) = f.ydown(i) / Bxy_floc.ydown(i); } - return xy * Grad_par(f_B, outloc, method); + return Bxy * Grad_par(f_B, outloc, method); } ///////////////////////////////////////////////////////// From f319c61b58ecb145e1607846e10e6748713fd519 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:04:49 +0100 Subject: [PATCH 202/461] Fix comment --- include/bout/options_io.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 509305aad0..3892ccbc1f 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -14,7 +14,7 @@ /// /// 2. Restart files: /// -/// auto restart = OptionsIOFactory::getInstance().createOutput(); +/// auto restart = OptionsIOFactory::getInstance().createRestart(); /// restart->write(data); /// /// where data is an Options tree. By default restart files are configured From 1519bf29232c90be0933153d7a6ae027f7887e29 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:11:06 +0100 Subject: [PATCH 203/461] Add simple method to write data in parallel --- include/bout/options_io.hxx | 28 ++++++++++++++++++++-------- src/sys/options/options_io.cxx | 9 +++++++++ 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 3892ccbc1f..f35aab6716 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -5,8 +5,8 @@ /// /// 1. Dump files, containing time history: /// -/// auto dump = OptionsIOFactory::getInstance().createOutput(); -/// dump->write(data); +/// auto dump = OptionsIOFactory::getInstance().createOutput(); +/// dump->write(data); /// /// where data is an Options tree. By default dump files are configured /// with the root `output` section, or an Option tree can be passed to @@ -14,20 +14,27 @@ /// /// 2. Restart files: /// -/// auto restart = OptionsIOFactory::getInstance().createRestart(); -/// restart->write(data); +/// auto restart = OptionsIOFactory::getInstance().createRestart(); +/// restart->write(data); /// /// where data is an Options tree. By default restart files are configured /// with the root `restart_files` section, or an Option tree can be passed to /// `createRestart`. /// /// 3. Ad-hoc single files -/// Note: The caller should consider how multiple processors interact with the file. +/// Note: The caller should consider how multiple processors interact with the file. /// -/// auto file = OptionsIOFactory::getInstance().createFile("some_file.nc"); -/// or -/// auto file = OptionsIO::create("some_file.nc"); +/// auto file = OptionsIOFactory::getInstance().createFile("some_file.nc"); +/// or +/// auto file = OptionsIO::create("some_file.nc"); /// +/// 4. Ad-hoc parallel files +/// This adds also metric information, such that the file can be read with +/// boutdata or xBOUT +/// +/// OptionIO::write("some_file", data, mesh); +/// +/// if mesh is omitted, no grid information is added. /// #pragma once @@ -77,6 +84,11 @@ public: /// This uses the default file type and default options. static std::unique_ptr create(const std::string& file); + /// Write some data to a file with a given name prefix + /// This will be done in parallel. If Mesh is given, also mesh data will be + /// added, which is needed for xBOUT or boutdata to read the files. + static void write(const std::string& prefix, Options data, Mesh* mesh = nullptr); + /// Create an OptionsIO for I/O to the given file. /// The file will be configured using the given `config` options: /// - "type" : string The file type e.g. "netcdf" or "adios" diff --git a/src/sys/options/options_io.cxx b/src/sys/options/options_io.cxx index 6717b6b07d..9ac5b69cc8 100644 --- a/src/sys/options/options_io.cxx +++ b/src/sys/options/options_io.cxx @@ -2,6 +2,7 @@ #include "bout/bout.hxx" #include "bout/globals.hxx" #include "bout/mesh.hxx" +#include "bout/version.hxx" #include "options_adios.hxx" #include "options_netcdf.hxx" @@ -55,4 +56,12 @@ void writeDefaultOutputFile(Options& data) { OptionsIOFactory::getInstance().createOutput()->write(data); } +void OptionsIO::write(const std::string& prefix, Options data, Mesh* mesh) { + Options file_options = {{"prefix", prefix}}; + data["BOUT_VERSION"].force(bout::version::as_double); + if (mesh != nullptr) { + mesh->outputVars(data); + } + OptionsIOFactory::getInstance().createOutput(&file_options)->write(data); +} } // namespace bout From b61f8158906fa877a806523a93de181465d82988 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:11:26 +0100 Subject: [PATCH 204/461] Use new interface to write data Allows to use other backends then netcdf --- src/solver/impls/euler/euler.cxx | 13 ++----------- src/solver/impls/pvode/pvode.cxx | 12 +----------- 2 files changed, 3 insertions(+), 22 deletions(-) diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 6bdceab516..ed8621a874 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -164,19 +164,10 @@ void EulerSolver::take_step(BoutReal curtime, BoutReal dt, Array& star mesh = f.var->getMesh(); } - if (mesh != nullptr) { - mesh->outputVars(debug); - debug["BOUT_VERSION"].force(bout::version::as_double); - } - const std::string outnumber = dump_at_time < -3 ? fmt::format(".{}", debug_counter++) : ""; - const std::string outname = - fmt::format("{}/BOUT.debug{}.{}.nc", - Options::root()["datadir"].withDefault("data"), - outnumber, BoutComm::rank()); - - bout::OptionsIO::create(outname)->write(debug); + const std::string outname = fmt::format("BOUT.debug{}", outnumber); + bout::OptionsIO::write(outname, debug, mesh); MPI_Barrier(BoutComm::get()); for (auto& f : f3d) { f.F_var->disableTracking(); diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 4cc90af8fd..5fbabe5bd2 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -399,17 +399,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { for (auto& f : f3d) { saveParallel(debug, f.name, *f.var); } - - if (mesh != nullptr) { - mesh->outputVars(debug); - debug["BOUT_VERSION"].force(bout::version::as_double); - } - - const std::string outname = fmt::format( - "{}/BOUT.debug.{}.nc", - Options::root()["datadir"].withDefault("data"), BoutComm::rank()); - - bout::OptionsIO::create(outname)->write(debug); + bout::OptionsIO::write("BOUT.debug", debug, mesh); MPI_Barrier(BoutComm::get()); } return -1.0; From eab6b0356545c84114ffdd7b6d351f77671d17d9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:30:10 +0100 Subject: [PATCH 205/461] Add docs on using solver-dumping --- manual/sphinx/developer_docs/debugging.rst | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index f16d0b6a3b..63c4f4e389 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -158,3 +158,23 @@ If you need to capture runtime information in the message, you can use the ``fmt`` syntax also used by the loggers:: TRACE("Value of i={}, some arbitrary {}", i, "string"); + + +Time evolution +============== + +It can be convinient to know what happend when the simulation failed. +The pvode solver can dump the state of the simulation, at the time where the +solver failed. This information does include the single terms in the +derivative. This allows to identify which term is causing the issue. +In addition the residuum is dump. This not only tells which term, but also +where the solver is struggeling. +This can be enabled with:: + + solver:type=pvode solver:debug_on_failure=true + +It is also possible to dump at a specific time using the euler solver. +This can be handy to track down what is causing differences between two +different versions. It can be used with:: + + solver:type=euler solver:dump_at_time=0 input:error_on_unused_options=false From 508dcdae51d09b49f12b1303d834275b0229a03a Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:39:14 +0100 Subject: [PATCH 206/461] Improve docs --- manual/sphinx/developer_docs/debugging.rst | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index 63c4f4e389..b23d882a42 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -163,18 +163,18 @@ the ``fmt`` syntax also used by the loggers:: Time evolution ============== -It can be convinient to know what happend when the simulation failed. -The pvode solver can dump the state of the simulation, at the time where the -solver failed. This information does include the single terms in the -derivative. This allows to identify which term is causing the issue. -In addition the residuum is dump. This not only tells which term, but also -where the solver is struggeling. -This can be enabled with:: +It can be useful to know what happened when the simulation failed. The pvode +solver can dump the state of the simulation, at the time the solver +failed. This information includes the individual terms in the derivative. This +allows to identify which term is causing the issue. Additionally, the +residuum is dumped. This identifies not only which term is causing the issue, +but also where in the domain the solver is struggling. This can be enabled +with:: solver:type=pvode solver:debug_on_failure=true It is also possible to dump at a specific time using the euler solver. -This can be handy to track down what is causing differences between two +This can be useful for tracking down what is causing differences between two different versions. It can be used with:: solver:type=euler solver:dump_at_time=0 input:error_on_unused_options=false From c9732b7fa2802b07dd2c329b04df647fafb63ad4 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:48:31 +0100 Subject: [PATCH 207/461] Add more docs --- manual/sphinx/developer_docs/debugging.rst | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/manual/sphinx/developer_docs/debugging.rst b/manual/sphinx/developer_docs/debugging.rst index b23d882a42..4f47cd63e3 100644 --- a/manual/sphinx/developer_docs/debugging.rst +++ b/manual/sphinx/developer_docs/debugging.rst @@ -171,10 +171,17 @@ residuum is dumped. This identifies not only which term is causing the issue, but also where in the domain the solver is struggling. This can be enabled with:: - solver:type=pvode solver:debug_on_failure=true + solver:type=pvode solver:debug_on_failure=true + +It can be also useful for understanding why the solver is slow. Forcing a +higher min_timestep, the solver will fail to evolve the system as it +encounters the situation, and provides information where it is happening. +This can be done with:: + + solver:type=pvode solver:debug_on_failure=true solver:min_timestep=1e2 It is also possible to dump at a specific time using the euler solver. This can be useful for tracking down what is causing differences between two different versions. It can be used with:: - solver:type=euler solver:dump_at_time=0 input:error_on_unused_options=false + solver:type=euler solver:dump_at_time=0 input:error_on_unused_options=false From 9b96dad5490af5515fad8464f830a22335a831be Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 14:50:05 +0100 Subject: [PATCH 208/461] simplify to_load creation Co-authored-by: Peter Hill --- src/solver/impls/pvode/pvode.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 5fbabe5bd2..d8e2ab2438 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -380,9 +380,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { std::vector evolve_bndrys{}; for (const auto& f : f3d) { mesh = f.var->getMesh(); - Field3D to_load{0., mesh}; - to_load.allocate(); - to_load.setLocation(f.location); + Field3D to_load{0., mesh}.setLocation(f.location); debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; list_of_fields.push_back(to_load); evolve_bndrys.push_back(f.evolve_bndry); From 94f9ea601d153f3f8020efa7fb38b4448aaf5e61 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 15:13:46 +0100 Subject: [PATCH 209/461] Fixup to_load --- src/solver/impls/pvode/pvode.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index d8e2ab2438..95adecaec1 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -380,7 +380,8 @@ BoutReal PvodeSolver::run(BoutReal tout) { std::vector evolve_bndrys{}; for (const auto& f : f3d) { mesh = f.var->getMesh(); - Field3D to_load{0., mesh}.setLocation(f.location); + Field3D to_load{0., mesh}; + to_load.setLocation(f.location); debug[fmt::format("{:s}{:s}", prefix, f.name)] = to_load; list_of_fields.push_back(to_load); evolve_bndrys.push_back(f.evolve_bndry); From 6ec39173f20de8a3aab59cf24f23e5793623fee6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 15:35:18 +0100 Subject: [PATCH 210/461] Take reference to avoid copy --- include/bout/options_io.hxx | 2 +- src/sys/options/options_io.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index f35aab6716..4fab70aea8 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -87,7 +87,7 @@ public: /// Write some data to a file with a given name prefix /// This will be done in parallel. If Mesh is given, also mesh data will be /// added, which is needed for xBOUT or boutdata to read the files. - static void write(const std::string& prefix, Options data, Mesh* mesh = nullptr); + static void write(const std::string& prefix, Options& data, Mesh* mesh = nullptr); /// Create an OptionsIO for I/O to the given file. /// The file will be configured using the given `config` options: diff --git a/src/sys/options/options_io.cxx b/src/sys/options/options_io.cxx index 9ac5b69cc8..256ec56b28 100644 --- a/src/sys/options/options_io.cxx +++ b/src/sys/options/options_io.cxx @@ -56,7 +56,7 @@ void writeDefaultOutputFile(Options& data) { OptionsIOFactory::getInstance().createOutput()->write(data); } -void OptionsIO::write(const std::string& prefix, Options data, Mesh* mesh) { +void OptionsIO::write(const std::string& prefix, Options& data, Mesh* mesh) { Options file_options = {{"prefix", prefix}}; data["BOUT_VERSION"].force(bout::version::as_double); if (mesh != nullptr) { From bc59d075f251660a476c241465be6f5bcee35e4c Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Feb 2026 10:59:38 +0100 Subject: [PATCH 211/461] Fix manual build --- manual/CMakeLists.txt | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt index b5224440bf..af4c528446 100644 --- a/manual/CMakeLists.txt +++ b/manual/CMakeLists.txt @@ -7,21 +7,18 @@ set(BOUT_SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/sphinx) set(BOUT_SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs) set(env_command - ${CMAKE_COMMAND} -E env - PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} + ${CMAKE_COMMAND} -E env PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} ) add_custom_target(sphinx-html - COMMAND ${env_command} - COMMAND ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} + COMMAND ${env_command} ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} COMMAND ${CMAKE_COMMAND} -E echo "Generated HTML docs in file://${BOUT_SPHINX_BUILD}/index.html" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating HTML documentation with Sphinx in ${BOUT_SPHINX_BUILD}" ) add_custom_target(sphinx-pdf - COMMAND ${env_command} - COMMAND ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} + COMMAND ${env_command} ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} COMMAND ${CMAKE_COMMAND} -E echo "Generated PDF docs in file://${BOUT_SPHINX_BUILD}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating PDF documentation with Sphinx in ${BOUT_SPHINX_BUILD}" From 5053c8f753e6cd9d98ea34e4161f82f1716e1b15 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 6 Feb 2026 09:16:44 -0800 Subject: [PATCH 212/461] initial_profiles: Use FieldFactory singleton Rather than creating a new FieldFactory, use the singleton object. This allows users to register new generators and use them to intialise variables. --- src/field/initialprofiles.cxx | 25 ++++--------------------- 1 file changed, 4 insertions(+), 21 deletions(-) diff --git a/src/field/initialprofiles.cxx b/src/field/initialprofiles.cxx index 2c34caa6e1..6a437dbe02 100644 --- a/src/field/initialprofiles.cxx +++ b/src/field/initialprofiles.cxx @@ -1,23 +1,10 @@ /************************************************************************** * Sets initial profiles * - * ChangeLog - * ========= - * - * 2011-02-12 Ben Dudson - * * Changed to use new options system. For now the structure of the - * options is the same, but this could be modified more easily in future - * - * 2010-05-12 Ben Dudson - * - * * Changed random numbers to use a hash of the parameters - * so that the phase doesn't vary with number of processors or grid size - * User can vary phase to give a different random sequence - * ************************************************************************** - * Copyright 2010 B.D.Dudson, S.Farley, M.V.Umansky, X.Q.Xu + * Copyright 2010 - 2026 BOUT++ contributors * - * Contact: Ben Dudson, bd512@york.ac.uk + * Contact: Ben Dudson, dudson2@llnl.gov * * This file is part of BOUT++. * @@ -49,12 +36,10 @@ void initial_profile(const std::string& name, Field3D& var) { Options* varOpts = Options::getRoot()->getSection(name); - FieldFactory f(localmesh); - std::string function; VAROPTION(varOpts, function, "0.0"); - var = f.create3D(function, varOpts, nullptr, var.getLocation()); + var = FieldFactory::get()->create3D(function, varOpts, localmesh, var.getLocation()); // Optionally scale the variable BoutReal scale; @@ -68,12 +53,10 @@ void initial_profile(const std::string& name, Field2D& var) { Options* varOpts = Options::getRoot()->getSection(name); - FieldFactory f(localmesh); - std::string function; VAROPTION(varOpts, function, "0.0"); - var = f.create2D(function, varOpts, nullptr, var.getLocation()); + var = FieldFactory::get()->create2D(function, varOpts, localmesh, var.getLocation()); // Optionally scale the variable BoutReal scale; From 8c351e45204e5ec1a1e5760d60a6e749707ee8f5 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 10:49:18 -0800 Subject: [PATCH 213/461] snes: Change target_its to BoutReal Allows the target to be set to push timestep rather than stall once reaching a set number of iterations. --- src/solver/impls/snes/snes.hxx | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 5d007f8ad7..15f97c42a7 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -162,8 +162,11 @@ private: ///< PID controller parameters bool pid_controller; ///< Use PID controller? - int target_its; ///< Target number of nonlinear iterations for the PID controller. - ///< Use with caution! Not tested values. + + /// Target number of nonlinear iterations for the PID controller. + /// This can be non-integer to push timestep more aggressively + BoutReal target_its; + BoutReal kP; ///< (0.6 - 0.8) Proportional parameter (main response to current step) BoutReal kI; ///< (0.2 - 0.4) Integral parameter (smooths history of changes) BoutReal kD; ///< (0.1 - 0.3) Derivative (dampens oscillation - optional) From 20613a4ad6419218da21e785addf01a88159aa8b Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 10:53:55 -0800 Subject: [PATCH 214/461] snes: last_failure_weight input for tuning recent_failure_rate Calculates a moving average of recent SNES failures. Enables timestep controllers to be more cautious when SNES is repeatedly failing. --- src/solver/impls/snes/snes.cxx | 7 +++++-- src/solver/impls/snes/snes.hxx | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index da66092e84..7684c79a86 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -545,6 +545,9 @@ SNESSolver::SNESSolver(Options* opts) (*options)["pid_consider_failures"] .doc("Reduce timestep increases if recent solves have failed") .withDefault(false)), + last_failure_weight((*options)["last_failure_weight"] + .doc("Weighting of last timestep in recent failure rate") + .withDefault(0.1)), diagnose( (*options)["diagnose"].doc("Print additional diagnostics").withDefault(false)), diagnose_failures((*options)["diagnose_failures"] @@ -979,12 +982,12 @@ int SNESSolver::run() { SNESGetLinearSolveIterations(snes, &lin_its); // Rolling average of recent failures - recent_failure_rate *= 1. - inv_failure_window; + recent_failure_rate *= 1. - last_failure_weight; if ((ierr != PETSC_SUCCESS) or (reason < 0)) { // Diverged or SNES failed - recent_failure_rate += inv_failure_window; + recent_failure_rate += last_failure_weight; ++snes_failures; steps_since_snes_failure = 0; diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 15f97c42a7..0cf9eef4b0 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -172,7 +172,7 @@ private: BoutReal kD; ///< (0.1 - 0.3) Derivative (dampens oscillation - optional) bool pid_consider_failures; ///< Reduce timestep increases if recent solves have failed BoutReal recent_failure_rate; ///< Rolling average of recent failure rate - const BoutReal inv_failure_window = 0.1; ///< 1 / number of recent solves + BoutReal last_failure_weight; ///< 1 / number of recent solves int nl_its_prev; int nl_its_prev2; From a4c48fb4db25d7957f580db68b2ea76ac3510bf9 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 10:57:09 -0800 Subject: [PATCH 215/461] snes: updateGlobalTimestep for global timestep control Separate out the global timestep control into a function --- src/solver/impls/snes/snes.cxx | 89 +++++++++++++--------------------- src/solver/impls/snes/snes.hxx | 4 ++ 2 files changed, 39 insertions(+), 54 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 7684c79a86..32a9902543 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -851,7 +851,6 @@ int SNESSolver::run() { bool looping = true; int snes_failures = 0; // Count SNES convergence failures - int steps_since_snes_failure = 0; int saved_jacobian_lag = 0; int loop_count = 0; recent_failure_rate = 0.0; @@ -990,7 +989,6 @@ int SNESSolver::run() { recent_failure_rate += last_failure_weight; ++snes_failures; - steps_since_snes_failure = 0; if (diagnose_failures or (snes_failures == max_snes_failures)) { // Print diagnostics to help identify source of the problem @@ -1123,7 +1121,6 @@ int SNESSolver::run() { } simtime += dt; - ++steps_since_snes_failure; if (diagnose) { // Gather and print diagnostic information @@ -1145,67 +1142,25 @@ int SNESSolver::run() { } if (equation_form == BoutSnesEquationForm::pseudo_transient) { - if (pid_controller) { - // Adjust pseudo_alpha based on nonlinear iterations - pseudo_alpha = pid(pseudo_alpha, nl_its, max_timestep * atol * 100); - } + // Adjust pseudo_alpha to globally scale timesteps + pseudo_alpha = updateGlobalTimestep(pseudo_alpha, nl_its, recent_failure_rate, max_timestep * atol * 100); // Adjust local timesteps PetscCall(updatePseudoTimestepping(snes_x)); - } else if (pid_controller) { - // Changing the timestep using a PID controller. - // Note: The preconditioner depends on the timestep, - // so we recalculate the jacobian and the preconditioner - // every time the timestep changes - - timestep = pid(timestep, nl_its, max_timestep); + } else { + // Adjust timestep + timestep = updateGlobalTimestep(timestep, nl_its, recent_failure_rate, max_timestep); + } - // NOTE(malamast): Do we really need this? - // Recompute Jacobian (for now) + if (static_cast(lin_its) / nl_its > 0.5 * maxl) { + // Recompute Jacobian if number of linear iterations is too high if (saved_jacobian_lag == 0) { SNESGetLagJacobian(snes, &saved_jacobian_lag); SNESSetLagJacobian(snes, 1); } - - } else { - // Consider changing the timestep. - // Note: The preconditioner depends on the timestep, - // so if it is not recalculated the it will be less - // effective. - if ((nl_its <= lower_its) && (timestep < max_timestep) - && (steps_since_snes_failure > 2)) { - // Increase timestep slightly - timestep *= timestep_factor_on_lower_its; - - timestep = std::min(timestep, max_timestep); - - // Note: Setting the SNESJacobianFn to NULL retains - // previously set evaluation function. - // - // The SNES Jacobian is a combination of the RHS Jacobian - // and a factor involving the timestep. - // Depends on equation_form - // -> Probably call SNESSetJacobian(snes, Jfd, Jfd, NULL, fdcoloring); - if (static_cast(lin_its) / nl_its > 4) { - // Recompute Jacobian (for now) - if (saved_jacobian_lag == 0) { - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); - } - } - - } else if (nl_its >= upper_its) { - // Reduce timestep slightly - timestep *= timestep_factor_on_upper_its; - - // Recompute Jacobian - if (saved_jacobian_lag == 0) { - SNESGetLagJacobian(snes, &saved_jacobian_lag); - SNESSetLagJacobian(snes, 1); - } - } } + snes_failures = 0; } while (looping); @@ -1274,6 +1229,32 @@ int SNESSolver::run() { return 0; } +BoutReal SNESSolver::updateGlobalTimestep(BoutReal timestep, int nl_its, + BoutReal recent_failure_rate, BoutReal max_dt) { + // Note: The preconditioner depends on the timestep, + // so if it is not recalculated the it will be less + // effective. + + if (pid_controller) { + // Changing the timestep using a PID controller. + return pid(timestep, nl_its, max_dt); + } + + // Consider changing the timestep. + if ((nl_its <= lower_its) && (timestep < max_timestep) && (recent_failure_rate < 0.5)) { + // Increase timestep slightly + timestep *= timestep_factor_on_lower_its; + + return std::min(timestep, max_timestep); + } + + if (nl_its >= upper_its) { + // Reduce timestep slightly + return timestep * timestep_factor_on_upper_its; + } + return timestep; // No change +} + PetscErrorCode SNESSolver::initPseudoTimestepping() { // Storage for per-variable timestep PetscCall(VecDuplicate(snes_x, &dt_vec)); diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 0cf9eef4b0..5b8ed14f7c 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -144,6 +144,10 @@ private: BoutReal pseudo_max_ratio; ///< Maximum timestep ratio between neighboring cells Vec dt_vec; ///< Each quantity can have its own timestep + /// Adjust the global timestep + BoutReal updateGlobalTimestep(BoutReal timestep, int nl_its, + BoutReal recent_failure_rate, BoutReal max_dt); + /// Initialize the Pseudo-Transient Continuation method PetscErrorCode initPseudoTimestepping(); /// Update dt_vec based on new solution x From 39b259594ebbe92c719716a79be6d4ff7a04f0ba Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 12:39:18 -0800 Subject: [PATCH 216/461] snes: Split residual and pseudo-timestep calculation Now always calculates local and global residuals. If pseudo timestepping is used then another loop over variables is used to update per-cell timesteps and set `dt_vec`. When `diagnose=true`, the local residual will be saved as `snes_local_residual`, and the global residual will be printed after each internal timestep. --- src/solver/impls/snes/snes.cxx | 168 +++++++++++++++++++++++---------- src/solver/impls/snes/snes.hxx | 17 +++- 2 files changed, 133 insertions(+), 52 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 32a9902543..4513cb5aeb 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -670,6 +670,10 @@ int SNESSolver::init() { if (equation_form == BoutSnesEquationForm::pseudo_transient) { PetscCall(initPseudoTimestepping()); } + // Per-cell residuals + local_residual = 0.0; + local_residual_2d = 0.0; + global_residual = 0.0; // Nonlinear solver interface (SNES) output_info.write("Create SNES\n"); @@ -845,6 +849,15 @@ int SNESSolver::run() { PetscCall(VecRestoreArray(snes_x, &xdata)); } + // Initialise residuals + local_residual = 0.0; + local_residual_2d = 0.0; + global_residual = 0.0; + PetscCall(updateResiduals(snes_x)); + if (diagnose) { + output.write("\n Residual: {}\n", global_residual); + } + BoutReal target = simtime; for (int s = 0; s < getNumberOutputSteps(); s++) { target += getOutputTimestep(); @@ -1122,6 +1135,9 @@ int SNESSolver::run() { simtime += dt; + // Update local and global residuals + PetscCall(updateResiduals(snes_x)); + if (diagnose) { // Gather and print diagnostic information @@ -1132,6 +1148,8 @@ int SNESSolver::run() { output.write(", SNES failures: {}", snes_failures); } output.write("\n"); + // Print residual on separate line, so post-processing isn't affected + output.write(" Residual: {}\n", global_residual); } // MatFilter and MatEliminateZeros(Mat, bool) require PETSc >= 3.20 @@ -1146,7 +1164,7 @@ int SNESSolver::run() { pseudo_alpha = updateGlobalTimestep(pseudo_alpha, nl_its, recent_failure_rate, max_timestep * atol * 100); // Adjust local timesteps - PetscCall(updatePseudoTimestepping(snes_x)); + PetscCall(updatePseudoTimestepping()); } else { // Adjust timestep @@ -1255,6 +1273,95 @@ BoutReal SNESSolver::updateGlobalTimestep(BoutReal timestep, int nl_its, return timestep; // No change } +PetscErrorCode SNESSolver::updateResiduals(Vec x) { + // Push residuals to previous residuals + // Copying so that data is not shared + local_residual_prev = copy(local_residual); + local_residual_2d_prev = copy(local_residual_2d); + global_residual_prev = global_residual; + + // Call RHS function to get time derivatives + PetscCall(rhs_function(x, snes_f, false)); + + // Reading the residual vectors + const BoutReal* current_residual = nullptr; + PetscCall(VecGetArrayRead(snes_f, ¤t_residual)); + + // Note: The ordering of quantities in the PETSc vectors + // depends on the Solver::loop_vars function + Mesh* mesh = bout::globals::mesh; + int idx = 0; // Index into PETSc Vecs + + // Boundary cells + for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { + // Field2D quantities evolved together + BoutReal residual = 0.0; + int count = 0; + + for (const auto& f : f2d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + residual += SQ(current_residual[idx++]); + ++count; + } + if (count > 0) { + local_residual_2d[i2d] = sqrt(residual / count); + } + + // Field3D quantities evolved together within a cell + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + count = 0; + residual = 0.0; + for (const auto& f : f3d) { + if (!f.evolve_bndry) { + continue; // Not evolving boundary => Skip + } + residual += SQ(current_residual[idx++]); + ++count; + } + if (count > 0) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + local_residual[i3d] = sqrt(residual / count); + } + } + } + + // Bulk of domain. + // These loops don't check the boundary flags + for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { + // Field2D quantities evolved together + if (!f2d.empty()) { + BoutReal residual = 0.0; + for (std::size_t i = 0; i != f2d.size(); ++i) { + residual += SQ(current_residual[idx++]); + } + local_residual_2d[i2d] = sqrt(residual / static_cast(f2d.size())); + } + + // Field3D quantities evolved together within a cell + if (!f3d.empty()) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + auto i3d = mesh->ind2Dto3D(i2d, jz); + + BoutReal residual = 0.0; + for (std::size_t i = 0; i != f3d.size(); ++i) { + residual += SQ(current_residual[idx++]); + } + local_residual[i3d] = sqrt(residual / static_cast(f3d.size())); + } + } + } + + // Restore Vec data arrays + PetscCall(VecRestoreArrayRead(snes_f, ¤t_residual)); + + // Global residual metric (RMS) + global_residual = std::sqrt(mean(SQ(local_residual), true)); + + return PETSC_SUCCESS; +} + PetscErrorCode SNESSolver::initPseudoTimestepping() { // Storage for per-variable timestep PetscCall(VecDuplicate(snes_x, &dt_vec)); @@ -1263,22 +1370,14 @@ PetscErrorCode SNESSolver::initPseudoTimestepping() { // Diagnostic outputs pseudo_timestep = timestep; - pseudo_residual = 0.0; - pseudo_residual_2d = 0.0; return PETSC_SUCCESS; } -PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { - // Call RHS function to get time derivatives - PetscCall(rhs_function(x, snes_f, false)); - +PetscErrorCode SNESSolver::updatePseudoTimestepping() { // Use a per-cell timestep so that e.g density and pressure // evolve in a way consistent with the equation of state. - // Reading the residual vectors - const BoutReal* current_residual = nullptr; - PetscCall(VecGetArrayRead(snes_f, ¤t_residual)); // Modifying the dt_vec values BoutReal* dt_data = nullptr; PetscCall(VecGetArray(dt_vec, &dt_data)); @@ -1292,47 +1391,34 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { for (const auto& i2d : mesh->getRegion2D("RGN_BNDRY")) { // Field2D quantities evolved together int count = 0; - BoutReal residual = 0.0; - for (const auto& f : f2d) { if (!f.evolve_bndry) { continue; // Not evolving boundary => Skip } - residual += SQ(current_residual[idx + count]); ++count; } if (count > 0) { - residual = sqrt(residual / count); - // Adjust timestep for these quantities - BoutReal new_timestep = - updatePseudoTimestep(dt_data[idx], pseudo_residual_2d[i2d], residual); + BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], local_residual_2d_prev[i2d], + local_residual_2d[i2d]); for (int i = 0; i != count; ++i) { dt_data[idx++] = new_timestep; } - pseudo_residual_2d[i2d] = residual; } // Field3D quantities evolved together within a cell for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { count = 0; - residual = 0.0; for (const auto& f : f3d) { if (!f.evolve_bndry) { continue; // Not evolving boundary => Skip } - residual += SQ(current_residual[idx + count]); ++count; } if (count > 0) { - residual = sqrt(residual / count); - auto i3d = mesh->ind2Dto3D(i2d, jz); - const BoutReal new_timestep = - updatePseudoTimestep(dt_data[idx], pseudo_residual[i3d], residual); - - pseudo_residual[i3d] = residual; - pseudo_timestep[i3d] = new_timestep; + const BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_prev[i3d], local_residual[i3d]); for (int i = 0; i != count; ++i) { dt_data[idx++] = new_timestep; @@ -1346,19 +1432,12 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { for (const auto& i2d : mesh->getRegion2D("RGN_NOBNDRY")) { // Field2D quantities evolved together if (!f2d.empty()) { - BoutReal residual = 0.0; - for (std::size_t i = 0; i != f2d.size(); ++i) { - residual += SQ(current_residual[idx + i]); - } - residual = sqrt(residual / f2d.size()); - // Adjust timestep for these quantities - const BoutReal new_timestep = - updatePseudoTimestep(dt_data[idx], pseudo_residual_2d[i2d], residual); + const BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_2d_prev[i2d], local_residual_2d[i2d]); for (std::size_t i = 0; i != f2d.size(); ++i) { dt_data[idx++] = new_timestep; } - pseudo_residual_2d[i2d] = residual; } // Field3D quantities evolved together within a cell @@ -1366,15 +1445,8 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { auto i3d = mesh->ind2Dto3D(i2d, jz); - BoutReal residual = 0.0; - for (std::size_t i = 0; i != f3d.size(); ++i) { - residual += SQ(current_residual[idx + i]); - } - residual = sqrt(residual / f3d.size()); - BoutReal new_timestep = - updatePseudoTimestep(dt_data[idx], pseudo_residual[i3d], residual); - - pseudo_residual[i3d] = residual; + BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_prev[i3d], local_residual[i3d]); // Compare to neighbors BoutReal min_neighboring_dt = max_timestep; @@ -1404,7 +1476,6 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping(Vec x) { } // Restore Vec data arrays - PetscCall(VecRestoreArrayRead(snes_f, ¤t_residual)); PetscCall(VecRestoreArray(dt_vec, &dt_data)); // Need timesteps on neighboring processors @@ -1801,11 +1872,12 @@ void SNESSolver::outputVars(Options& output_options, bool save_repeat) { return; // Don't save diagnostics to restart files } + output_options["snes_local_residual"].assignRepeat(local_residual, "t", save_repeat, + "SNESSolver"); + if (equation_form == BoutSnesEquationForm::pseudo_transient) { output_options["snes_pseudo_alpha"].assignRepeat(pseudo_alpha, "t", save_repeat, "SNESSolver"); - output_options["snes_pseudo_residual"].assignRepeat(pseudo_residual, "t", save_repeat, - "SNESSolver"); output_options["snes_pseudo_timestep"].assignRepeat(pseudo_timestep, "t", save_repeat, "SNESSolver"); } diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 5b8ed14f7c..b36212ade2 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -148,10 +148,20 @@ private: BoutReal updateGlobalTimestep(BoutReal timestep, int nl_its, BoutReal recent_failure_rate, BoutReal max_dt); + /// Calculate per-cell and global residuals + /// given an input system state `x` + PetscErrorCode updateResiduals(Vec x); + Field3D local_residual; ///< Residual of Field3D quantities in each cell + Field2D local_residual_2d; ///< Residual of Field2D quantities in each cell + BoutReal global_residual; ///< Global residual measure + Field3D local_residual_prev; ///< Previous Field3D local residuals + Field2D local_residual_2d_prev; ///< Previous Field2D local residuals + BoutReal global_residual_prev; ///< Previous global residual + /// Initialize the Pseudo-Transient Continuation method PetscErrorCode initPseudoTimestepping(); - /// Update dt_vec based on new solution x - PetscErrorCode updatePseudoTimestepping(Vec x); + /// Update dt_vec based on residuals + PetscErrorCode updatePseudoTimestepping(); /// Decide the next pseudo-timestep. Called by updatePseudoTimestepping BoutReal updatePseudoTimestep(BoutReal previous_timestep, BoutReal previous_residual, BoutReal current_residual); @@ -160,8 +170,7 @@ private: BoutReal updatePseudoTimestep_history_based(BoutReal previous_timestep, BoutReal previous_residual, BoutReal current_residual); - Field3D pseudo_residual; ///< Diagnostic output - Field2D pseudo_residual_2d; + Field3D pseudo_timestep; ///< PID controller parameters From b2102d058a9a9cbe89c69abc05b30f4744c71715 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 15:04:12 -0800 Subject: [PATCH 217/461] snes: output_trigger option Selects the trigger for writing an output. The default is `fixed_time_interval` that outputs at regular time intervals. Other choice is `residual_ratio` that outputs when the global residual falls by a factor `output_residual_ratio`. --- src/solver/impls/snes/snes.cxx | 93 +++++++++++++++++++++------------- src/solver/impls/snes/snes.hxx | 27 ++++++++-- 2 files changed, 80 insertions(+), 40 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 4513cb5aeb..1766c94186 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -474,6 +474,14 @@ PetscErrorCode SNESSolver::FDJrestoreFromPruning() { SNESSolver::SNESSolver(Options* opts) : Solver(opts), + output_trigger( + (*options)["output_trigger"] + .doc("Decides when to save outputs. fixed_time_interval, residual_ratio") + .withDefault(BoutSnesOutput::fixed_time_interval)), + output_residual_ratio( + (*options)["output_residual_ratio"] + .doc("Trigger an output when residual falls by this ratio") + .withDefault(0.5)), timestep( (*options)["timestep"].doc("Initial backward Euler timestep").withDefault(1.0)), dt_min_reset((*options)["dt_min_reset"] @@ -535,8 +543,13 @@ SNESSolver::SNESSolver(Options* opts) pseudo_max_ratio((*options)["pseudo_max_ratio"] .doc("PTC maximum timestep ratio between neighbors") .withDefault(2.)), - pid_controller( - (*options)["pid_controller"].doc("Use PID controller?").withDefault(false)), + timestep_control((*options)["timestep_control"] + .doc("Timestep control method") + .withDefault(BoutSnesTimestep::pid_nonlinear_its)), + timestep_factor((*options)["timestep_factor"] + .doc("When timestep_control=residual_ratio, multiply timestep " + "by this factor each step") + .withDefault(1.1)), target_its((*options)["target_its"].doc("Target snes iterations").withDefault(7)), kP((*options)["kP"].doc("Proportional PID parameter").withDefault(0.7)), kI((*options)["kI"].doc("Integral PID parameter").withDefault(0.3)), @@ -733,16 +746,10 @@ int SNESSolver::init() { // Note: If the 'Amat' Jacobian is matrix free, SNESComputeJacobian // always updates its reference 'u' vector every nonlinear iteration SNESSetLagJacobian(snes, lag_jacobian); - if (pid_controller) { - nl_its_prev = target_its; - nl_its_prev2 = target_its; - SNESSetLagJacobianPersists(snes, PETSC_FALSE); - SNESSetLagPreconditionerPersists(snes, PETSC_FALSE); - } else { - // Set Jacobian and preconditioner to persist across time steps - SNESSetLagJacobianPersists(snes, PETSC_TRUE); - SNESSetLagPreconditionerPersists(snes, PETSC_TRUE); - } + nl_its_prev = target_its; + nl_its_prev2 = target_its; + SNESSetLagJacobianPersists(snes, PETSC_FALSE); + SNESSetLagPreconditionerPersists(snes, PETSC_FALSE); SNESSetLagPreconditioner(snes, 1); // Rebuild when Jacobian is rebuilt } @@ -859,6 +866,7 @@ int SNESSolver::run() { } BoutReal target = simtime; + recent_failure_rate = 0.0; for (int s = 0; s < getNumberOutputSteps(); s++) { target += getOutputTimestep(); @@ -866,10 +874,14 @@ int SNESSolver::run() { int snes_failures = 0; // Count SNES convergence failures int saved_jacobian_lag = 0; int loop_count = 0; - recent_failure_rate = 0.0; + + BoutReal start_global_residual = global_residual; do { - if (simtime >= target) + if ((output_trigger == BoutSnesOutput::fixed_time_interval && (simtime >= target)) + || (output_trigger == BoutSnesOutput::residual_ratio + && (global_residual <= start_global_residual * output_residual_ratio))) break; // Could happen if step over multiple outputs + if (scale_vars) { // Individual variable scaling // Note: If variables are rescaled then the Jacobian columns @@ -930,8 +942,7 @@ int SNESSolver::run() { PetscCall(VecMin(dt_vec, nullptr, ×tep)); dt = timestep; - looping = true; - if (simtime + timestep >= target) { + if (output_trigger == BoutSnesOutput::fixed_time_interval && simtime + timestep >= target) { looping = false; } } else { @@ -954,8 +965,7 @@ int SNESSolver::run() { // Set the timestep dt = timestep; - looping = true; - if (simtime + dt >= target) { + if (output_trigger == BoutSnesOutput::fixed_time_interval && simtime + dt >= target) { // Note: When the timestep is changed the preconditioner needs to be updated // => Step over the output time and interpolate if not matrix free @@ -975,9 +985,7 @@ int SNESSolver::run() { VecAXPBY(snes_x, -beta, (1. + beta), x1); } - if (pid_controller) { - SNESSetLagJacobian(snes, lag_jacobian); - } + SNESSetLagJacobian(snes, lag_jacobian); } // Run the solver @@ -1182,7 +1190,8 @@ int SNESSolver::run() { snes_failures = 0; } while (looping); - if (!matrix_free) { + BoutReal output_time = simtime; + if (output_trigger == BoutSnesOutput::fixed_time_interval && !matrix_free) { ASSERT2(simtime >= target); ASSERT2(simtime - dt <= target); // Stepped over output timestep => Interpolate @@ -1197,6 +1206,7 @@ int SNESSolver::run() { // output_x <- alpha * x0 + (1 - alpha) * output_x VecAXPBY(output_x, alpha, 1. - alpha, x0); + output_time = target; } else { // Timestep was adjusted to hit target output time @@ -1231,7 +1241,7 @@ int SNESSolver::run() { PetscCall(VecRestoreArrayRead(scaled_x, &xdata)); try { - run_rhs(target); // Run RHS to calculate auxilliary variables + run_rhs(output_time); // Run RHS to calculate auxilliary variables } catch (BoutException& e) { output_error.write("ERROR: BoutException thrown: {}\n", e.what()); // Abort simulation. There is no way to recover unless @@ -1239,7 +1249,7 @@ int SNESSolver::run() { BoutComm::abort(1); } - if (call_monitors(target, s, getNumberOutputSteps()) != 0) { + if (call_monitors(output_time, s, getNumberOutputSteps()) != 0) { break; // User signalled to quit } } @@ -1253,22 +1263,35 @@ BoutReal SNESSolver::updateGlobalTimestep(BoutReal timestep, int nl_its, // so if it is not recalculated the it will be less // effective. - if (pid_controller) { + switch (timestep_control) { + case BoutSnesTimestep::pid_nonlinear_its: // Changing the timestep using a PID controller. return pid(timestep, nl_its, max_dt); - } - // Consider changing the timestep. - if ((nl_its <= lower_its) && (timestep < max_timestep) && (recent_failure_rate < 0.5)) { - // Increase timestep slightly - timestep *= timestep_factor_on_lower_its; + case BoutSnesTimestep::threshold_nonlinear_its: + // Consider changing the timestep, based on thresholds in NL iterations + if ((nl_its <= lower_its) && (timestep < max_timestep) && (recent_failure_rate < 0.5)) { + // Increase timestep slightly + timestep *= timestep_factor_on_lower_its; + return std::min(timestep, max_timestep); + } - return std::min(timestep, max_timestep); - } + if (nl_its >= upper_its) { + // Reduce timestep slightly + return timestep * timestep_factor_on_upper_its; + } + return timestep; // No change - if (nl_its >= upper_its) { - // Reduce timestep slightly - return timestep * timestep_factor_on_upper_its; + case BoutSnesTimestep::residual_ratio: + // Use ratio of previous and current global residual + // Intended to be the same as https://petsc.org/release/manualpages/TS/TSPSEUDO/ + // (Note the PETSc manual has the expression for dt_n upside down) + + return std::min({timestep_factor * timestep * global_residual_prev / global_residual, + max_timestep}); + + case BoutSnesTimestep::fixed: + break; } return timestep; // No change } diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index b36212ade2..768ba33520 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -4,7 +4,7 @@ * using PETSc for the SNES interface * ************************************************************************** - * Copyright 2015-2025 BOUT++ contributors + * Copyright 2015-2026 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -59,6 +59,16 @@ BOUT_ENUM_CLASS(BoutPTCStrategy, history_based, ///< Grow/shrink dt based on residual decrease/increase hybrid); ///< Combine inverse_residual and history_based strategies +BOUT_ENUM_CLASS(BoutSnesTimestep, + pid_nonlinear_its, ///< PID controller on nonlinear iterations + threshold_nonlinear_its, ///< Use thresholds on nonlinear iterations + residual_ratio, ///< Use ratio of previous and current residual + fixed); ///< Fixed timestep (no adaptation) + +BOUT_ENUM_CLASS(BoutSnesOutput, + fixed_time_interval, ///< Output at fixed time intervals + residual_ratio); ///< When the residual is reduced by a given ratio + /// Uses PETSc's SNES interface to find a steady state solution to a /// nonlinear ODE by integrating in time with Backward Euler class SNESSolver : public Solver { @@ -112,6 +122,10 @@ private: /// @param[in] linear Specifies that the SNES solver is in a linear (KSP) inner loop PetscErrorCode rhs_function(Vec x, Vec f, bool linear); + BoutSnesOutput output_trigger; ///< Sets when outputs are written + + BoutReal output_residual_ratio; ///< Trigger an output when residual falls by this ratio + BoutReal timestep; ///< Internal timestep BoutReal dt; ///< Current timestep used in snes_function. BoutReal dt_min_reset; ///< If dt falls below this, reset solve @@ -173,8 +187,11 @@ private: Field3D pseudo_timestep; - ///< PID controller parameters - bool pid_controller; ///< Use PID controller? + /// Timestep controller method + BoutSnesTimestep timestep_control; + + /// When using BoutSnesTimestep::residual_ratio + BoutReal timestep_factor; ///< Multiply timestep by this each step /// Target number of nonlinear iterations for the PID controller. /// This can be non-integer to push timestep more aggressively @@ -187,8 +204,8 @@ private: BoutReal recent_failure_rate; ///< Rolling average of recent failure rate BoutReal last_failure_weight; ///< 1 / number of recent solves - int nl_its_prev; - int nl_its_prev2; + BoutReal nl_its_prev; + BoutReal nl_its_prev2; BoutReal pid(BoutReal timestep, int nl_its, BoutReal max_dt); ///< Updates the timestep From 4e134e9ab0080cf859802f31f893a7cb51ded0e2 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 15:33:23 -0800 Subject: [PATCH 218/461] snes: Update manual Describe new settings `timestep_control` and `output_trigger` that select different strategies to adjust global timesteps and output simulation state. --- manual/sphinx/user_docs/time_integration.rst | 70 ++++++++++++++++---- 1 file changed, 57 insertions(+), 13 deletions(-) diff --git a/manual/sphinx/user_docs/time_integration.rst b/manual/sphinx/user_docs/time_integration.rst index 7658602bc8..70b9c3df92 100644 --- a/manual/sphinx/user_docs/time_integration.rst +++ b/manual/sphinx/user_docs/time_integration.rst @@ -444,29 +444,73 @@ on nonlinear iteration count. dt_min_reset = 1e-6 # Reset the solver when timestep < this # Timestep adaptation + timestep_control = pid_nonlinear_its + target_its = 7 # Target number of nonlinear iterations + kP = 0.7 # Proportional gain + kI = 0.3 # Integral gain + kD = 0.2 # Derivative gain + +This uses a PID controller that adjusts the timestep to maintain approximately ``target_its`` +nonlinear iterations per solve. + +Residual Ratio +^^^^^^^^^^^^^^ + +This adjusts the timestep using the ratio of global residuals and a timestep factor: + +.. math:: + + dt_n = r dt_{n-1} \frac{||F(X_{n-1})||}{||F(X_{n})|| + +so that as the residual falls the timestep :math:`dt` is increased. +The :math:`r` parameter is input option ``timestep_factor`` that has +default value 1.1. + +.. code-block:: ini + + [solver] + timestep_control = residual_ratio # Use global residual + timestep_factor = 1.1 # Constant timestep factor + +Threshold Controller +^^^^^^^^^^^^^^^^^^^^ + +An alternative adaptive strategy uses thresholds in nonlinear iterations +to adjust the timestep: + +.. code-block:: ini + + [solver] + timestep_control = threshold_nonlinear_its lower_its = 3 # Increase dt if iterations < this upper_its = 10 # Decrease dt if iterations > this timestep_factor_on_lower_its = 1.4 # Growth factor timestep_factor_on_upper_its = 0.9 # Reduction factor timestep_factor_on_failure = 0.5 # Reduction on convergence failure -PID Controller -^^^^^^^^^^^^^^ +The adjustments are less smooth than the default PID method, but the +timestep is changed less frequently. This may enable the Jacobian and +preconditioner to be used for more iterations. -An alternative adaptive strategy using a PID controller: +Output trigger +~~~~~~~~~~~~~~ + +The default behavior is to save outputs at a regular time interval, as +BOUT++ solvers do. This is desirable when performing time-dependent +simulations, but for simulations that are trying to get to steady +state a better measure of progress is reduction of the global residual +(norm of the time-derivatives of the system). .. code-block:: ini [solver] - pid_controller = true - target_its = 7 # Target number of nonlinear iterations - kP = 0.7 # Proportional gain - kI = 0.3 # Integral gain - kD = 0.2 # Derivative gain + output_trigger = residual_ratio # Trigger an output based on the ratio of residuals + output_residual_ratio = 0.5 # Output when global residual is multiplied by this -The PID controller adjusts the timestep to maintain approximately ``target_its`` -nonlinear iterations per solve, providing smoother adaptation than threshold-based -methods. +With this choice, each output has a global residual that is less than +or equal to `output_residual_ratio` times the last output global +residual. This provides a way of measuring progress to steady state +that is independent of time integration accuracy. Pseudo-Transient Continuation and Switched Evolution Relaxation ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -518,7 +562,7 @@ state. timestep = 1.0 # Initial timestep # SER parameters - pid_controller = true # Scale timesteps based on iterations + timestep_control = pid_controller # Scale timesteps based on iterations pseudo_max_ratio = 2.0 # Limit neighbor timestep ratio # Tolerances @@ -588,7 +632,7 @@ adjust ``pseudo_alpha`` depending on the nonlinearity of the system: .. code-block:: ini [solver] - pid_controller = true + timestep_control = pid_controller # Scale global timestep using PID controller target_its = 7 # Target number of nonlinear iterations kP = 0.7 # Proportional gain kI = 0.3 # Integral gain From e5bb430240546531d2180e6bbad01543bd0809da Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 15:39:06 -0800 Subject: [PATCH 219/461] snes: Add snes_global_residual scalar to dmp files Save the global residual diagnostic --- src/solver/impls/snes/snes.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 1766c94186..75798bfca5 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -1897,6 +1897,8 @@ void SNESSolver::outputVars(Options& output_options, bool save_repeat) { output_options["snes_local_residual"].assignRepeat(local_residual, "t", save_repeat, "SNESSolver"); + output_options["snes_global_residual"].assignRepeat(global_residual, "t", save_repeat, + "SNESSolver"); if (equation_form == BoutSnesEquationForm::pseudo_transient) { output_options["snes_pseudo_alpha"].assignRepeat(pseudo_alpha, "t", save_repeat, From b9b038b0243d3a54e8f89448fb0eb6664f7821d9 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Feb 2026 16:11:01 -0800 Subject: [PATCH 220/461] snes: Fix manual Uses setting `pid_controller` rather than `pid_nonlinear_its`. --- manual/sphinx/user_docs/time_integration.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/manual/sphinx/user_docs/time_integration.rst b/manual/sphinx/user_docs/time_integration.rst index 70b9c3df92..37bf9a6abf 100644 --- a/manual/sphinx/user_docs/time_integration.rst +++ b/manual/sphinx/user_docs/time_integration.rst @@ -562,7 +562,7 @@ state. timestep = 1.0 # Initial timestep # SER parameters - timestep_control = pid_controller # Scale timesteps based on iterations + timestep_control = pid_nonlinear_its # Scale timesteps based on iterations pseudo_max_ratio = 2.0 # Limit neighbor timestep ratio # Tolerances @@ -632,7 +632,7 @@ adjust ``pseudo_alpha`` depending on the nonlinearity of the system: .. code-block:: ini [solver] - timestep_control = pid_controller # Scale global timestep using PID controller + timestep_control = pid_nonlinear_its # Scale global timestep using PID controller target_its = 7 # Target number of nonlinear iterations kP = 0.7 # Proportional gain kI = 0.3 # Integral gain From 2c2402ed59c91164eaff46dee0f79386b7347e9e Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:14:36 +0000 Subject: [PATCH 221/461] [bot] Apply format changes --- src/solver/impls/snes/snes.cxx | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 75798bfca5..420adaf046 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -942,7 +942,8 @@ int SNESSolver::run() { PetscCall(VecMin(dt_vec, nullptr, ×tep)); dt = timestep; - if (output_trigger == BoutSnesOutput::fixed_time_interval && simtime + timestep >= target) { + if (output_trigger == BoutSnesOutput::fixed_time_interval + && simtime + timestep >= target) { looping = false; } } else { @@ -965,7 +966,8 @@ int SNESSolver::run() { // Set the timestep dt = timestep; - if (output_trigger == BoutSnesOutput::fixed_time_interval && simtime + dt >= target) { + if (output_trigger == BoutSnesOutput::fixed_time_interval + && simtime + dt >= target) { // Note: When the timestep is changed the preconditioner needs to be updated // => Step over the output time and interpolate if not matrix free @@ -1169,14 +1171,16 @@ int SNESSolver::run() { if (equation_form == BoutSnesEquationForm::pseudo_transient) { // Adjust pseudo_alpha to globally scale timesteps - pseudo_alpha = updateGlobalTimestep(pseudo_alpha, nl_its, recent_failure_rate, max_timestep * atol * 100); + pseudo_alpha = updateGlobalTimestep(pseudo_alpha, nl_its, recent_failure_rate, + max_timestep * atol * 100); // Adjust local timesteps PetscCall(updatePseudoTimestepping()); } else { // Adjust timestep - timestep = updateGlobalTimestep(timestep, nl_its, recent_failure_rate, max_timestep); + timestep = + updateGlobalTimestep(timestep, nl_its, recent_failure_rate, max_timestep); } if (static_cast(lin_its) / nl_its > 0.5 * maxl) { @@ -1270,7 +1274,8 @@ BoutReal SNESSolver::updateGlobalTimestep(BoutReal timestep, int nl_its, case BoutSnesTimestep::threshold_nonlinear_its: // Consider changing the timestep, based on thresholds in NL iterations - if ((nl_its <= lower_its) && (timestep < max_timestep) && (recent_failure_rate < 0.5)) { + if ((nl_its <= lower_its) && (timestep < max_timestep) + && (recent_failure_rate < 0.5)) { // Increase timestep slightly timestep *= timestep_factor_on_lower_its; return std::min(timestep, max_timestep); @@ -1422,8 +1427,8 @@ PetscErrorCode SNESSolver::updatePseudoTimestepping() { } if (count > 0) { // Adjust timestep for these quantities - BoutReal new_timestep = updatePseudoTimestep(dt_data[idx], local_residual_2d_prev[i2d], - local_residual_2d[i2d]); + BoutReal new_timestep = updatePseudoTimestep( + dt_data[idx], local_residual_2d_prev[i2d], local_residual_2d[i2d]); for (int i = 0; i != count; ++i) { dt_data[idx++] = new_timestep; } From d5ad38cb0506762b72ae513ea26341afcc51a61b Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Thu, 12 Feb 2026 00:14:37 +0000 Subject: [PATCH 222/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index a8bd688f08..3c401e4195 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -4,3 +4,4 @@ ed2117e6d6826a98b6988e2f18c0c34e408563b6 # Added by the bot 4b010b7634aee1045743be80c268d4644522cd29 a71cad2dd6ace5741a754e2ca7daacd4bb094e0e +2c2402ed59c91164eaff46dee0f79386b7347e9e From 74ce9aad802cf355ce7fdabab55e7c90cd0f5c9b Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Thu, 12 Feb 2026 10:59:13 -0800 Subject: [PATCH 223/461] snes: jacobian_persists option False by default. If true, re-uses the Jacobian and preconditioner across SNES calls. --- src/solver/impls/snes/snes.cxx | 31 ++++++++++++++++++++----------- src/solver/impls/snes/snes.hxx | 1 + 2 files changed, 21 insertions(+), 11 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 420adaf046..a05a19b02c 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -256,8 +256,10 @@ PetscErrorCode SNESSolver::FDJinitialise() { for (int i = 0; i < nlocal; ++i) { // Assume all elements in the z direction are potentially coupled - d_nnz.emplace_back(d_nnz_map3d[i].size() * mesh->LocalNz + d_nnz_map2d[i].size()); - o_nnz.emplace_back(o_nnz_map3d[i].size() * mesh->LocalNz + o_nnz_map2d[i].size()); + d_nnz.emplace_back((d_nnz_map3d[i].size() * mesh->LocalNz) + + d_nnz_map2d[i].size()); + o_nnz.emplace_back((o_nnz_map3d[i].size() * mesh->LocalNz) + + o_nnz_map2d[i].size()); } } @@ -369,7 +371,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { // Values are 0 or 1 so tolerance (1e-5) shouldn't matter PetscBool symmetric; PetscCall(MatIsSymmetric(Jfd, 1e-5, &symmetric)); - if (!symmetric) { + if (!static_cast(symmetric)) { output_warn.write("Jacobian pattern is not symmetric\n"); } } @@ -597,6 +599,10 @@ SNESSolver::SNESSolver(Options* opts) lag_jacobian((*options)["lag_jacobian"] .doc("Re-use the Jacobian this number of SNES iterations") .withDefault(50)), + jacobian_persists( + (*options)["jacobian_persists"] + .doc("Re-use Jacobian and preconditioner across nonlinear solves") + .withDefault(false)), use_coloring((*options)["use_coloring"] .doc("Use matrix coloring to calculate Jacobian?") .withDefault(true)), @@ -630,7 +636,7 @@ int SNESSolver::init() { // Get total problem size int ntmp; if (bout::globals::mpi->MPI_Allreduce(&nlocal, &ntmp, 1, MPI_INT, MPI_SUM, - BoutComm::get())) { + BoutComm::get()) != 0) { throw BoutException("MPI_Allreduce failed!"); } neq = ntmp; @@ -748,9 +754,11 @@ int SNESSolver::init() { SNESSetLagJacobian(snes, lag_jacobian); nl_its_prev = target_its; nl_its_prev2 = target_its; - SNESSetLagJacobianPersists(snes, PETSC_FALSE); - SNESSetLagPreconditionerPersists(snes, PETSC_FALSE); - SNESSetLagPreconditioner(snes, 1); // Rebuild when Jacobian is rebuilt + PetscCall( + SNESSetLagJacobianPersists(snes, static_cast(jacobian_persists))); + PetscCall(SNESSetLagPreconditionerPersists( + snes, static_cast(jacobian_persists))); + PetscCall(SNESSetLagPreconditioner(snes, 1)); // Rebuild when Jacobian is rebuilt } // Set tolerances @@ -826,10 +834,10 @@ int SNESSolver::init() { SNESType snestype; SNESGetType(snes, &snestype); output_info.write("SNES Type : {}\n", snestype); - if (ksptype) { + if (ksptype != nullptr) { output_info.write("KSP Type : {}\n", ksptype); } - if (pctype) { + if (pctype != nullptr) { output_info.write("PC Type : {}\n", pctype); } } @@ -879,8 +887,9 @@ int SNESSolver::run() { do { if ((output_trigger == BoutSnesOutput::fixed_time_interval && (simtime >= target)) || (output_trigger == BoutSnesOutput::residual_ratio - && (global_residual <= start_global_residual * output_residual_ratio))) + && (global_residual <= start_global_residual * output_residual_ratio))) { break; // Could happen if step over multiple outputs + } if (scale_vars) { // Individual variable scaling @@ -1033,7 +1042,7 @@ int SNESSolver::run() { } if (snes_failures == max_snes_failures) { - output.write("Too many SNES failures ({}). Aborting."); + output.write("Too many SNES failures ({}). Aborting.", snes_failures); return 1; } diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index 768ba33520..ca0ca50d36 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -243,6 +243,7 @@ private: bool matrix_free; ///< Use matrix free Jacobian bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? int lag_jacobian; ///< Re-use Jacobian + bool jacobian_persists; ///< Re-use Jacobian and preconditioner across nonlinear solves bool use_coloring; ///< Use matrix coloring bool jacobian_recalculated; ///< Flag set when Jacobian is recalculated From 05b7c571544c3bcb153fce67d12b9ac48947fc2d Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Thu, 12 Feb 2026 19:02:18 +0000 Subject: [PATCH 224/461] [bot] Apply format changes --- src/solver/impls/snes/snes.cxx | 3 ++- src/solver/impls/snes/snes.hxx | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index a05a19b02c..2e4744eb44 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -636,7 +636,8 @@ int SNESSolver::init() { // Get total problem size int ntmp; if (bout::globals::mpi->MPI_Allreduce(&nlocal, &ntmp, 1, MPI_INT, MPI_SUM, - BoutComm::get()) != 0) { + BoutComm::get()) + != 0) { throw BoutException("MPI_Allreduce failed!"); } neq = ntmp; diff --git a/src/solver/impls/snes/snes.hxx b/src/solver/impls/snes/snes.hxx index ca0ca50d36..40412f83b7 100644 --- a/src/solver/impls/snes/snes.hxx +++ b/src/solver/impls/snes/snes.hxx @@ -243,7 +243,7 @@ private: bool matrix_free; ///< Use matrix free Jacobian bool matrix_free_operator; ///< Use matrix free Jacobian in the operator? int lag_jacobian; ///< Re-use Jacobian - bool jacobian_persists; ///< Re-use Jacobian and preconditioner across nonlinear solves + bool jacobian_persists; ///< Re-use Jacobian and preconditioner across nonlinear solves bool use_coloring; ///< Use matrix coloring bool jacobian_recalculated; ///< Flag set when Jacobian is recalculated From 2d51378948980d1bdce739d9dd7112a77f1c137f Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Thu, 12 Feb 2026 19:02:20 +0000 Subject: [PATCH 225/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 3c401e4195..93c768a148 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,3 +5,4 @@ ed2117e6d6826a98b6988e2f18c0c34e408563b6 4b010b7634aee1045743be80c268d4644522cd29 a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 2c2402ed59c91164eaff46dee0f79386b7347e9e +05b7c571544c3bcb153fce67d12b9ac48947fc2d From 6f19419c56a3fee47b9cb90855eaaac7fcc0b0fa Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 13 Feb 2026 14:05:18 +0100 Subject: [PATCH 226/461] Cleanup included headers --- include/bout/options_io.hxx | 1 + src/solver/impls/euler/euler.cxx | 2 +- src/solver/impls/pvode/pvode.cxx | 2 +- 3 files changed, 3 insertions(+), 2 deletions(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 4fab70aea8..81addc1521 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -45,6 +45,7 @@ #include "bout/build_defines.hxx" #include "bout/generic_factory.hxx" #include "bout/options.hxx" +#include "bout/mesh.hxx" #include #include diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index ed8621a874..19dc9d7015 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -5,7 +5,7 @@ #include #include #include -#include +#include #include #include diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 95adecaec1..115ffb2056 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -37,9 +37,9 @@ #include #include #include +#include #include #include -#include #include #include // use CVSPGMR linear solver each internal step From b04a24232eeeeee29ff5e5951f2b9011f863c3d7 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 13 Feb 2026 14:05:32 +0100 Subject: [PATCH 227/461] Switch to [[maybe_unused]] --- src/solver/impls/pvode/pvode.cxx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 115ffb2056..aadb4a87c6 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -39,7 +39,6 @@ #include #include #include -#include #include #include // use CVSPGMR linear solver each internal step @@ -411,7 +410,7 @@ BoutReal PvodeSolver::run(BoutReal tout) { * RHS function **************************************************************************/ -void PvodeSolver::rhs(int UNUSED(N), BoutReal t, BoutReal* udata, BoutReal* dudata) { +void PvodeSolver::rhs([[maybe_unused]] int N, BoutReal t, BoutReal* udata, BoutReal* dudata) { TRACE("Running RHS: PvodeSolver::rhs({})", t); // Get current timestep @@ -427,7 +426,7 @@ void PvodeSolver::rhs(int UNUSED(N), BoutReal t, BoutReal* udata, BoutReal* duda save_derivs(dudata); } -void PvodeSolver::gloc(int UNUSED(N), BoutReal t, BoutReal* udata, BoutReal* dudata) { +void PvodeSolver::gloc([[maybe_unused]] int N, BoutReal t, BoutReal* udata, BoutReal* dudata) { TRACE("Running RHS: PvodeSolver::gloc({})", t); Timer timer("rhs"); @@ -468,8 +467,8 @@ void solver_gloc(integer N, BoutReal t, BoutReal* u, BoutReal* udot, void* f_dat } // Preconditioner communication function -void solver_cfn(integer UNUSED(N), BoutReal UNUSED(t), N_Vector UNUSED(u), - void* UNUSED(f_data)) { +void solver_cfn([[maybe_unused]] integer N, [[maybe_unused]] BoutReal t, [[maybe_unused]] N_Vector u, + [[maybe_unused]] void* f_data) { // doesn't do anything at the moment } From 83cf77923a4c72e44303354923021acf932b4fd2 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Fri, 13 Feb 2026 13:07:08 +0000 Subject: [PATCH 228/461] [bot] Apply format changes --- include/bout/options_io.hxx | 2 +- src/solver/impls/euler/euler.cxx | 2 +- src/solver/impls/pvode/pvode.cxx | 12 +++++++----- 3 files changed, 9 insertions(+), 7 deletions(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 81addc1521..00dd0c4cf3 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -44,8 +44,8 @@ #include "bout/build_defines.hxx" #include "bout/generic_factory.hxx" -#include "bout/options.hxx" #include "bout/mesh.hxx" +#include "bout/options.hxx" #include #include diff --git a/src/solver/impls/euler/euler.cxx b/src/solver/impls/euler/euler.cxx index 19dc9d7015..9754a68826 100644 --- a/src/solver/impls/euler/euler.cxx +++ b/src/solver/impls/euler/euler.cxx @@ -4,8 +4,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index aadb4a87c6..8ccd5f134a 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -36,8 +36,8 @@ #include #include #include -#include #include +#include #include #include @@ -410,7 +410,8 @@ BoutReal PvodeSolver::run(BoutReal tout) { * RHS function **************************************************************************/ -void PvodeSolver::rhs([[maybe_unused]] int N, BoutReal t, BoutReal* udata, BoutReal* dudata) { +void PvodeSolver::rhs([[maybe_unused]] int N, BoutReal t, BoutReal* udata, + BoutReal* dudata) { TRACE("Running RHS: PvodeSolver::rhs({})", t); // Get current timestep @@ -426,7 +427,8 @@ void PvodeSolver::rhs([[maybe_unused]] int N, BoutReal t, BoutReal* udata, BoutR save_derivs(dudata); } -void PvodeSolver::gloc([[maybe_unused]] int N, BoutReal t, BoutReal* udata, BoutReal* dudata) { +void PvodeSolver::gloc([[maybe_unused]] int N, BoutReal t, BoutReal* udata, + BoutReal* dudata) { TRACE("Running RHS: PvodeSolver::gloc({})", t); Timer timer("rhs"); @@ -467,8 +469,8 @@ void solver_gloc(integer N, BoutReal t, BoutReal* u, BoutReal* udot, void* f_dat } // Preconditioner communication function -void solver_cfn([[maybe_unused]] integer N, [[maybe_unused]] BoutReal t, [[maybe_unused]] N_Vector u, - [[maybe_unused]] void* f_data) { +void solver_cfn([[maybe_unused]] integer N, [[maybe_unused]] BoutReal t, + [[maybe_unused]] N_Vector u, [[maybe_unused]] void* f_data) { // doesn't do anything at the moment } From bc86a19972d60c640c60268e880de9883870dd2d Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Fri, 13 Feb 2026 13:07:10 +0000 Subject: [PATCH 229/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 31b33eb76b..99f926614e 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -5,3 +5,4 @@ ed2117e6d6826a98b6988e2f18c0c34e408563b6 4b010b7634aee1045743be80c268d4644522cd29 52301380586fdbf890f620c04f689b08d89a6c34 a71cad2dd6ace5741a754e2ca7daacd4bb094e0e +83cf77923a4c72e44303354923021acf932b4fd2 From 603e7d669272e71eb27c5b7b23398e5fa173e6a5 Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Wed, 18 Feb 2026 10:53:30 +0000 Subject: [PATCH 230/461] Fix CI CMake formatting. --- .github/workflows/auto-formatting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml index b3512f02cf..f54b012298 100644 --- a/.github/workflows/auto-formatting.yml +++ b/.github/workflows/auto-formatting.yml @@ -43,7 +43,7 @@ jobs: run: | pwd ls - $HOME/.local/bin/cmake-format $(find -name CMakeLists.txt) cmake/*.cmake + $HOME/.local/bin/cmake-format -i $(find -name CMakeLists.txt) cmake/*.cmake - name: Commit formatting uses: stefanzweifel/git-auto-commit-action@v7 From 0ba969553ac13c20370546cf52d1f30a972954b7 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 17 Feb 2026 15:28:08 +0000 Subject: [PATCH 231/461] tests: Set missing `mesh` members in `FakeMesh` --- tests/unit/fake_mesh.hxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 7d7326c149..4656ee0282 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -43,6 +43,9 @@ public: GlobalNx = nx; GlobalNy = ny; GlobalNz = nz; + GlobalNxNoBoundaries = nx - 2; + GlobalNyNoBoundaries = ny - 2; + GlobalNzNoBoundaries = nz; LocalNx = nx; LocalNy = ny; LocalNz = nz; From 598b1d9c4cf311d40755ba166708cfe550f049b6 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 18 Feb 2026 09:31:58 +0000 Subject: [PATCH 232/461] Fix missing headers --- src/mesh/interpolation/interpolation_z.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/interpolation/interpolation_z.cxx b/src/mesh/interpolation/interpolation_z.cxx index 8d39e6baa8..e743ec8555 100644 --- a/src/mesh/interpolation/interpolation_z.cxx +++ b/src/mesh/interpolation/interpolation_z.cxx @@ -20,8 +20,10 @@ * **************************************************************************/ +#include #include #include +#include ZInterpolation::ZInterpolation(int y_offset, Mesh* mesh, Region region_in) : localmesh(mesh == nullptr ? bout::globals::mesh : mesh), region(region_in), From c66dbb6a8f11cfd250824980478b4b19f334bbab Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Fri, 20 Feb 2026 11:44:27 +0000 Subject: [PATCH 233/461] Add cmake-format config. Dangle parentheses and format bout_add_example, bout_add_integrated_test, bout_add_mms_test, bout_handle_requires_conflicts more nicely. --- cmake-format.yaml | 48 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 cmake-format.yaml diff --git a/cmake-format.yaml b/cmake-format.yaml new file mode 100644 index 0000000000..082e7b5ef1 --- /dev/null +++ b/cmake-format.yaml @@ -0,0 +1,48 @@ +parse: + additional_commands: + bout_add_example: + pargs: 1 + kwargs: + CONFLICTS: '*' + DATA_DIRS: '*' + EXTRA_FILES: '*' + REQUIRES: '*' + SOURCES: '*' + bout_add_integrated_test: + pargs: 1 + flags: [USE_DATA_BOUT_INP, USE_RUNTEST] + kwargs: + CONFLICTS: '*' + DOWNLOAD: 1 + DOWNLOAD_NAME: 1 + EXECUTABLE_NAME: 1 + EXTRA_DEPENDS: '*' + EXTRA_FILES: '*' + PROCESSORS: 1 + REQUIRES: '*' + SOURCES: '*' + TESTARGS: '*' + bout_add_mms_test: + pargs: 1 + flags: [USE_DATA_BOUT_INP, USE_RUNTEST] + kwargs: + CONFLICTS: '*' + DOWNLOAD: 1 + DOWNLOAD_NAME: 1 + EXECUTABLE_NAME: 1 + EXTRA_DEPENDS: '*' + EXTRA_FILES: '*' + PROCESSORS: 1 + REQUIRES: '*' + SOURCES: '*' + TESTARGS: '*' + bout_handle_requires_conflicts: + pargs: 2 + kwargs: + CONFLICTS: '*' + REQUIRES: '*' +format: + dangle_parens: true +markup: + # markup messes up comments - disable it + enable_markup: false \ No newline at end of file From 17ac13c28aa3b34a0e46dbe87bb3874f6b25e706 Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Fri, 20 Feb 2026 12:02:38 +0000 Subject: [PATCH 234/461] Apply CMake formatting. --- CMakeLists.txt | 1055 +++++++++-------- cmake/BOUT++functions.cmake | 167 ++- cmake/BuildType.cmake | 17 +- cmake/CorrectWindowsPaths.cmake | 14 +- cmake/EnableCXXWarningIfSupport.cmake | 22 +- cmake/FindBash.cmake | 21 +- cmake/FindClangFormat.cmake | 24 +- cmake/FindCython.cmake | 12 +- cmake/FindFFTW.cmake | 72 +- cmake/FindHYPRE.cmake | 58 +- cmake/FindLibuuid.cmake | 31 +- cmake/FindNumpy.cmake | 45 +- cmake/FindPETSc.cmake | 699 +++++++---- cmake/FindPackageMultipass.cmake | 163 +-- cmake/FindSLEPc.cmake | 156 ++- cmake/FindSUNDIALS.cmake | 131 +- cmake/FindScoreP.cmake | 100 +- cmake/FindSphinx.cmake | 14 +- cmake/FindnetCDF.cmake | 130 +- cmake/FindnetCDFCxx.cmake | 109 +- cmake/GenerateDateTimeFile.cmake | 7 +- cmake/GetGitRevisionDescription.cmake | 235 ++-- cmake/ResolveCompilerPaths.cmake | 121 +- cmake/Sanitizers.cmake | 96 +- cmake/SetupBOUTThirdParty.cmake | 325 +++-- cmake/SetupCompilers.cmake | 64 +- examples/6field-simple/CMakeLists.txt | 8 +- .../IMEX/advection-diffusion/CMakeLists.txt | 2 +- .../IMEX/advection-reaction/CMakeLists.txt | 8 +- examples/IMEX/diffusion-nl/CMakeLists.txt | 2 +- .../IMEX/drift-wave-constraint/CMakeLists.txt | 2 +- examples/IMEX/drift-wave/CMakeLists.txt | 2 +- examples/blob2d-laplacexz/CMakeLists.txt | 2 +- examples/blob2d-outerloop/CMakeLists.txt | 2 +- examples/blob2d/CMakeLists.txt | 8 +- .../advection/CMakeLists.txt | 11 +- examples/boutpp/CMakeLists.txt | 2 +- examples/conducting-wall-mode/CMakeLists.txt | 8 +- examples/conduction-snb/CMakeLists.txt | 17 +- examples/conduction/CMakeLists.txt | 8 +- .../constraints/alfven-wave/CMakeLists.txt | 9 +- .../constraints/laplace-dae/CMakeLists.txt | 8 +- examples/dalf3/CMakeLists.txt | 9 +- examples/eigen-box/CMakeLists.txt | 8 +- examples/elm-pb-outerloop/CMakeLists.txt | 8 +- examples/elm-pb/CMakeLists.txt | 13 +- examples/fci-wave/CMakeLists.txt | 8 +- .../finite-volume/diffusion/CMakeLists.txt | 8 +- examples/finite-volume/fluid/CMakeLists.txt | 8 +- examples/finite-volume/test/CMakeLists.txt | 2 +- examples/gas-compress/CMakeLists.txt | 8 +- examples/gyro-gem/CMakeLists.txt | 8 +- examples/hasegawa-wakatani-3d/CMakeLists.txt | 2 +- examples/hasegawa-wakatani/CMakeLists.txt | 3 +- examples/invertable_operator/CMakeLists.txt | 8 +- examples/laplacexy/alfven-wave/CMakeLists.txt | 8 +- .../laplacexy/laplace_perp/CMakeLists.txt | 10 +- examples/laplacexy/simple/CMakeLists.txt | 5 +- examples/monitor-newapi/CMakeLists.txt | 2 +- examples/orszag-tang/CMakeLists.txt | 8 +- examples/preconditioning/wave/CMakeLists.txt | 2 +- examples/staggered_grid/CMakeLists.txt | 8 +- examples/subsampling/CMakeLists.txt | 9 +- examples/wave-slab/CMakeLists.txt | 8 +- externalpackages/PVODE/CMakeLists.txt | 73 +- manual/CMakeLists.txt | 25 +- tests/MMS/CMakeLists.txt | 2 +- tests/MMS/advection/arakawa/CMakeLists.txt | 6 +- tests/MMS/advection/central/CMakeLists.txt | 6 +- tests/MMS/advection/upwind/CMakeLists.txt | 6 +- tests/MMS/advection/weno3/CMakeLists.txt | 6 +- tests/MMS/bracket/CMakeLists.txt | 6 +- tests/MMS/derivatives3/CMakeLists.txt | 6 +- tests/MMS/diffusion/CMakeLists.txt | 8 +- tests/MMS/diffusion2/CMakeLists.txt | 10 +- tests/MMS/hw/CMakeLists.txt | 6 +- tests/MMS/laplace/CMakeLists.txt | 8 +- tests/MMS/shiftedmetricinterp/CMakeLists.txt | 6 +- tests/MMS/spatial/advection/CMakeLists.txt | 6 +- tests/MMS/spatial/d2dx2/CMakeLists.txt | 6 +- tests/MMS/spatial/d2dz2/CMakeLists.txt | 6 +- tests/MMS/spatial/diffusion/CMakeLists.txt | 3 +- tests/MMS/spatial/fci/CMakeLists.txt | 6 +- tests/MMS/time-petsc/CMakeLists.txt | 7 +- tests/MMS/time/CMakeLists.txt | 6 +- tests/MMS/upwinding3/CMakeLists.txt | 6 +- tests/MMS/wave-1d-y/CMakeLists.txt | 6 +- tests/MMS/wave-1d/CMakeLists.txt | 6 +- .../integrated/test-backtrace/CMakeLists.txt | 3 +- tests/integrated/test-beuler/CMakeLists.txt | 7 +- .../CMakeLists.txt | 5 +- tests/integrated/test-boutpp/CMakeLists.txt | 2 +- .../collect-staggered/CMakeLists.txt | 6 +- .../test-boutpp/collect/CMakeLists.txt | 3 +- .../test-boutpp/legacy-model/CMakeLists.txt | 6 +- .../test-boutpp/mms-ddz/CMakeLists.txt | 6 +- .../test-boutpp/print/CMakeLists.txt | 3 +- .../test-boutpp/simple-model/CMakeLists.txt | 3 +- .../test-boutpp/slicing/CMakeLists.txt | 3 +- tests/integrated/test-collect/CMakeLists.txt | 8 +- .../test-command-args/CMakeLists.txt | 5 +- .../test-communications/CMakeLists.txt | 11 +- .../CMakeLists.txt | 8 +- tests/integrated/test-cyclic/CMakeLists.txt | 8 +- .../test-datafilefacade/CMakeLists.txt | 8 +- tests/integrated/test-delp2/CMakeLists.txt | 8 +- .../CMakeLists.txt | 8 +- .../test-drift-instability/CMakeLists.txt | 8 +- .../test-fci-boundary/CMakeLists.txt | 25 +- tests/integrated/test-fci-mpi/CMakeLists.txt | 9 +- .../test-fieldgroupComm/CMakeLists.txt | 8 +- .../CMakeLists.txt | 14 +- tests/integrated/test-griddata/CMakeLists.txt | 5 +- tests/integrated/test-gyro/CMakeLists.txt | 8 +- tests/integrated/test-initial/CMakeLists.txt | 8 +- .../CMakeLists.txt | 5 +- .../test-interpolate-z/CMakeLists.txt | 8 +- .../test-interpolate/CMakeLists.txt | 8 +- .../test-invertable-operator/CMakeLists.txt | 8 +- tests/integrated/test-invpar/CMakeLists.txt | 8 +- .../test-laplace-hypre3d/CMakeLists.txt | 12 +- .../test-laplace-petsc3d/CMakeLists.txt | 12 +- tests/integrated/test-laplace/CMakeLists.txt | 8 +- .../test-laplacexy-fv/CMakeLists.txt | 11 +- .../test-laplacexy-short/CMakeLists.txt | 11 +- .../integrated/test-laplacexy/CMakeLists.txt | 8 +- .../test-laplacexy2-hypre/CMakeLists.txt | 8 +- .../integrated/test-laplacexz/CMakeLists.txt | 8 +- .../test-multigrid_laplace/CMakeLists.txt | 8 +- .../test-naulin-laplace/CMakeLists.txt | 8 +- .../test-options-netcdf/CMakeLists.txt | 8 +- .../test-petsc_laplace/CMakeLists.txt | 11 +- .../CMakeLists.txt | 14 +- .../integrated/test-restart-io/CMakeLists.txt | 8 +- .../integrated/test-restarting/CMakeLists.txt | 8 +- .../test-slepc-solver/CMakeLists.txt | 8 +- tests/integrated/test-smooth/CMakeLists.txt | 8 +- tests/integrated/test-snb/CMakeLists.txt | 5 +- tests/integrated/test-solver/CMakeLists.txt | 4 +- tests/integrated/test-squash/CMakeLists.txt | 8 +- .../test-stopCheck-file/CMakeLists.txt | 13 +- .../integrated/test-stopCheck/CMakeLists.txt | 8 +- .../test-twistshift-staggered/CMakeLists.txt | 8 +- .../integrated/test-twistshift/CMakeLists.txt | 8 +- tests/integrated/test-vec/CMakeLists.txt | 8 +- .../test-yupdown-weights/CMakeLists.txt | 8 +- tests/integrated/test-yupdown/CMakeLists.txt | 8 +- tests/unit/CMakeLists.txt | 210 ++-- tools/pylib/_boutpp_build/CMakeLists.txt | 108 +- 149 files changed, 2979 insertions(+), 2181 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 479b13b668..c05bad29db 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,10 +15,15 @@ if(${CMAKE_VERSION} VERSION_GREATER_EQUAL 3.22) cmake_policy(SET CMP0127 NEW) endif() -if ("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") - option(BOUT_ALLOW_INSOURCE_BUILD "Whether BOUT++ should really allow to build in source." OFF) - if (NOT ${BOUT_ALLOW_INSOURCE_BUILD}) - message(FATAL_ERROR "BOUT++ does not recommend in source builds. Try building out of source, e.g. with `cmake -S . -B build` or set -DBOUT_ALLOW_INSOURCE_BUILD=ON - but things may break!") +if("${CMAKE_CURRENT_BINARY_DIR}" STREQUAL "${CMAKE_CURRENT_SOURCE_DIR}") + option(BOUT_ALLOW_INSOURCE_BUILD + "Whether BOUT++ should really allow to build in source." OFF + ) + if(NOT ${BOUT_ALLOW_INSOURCE_BUILD}) + message( + FATAL_ERROR + "BOUT++ does not recommend in source builds. Try building out of source, e.g. with `cmake -S . -B build` or set -DBOUT_ALLOW_INSOURCE_BUILD=ON - but things may break!" + ) endif() endif() @@ -29,41 +34,53 @@ set(_bout_previous_version "5.2.0") set(_bout_next_version "5.2.1") execute_process( COMMAND "git" describe --tags --match=v${_bout_previous_version} - COMMAND sed -e s/${_bout_previous_version}-/${_bout_next_version}.dev/ -e s/-/+/ - RESULTS_VARIABLE error_codes + COMMAND sed -e s/${_bout_previous_version}-/${_bout_next_version}.dev/ -e + s/-/+/ RESULTS_VARIABLE error_codes OUTPUT_VARIABLE BOUT_FULL_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) +) foreach(error_code ${error_codes}) - if (NOT ${error_code} STREQUAL 0) + if(NOT ${error_code} STREQUAL 0) set(BOUT_FULL_VERSION ${_bout_next_version}) endif() endforeach() # Remove leading "v" string(REGEX REPLACE "^v(.*)" "\\1" BOUT_FULL_VERSION ${BOUT_FULL_VERSION}) # Remove trailing tag -string(REGEX REPLACE "^([0-9]+\.[0-9]+\.[0-9]+)\..*" "\\1" BOUT_CMAKE_ACCEPTABLE_VERSION ${BOUT_FULL_VERSION}) +string(REGEX REPLACE "^([0-9]+\.[0-9]+\.[0-9]+)\..*" "\\1" + BOUT_CMAKE_ACCEPTABLE_VERSION ${BOUT_FULL_VERSION} +) # Get the trailing tag -string(REGEX REPLACE "^[0-9]+\.[0-9]+\.[0-9]+\.(.*)" "\\1" BOUT_VERSION_TAG ${BOUT_FULL_VERSION}) +string(REGEX REPLACE "^[0-9]+\.[0-9]+\.[0-9]+\.(.*)" "\\1" BOUT_VERSION_TAG + ${BOUT_FULL_VERSION} +) message(STATUS "Configuring BOUT++ version ${BOUT_FULL_VERSION}") -project(BOUT++ +project( + BOUT++ DESCRIPTION "Fluid PDE solver framework" VERSION ${BOUT_CMAKE_ACCEPTABLE_VERSION} - LANGUAGES CXX) + LANGUAGES CXX +) include(CMakeDependentOption) option(BUILD_SHARED_LIBS "Build shared libs" ON) # Override default -option(INSTALL_GTEST "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" OFF) +option( + INSTALL_GTEST + "Enable installation of googletest. (Projects embedding googletest may want to turn this OFF.)" + OFF +) set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake" ${CMAKE_MODULE_PATH}) include(BOUT++functions) -option(BOUT_UPDATE_GIT_SUBMODULE "Check submodules are up-to-date during build" ON) +option(BOUT_UPDATE_GIT_SUBMODULE "Check submodules are up-to-date during build" + ON +) # Adapted from https://cliutils.gitlab.io/modern-cmake/chapters/projects/submodule.html # Update submodules as needed function(bout_update_submodules) @@ -73,337 +90,363 @@ function(bout_update_submodules) find_package(Git QUIET) if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git") message(STATUS "Submodule update") - execute_process(COMMAND ${GIT_EXECUTABLE} -c submodule.recurse=false submodule update --init --recursive + execute_process( + COMMAND ${GIT_EXECUTABLE} -c submodule.recurse=false submodule update + --init --recursive WORKING_DIRECTORY ${PROJECT_SOURCE_DIR} - RESULT_VARIABLE GIT_SUBMOD_RESULT) + RESULT_VARIABLE GIT_SUBMOD_RESULT + ) if(NOT GIT_SUBMOD_RESULT EQUAL "0") - message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules") + message( + FATAL_ERROR + "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules" + ) endif() endif() endfunction() set(BOUT_SOURCES - ./include/bout/adios_object.hxx - ./include/bout/array.hxx - ./include/bout/assert.hxx - ./include/bout/boundary_factory.hxx - ./include/bout/boundary_op.hxx - ./include/bout/boundary_region.hxx - ./include/bout/boundary_standard.hxx - ./include/bout/bout.hxx - ./include/bout/bout_enum_class.hxx - ./include/bout/bout_types.hxx - ./include/bout/build_config.hxx - ./include/bout/boutcomm.hxx - ./include/bout/boutexception.hxx - ./include/bout/caliper_wrapper.hxx - ./include/bout/constants.hxx - ./include/bout/coordinates.hxx - ./include/bout/coordinates_accessor.hxx - ./include/bout/cyclic_reduction.hxx - ./include/bout/dcomplex.hxx - ./include/bout/deriv_store.hxx - ./include/bout/derivs.hxx - ./include/bout/difops.hxx - ./include/bout/expr.hxx - ./include/bout/fft.hxx - ./include/bout/field.hxx - ./include/bout/field2d.hxx - ./include/bout/field3d.hxx - ./include/bout/field_accessor.hxx - ./include/bout/field_data.hxx - ./include/bout/field_factory.hxx - ./include/bout/fieldgroup.hxx - ./include/bout/fieldperp.hxx - ./include/bout/fv_ops.hxx - ./include/bout/generic_factory.hxx - ./include/bout/globalfield.hxx - ./include/bout/globalindexer.hxx - ./include/bout/globals.hxx - ./include/bout/griddata.hxx - ./include/bout/gyro_average.hxx - ./include/bout/hypre_interface.hxx - ./include/bout/index_derivs.hxx - ./include/bout/index_derivs_interface.hxx - ./include/bout/initialprofiles.hxx - ./include/bout/interpolation.hxx - ./include/bout/interpolation_xz.hxx - ./include/bout/interpolation_z.hxx - ./include/bout/invert/laplacexy.hxx - ./include/bout/invert/laplacexz.hxx - ./include/bout/invert_laplace.hxx - ./include/bout/invert_parderiv.hxx - ./include/bout/invert_pardiv.hxx - ./include/bout/invertable_operator.hxx - ./include/bout/lapack_routines.hxx - ./include/bout/macro_for_each.hxx - ./include/bout/mask.hxx - ./include/bout/mesh.hxx - ./include/bout/monitor.hxx - ./include/bout/mpi_wrapper.hxx - ./include/bout/msg_stack.hxx - ./include/bout/multiostream.hxx - ./include/bout/openmpwrap.hxx - ./include/bout/operatorstencil.hxx - ./include/bout/options.hxx - ./include/bout/options_io.hxx - ./include/bout/optionsreader.hxx - ./include/bout/output.hxx - ./include/bout/output_bout_types.hxx - ./include/bout/parallel_boundary_op.hxx - ./include/bout/parallel_boundary_region.hxx - ./include/bout/paralleltransform.hxx - ./include/bout/petsc_interface.hxx - ./include/bout/petsclib.hxx - ./include/bout/physicsmodel.hxx - ./include/bout/rajalib.hxx - ./include/bout/region.hxx - ./include/bout/rkscheme.hxx - ./include/bout/rvec.hxx - ./include/bout/scorepwrapper.hxx - ./include/bout/single_index_ops.hxx - ./include/bout/slepclib.hxx - ./include/bout/smoothing.hxx - ./include/bout/snb.hxx - ./include/bout/solver.hxx - ./include/bout/solverfactory.hxx - ./include/bout/sourcex.hxx - ./include/bout/stencils.hxx - ./include/bout/sundials_backports.hxx - ./include/bout/surfaceiter.hxx - ./include/bout/sys/expressionparser.hxx - ./include/bout/sys/generator_context.hxx - ./include/bout/sys/gettext.hxx - ./include/bout/sys/range.hxx - ./include/bout/sys/timer.hxx - ./include/bout/sys/type_name.hxx - ./include/bout/sys/uncopyable.hxx - ./include/bout/sys/uuid.h - ./include/bout/sys/variant.hxx - ./include/bout/template_combinations.hxx - ./include/bout/traits.hxx - ./include/bout/unused.hxx - ./include/bout/utils.hxx - ./include/bout/vecops.hxx - ./include/bout/vector2d.hxx - ./include/bout/vector3d.hxx - ./include/bout/where.hxx - ./src/bout++.cxx - ./src/bout++-time.hxx - ./src/field/field.cxx - ./src/field/field2d.cxx - ./src/field/field3d.cxx - ./src/field/field_data.cxx - ./src/field/field_factory.cxx - ./src/field/fieldgenerators.cxx - ./src/field/fieldgenerators.hxx - ./src/field/fieldgroup.cxx - ./src/field/fieldperp.cxx - ./src/field/generated_fieldops.cxx - ./src/field/globalfield.cxx - ./src/field/initialprofiles.cxx - ./src/field/vecops.cxx - ./src/field/vector2d.cxx - ./src/field/vector3d.cxx - ./src/field/where.cxx - ./src/invert/fft_fftw.cxx - ./src/invert/lapack_routines.cxx - ./src/invert/laplace/impls/cyclic/cyclic_laplace.cxx - ./src/invert/laplace/impls/cyclic/cyclic_laplace.hxx - ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx - ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx - ./src/invert/laplace/impls/multigrid/multigrid_alg.cxx - ./src/invert/laplace/impls/multigrid/multigrid_laplace.cxx - ./src/invert/laplace/impls/multigrid/multigrid_laplace.hxx - ./src/invert/laplace/impls/multigrid/multigrid_solver.cxx - ./src/invert/laplace/impls/naulin/naulin_laplace.cxx - ./src/invert/laplace/impls/naulin/naulin_laplace.hxx - ./src/invert/laplace/impls/pcr/pcr.cxx - ./src/invert/laplace/impls/pcr/pcr.hxx - ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx - ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx - ./src/invert/laplace/impls/petsc/petsc_laplace.cxx - ./src/invert/laplace/impls/petsc/petsc_laplace.hxx - ./src/invert/laplace/impls/petsc3damg/petsc3damg.cxx - ./src/invert/laplace/impls/petsc3damg/petsc3damg.hxx - ./src/invert/laplace/impls/serial_band/serial_band.cxx - ./src/invert/laplace/impls/serial_band/serial_band.hxx - ./src/invert/laplace/impls/serial_tri/serial_tri.cxx - ./src/invert/laplace/impls/serial_tri/serial_tri.hxx - ./src/invert/laplace/impls/spt/spt.cxx - ./src/invert/laplace/impls/spt/spt.hxx - ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.cxx - ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.hxx - ./src/invert/laplace/invert_laplace.cxx - ./src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx - ./src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx - ./src/invert/laplacexy/impls/petsc/laplacexy-petsc.cxx - ./src/invert/laplacexy/impls/petsc/laplacexy-petsc.hxx - ./src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.cxx - ./src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.hxx - ./src/invert/laplacexy/laplacexy.cxx - ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx - ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx - ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx - ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.hxx - ./src/invert/laplacexz/laplacexz.cxx - ./src/invert/parderiv/impls/cyclic/cyclic.cxx - ./src/invert/parderiv/impls/cyclic/cyclic.hxx - ./src/invert/parderiv/invert_parderiv.cxx - ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx - ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.hxx - ./src/invert/pardiv/invert_pardiv.cxx - ./src/mesh/boundary_factory.cxx - ./src/mesh/boundary_region.cxx - ./src/mesh/boundary_standard.cxx - ./src/mesh/coordinates.cxx - ./src/mesh/coordinates_accessor.cxx - ./src/mesh/data/gridfromfile.cxx - ./src/mesh/data/gridfromoptions.cxx - ./src/mesh/difops.cxx - ./src/mesh/fv_ops.cxx - ./src/mesh/impls/bout/boutmesh.cxx - ./src/mesh/impls/bout/boutmesh.hxx - ./src/mesh/index_derivs.cxx - ./src/mesh/interpolation_xz.cxx - ./src/mesh/interpolation/bilinear_xz.cxx - ./src/mesh/interpolation/hermite_spline_xz.cxx - ./src/mesh/interpolation/hermite_spline_z.cxx - ./src/mesh/interpolation/interpolation_z.cxx - ./src/mesh/interpolation/lagrange_4pt_xz.cxx - ./src/mesh/interpolation/monotonic_hermite_spline_xz.cxx - ./src/mesh/invert3x3.hxx - ./src/mesh/mesh.cxx - ./src/mesh/parallel/fci.cxx - ./src/mesh/parallel/fci.hxx - ./src/mesh/parallel/identity.cxx - ./src/mesh/parallel/shiftedmetric.cxx - ./src/mesh/parallel/shiftedmetricinterp.cxx - ./src/mesh/parallel/shiftedmetricinterp.hxx - ./src/mesh/parallel_boundary_op.cxx - ./src/mesh/parallel_boundary_region.cxx - ./src/mesh/surfaceiter.cxx - ./src/physics/gyro_average.cxx - ./src/physics/physicsmodel.cxx - ./src/physics/smoothing.cxx - ./src/physics/snb.cxx - ./src/physics/sourcex.cxx - ./src/solver/impls/adams_bashforth/adams_bashforth.cxx - ./src/solver/impls/adams_bashforth/adams_bashforth.hxx - ./src/solver/impls/arkode/arkode.cxx - ./src/solver/impls/arkode/arkode.hxx - ./src/solver/impls/cvode/cvode.cxx - ./src/solver/impls/cvode/cvode.hxx - ./src/solver/impls/euler/euler.cxx - ./src/solver/impls/euler/euler.hxx - ./src/solver/impls/ida/ida.cxx - ./src/solver/impls/ida/ida.hxx - ./src/solver/impls/imex-bdf2/imex-bdf2.cxx - ./src/solver/impls/imex-bdf2/imex-bdf2.hxx - ./src/solver/impls/petsc/petsc.cxx - ./src/solver/impls/petsc/petsc.hxx - ./src/solver/impls/power/power.cxx - ./src/solver/impls/power/power.hxx - ./src/solver/impls/pvode/pvode.cxx - ./src/solver/impls/pvode/pvode.hxx - ./src/solver/impls/rk3-ssp/rk3-ssp.cxx - ./src/solver/impls/rk3-ssp/rk3-ssp.hxx - ./src/solver/impls/rk4/rk4.cxx - ./src/solver/impls/rk4/rk4.hxx - ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.cxx - ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.hxx - ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.cxx - ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.hxx - ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.cxx - ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.hxx - ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.cxx - ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.hxx - ./src/solver/impls/rkgeneric/rkgeneric.cxx - ./src/solver/impls/rkgeneric/rkgeneric.hxx - ./src/solver/impls/rkgeneric/rkscheme.cxx - ./src/solver/impls/slepc/slepc.cxx - ./src/solver/impls/slepc/slepc.hxx - ./src/solver/impls/snes/snes.cxx - ./src/solver/impls/snes/snes.hxx - ./src/solver/impls/split-rk/split-rk.cxx - ./src/solver/impls/split-rk/split-rk.hxx - ./src/solver/solver.cxx - ./src/sys/adios_object.cxx - ./src/sys/bout_types.cxx - ./src/sys/boutcomm.cxx - ./src/sys/boutexception.cxx - ./src/sys/derivs.cxx - ./src/sys/expressionparser.cxx - ./src/sys/generator_context.cxx - ./include/bout/hyprelib.hxx - ./src/sys/hyprelib.cxx - ./src/sys/msg_stack.cxx - ./src/sys/options.cxx - ./src/sys/options/optionparser.hxx - ./src/sys/options/options_ini.cxx - ./src/sys/options/options_ini.hxx - ./src/sys/options/options_io.cxx - ./src/sys/options/options_netcdf.cxx - ./src/sys/options/options_netcdf.hxx - ./src/sys/options/options_adios.cxx - ./src/sys/options/options_adios.hxx - ./src/sys/optionsreader.cxx - ./src/sys/output.cxx - ./src/sys/petsclib.cxx - ./src/sys/range.cxx - ./src/sys/slepclib.cxx - ./src/sys/timer.cxx - ./src/sys/type_name.cxx - ./src/sys/utils.cxx - ${CMAKE_CURRENT_BINARY_DIR}/include/bout/revision.hxx - ${CMAKE_CURRENT_BINARY_DIR}/include/bout/version.hxx - ) - + ./include/bout/adios_object.hxx + ./include/bout/array.hxx + ./include/bout/assert.hxx + ./include/bout/boundary_factory.hxx + ./include/bout/boundary_op.hxx + ./include/bout/boundary_region.hxx + ./include/bout/boundary_standard.hxx + ./include/bout/bout.hxx + ./include/bout/bout_enum_class.hxx + ./include/bout/bout_types.hxx + ./include/bout/build_config.hxx + ./include/bout/boutcomm.hxx + ./include/bout/boutexception.hxx + ./include/bout/caliper_wrapper.hxx + ./include/bout/constants.hxx + ./include/bout/coordinates.hxx + ./include/bout/coordinates_accessor.hxx + ./include/bout/cyclic_reduction.hxx + ./include/bout/dcomplex.hxx + ./include/bout/deriv_store.hxx + ./include/bout/derivs.hxx + ./include/bout/difops.hxx + ./include/bout/expr.hxx + ./include/bout/fft.hxx + ./include/bout/field.hxx + ./include/bout/field2d.hxx + ./include/bout/field3d.hxx + ./include/bout/field_accessor.hxx + ./include/bout/field_data.hxx + ./include/bout/field_factory.hxx + ./include/bout/fieldgroup.hxx + ./include/bout/fieldperp.hxx + ./include/bout/fv_ops.hxx + ./include/bout/generic_factory.hxx + ./include/bout/globalfield.hxx + ./include/bout/globalindexer.hxx + ./include/bout/globals.hxx + ./include/bout/griddata.hxx + ./include/bout/gyro_average.hxx + ./include/bout/hypre_interface.hxx + ./include/bout/index_derivs.hxx + ./include/bout/index_derivs_interface.hxx + ./include/bout/initialprofiles.hxx + ./include/bout/interpolation.hxx + ./include/bout/interpolation_xz.hxx + ./include/bout/interpolation_z.hxx + ./include/bout/invert/laplacexy.hxx + ./include/bout/invert/laplacexz.hxx + ./include/bout/invert_laplace.hxx + ./include/bout/invert_parderiv.hxx + ./include/bout/invert_pardiv.hxx + ./include/bout/invertable_operator.hxx + ./include/bout/lapack_routines.hxx + ./include/bout/macro_for_each.hxx + ./include/bout/mask.hxx + ./include/bout/mesh.hxx + ./include/bout/monitor.hxx + ./include/bout/mpi_wrapper.hxx + ./include/bout/msg_stack.hxx + ./include/bout/multiostream.hxx + ./include/bout/openmpwrap.hxx + ./include/bout/operatorstencil.hxx + ./include/bout/options.hxx + ./include/bout/options_io.hxx + ./include/bout/optionsreader.hxx + ./include/bout/output.hxx + ./include/bout/output_bout_types.hxx + ./include/bout/parallel_boundary_op.hxx + ./include/bout/parallel_boundary_region.hxx + ./include/bout/paralleltransform.hxx + ./include/bout/petsc_interface.hxx + ./include/bout/petsclib.hxx + ./include/bout/physicsmodel.hxx + ./include/bout/rajalib.hxx + ./include/bout/region.hxx + ./include/bout/rkscheme.hxx + ./include/bout/rvec.hxx + ./include/bout/scorepwrapper.hxx + ./include/bout/single_index_ops.hxx + ./include/bout/slepclib.hxx + ./include/bout/smoothing.hxx + ./include/bout/snb.hxx + ./include/bout/solver.hxx + ./include/bout/solverfactory.hxx + ./include/bout/sourcex.hxx + ./include/bout/stencils.hxx + ./include/bout/sundials_backports.hxx + ./include/bout/surfaceiter.hxx + ./include/bout/sys/expressionparser.hxx + ./include/bout/sys/generator_context.hxx + ./include/bout/sys/gettext.hxx + ./include/bout/sys/range.hxx + ./include/bout/sys/timer.hxx + ./include/bout/sys/type_name.hxx + ./include/bout/sys/uncopyable.hxx + ./include/bout/sys/uuid.h + ./include/bout/sys/variant.hxx + ./include/bout/template_combinations.hxx + ./include/bout/traits.hxx + ./include/bout/unused.hxx + ./include/bout/utils.hxx + ./include/bout/vecops.hxx + ./include/bout/vector2d.hxx + ./include/bout/vector3d.hxx + ./include/bout/where.hxx + ./src/bout++.cxx + ./src/bout++-time.hxx + ./src/field/field.cxx + ./src/field/field2d.cxx + ./src/field/field3d.cxx + ./src/field/field_data.cxx + ./src/field/field_factory.cxx + ./src/field/fieldgenerators.cxx + ./src/field/fieldgenerators.hxx + ./src/field/fieldgroup.cxx + ./src/field/fieldperp.cxx + ./src/field/generated_fieldops.cxx + ./src/field/globalfield.cxx + ./src/field/initialprofiles.cxx + ./src/field/vecops.cxx + ./src/field/vector2d.cxx + ./src/field/vector3d.cxx + ./src/field/where.cxx + ./src/invert/fft_fftw.cxx + ./src/invert/lapack_routines.cxx + ./src/invert/laplace/impls/cyclic/cyclic_laplace.cxx + ./src/invert/laplace/impls/cyclic/cyclic_laplace.hxx + ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx + ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.hxx + ./src/invert/laplace/impls/multigrid/multigrid_alg.cxx + ./src/invert/laplace/impls/multigrid/multigrid_laplace.cxx + ./src/invert/laplace/impls/multigrid/multigrid_laplace.hxx + ./src/invert/laplace/impls/multigrid/multigrid_solver.cxx + ./src/invert/laplace/impls/naulin/naulin_laplace.cxx + ./src/invert/laplace/impls/naulin/naulin_laplace.hxx + ./src/invert/laplace/impls/pcr/pcr.cxx + ./src/invert/laplace/impls/pcr/pcr.hxx + ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx + ./src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx + ./src/invert/laplace/impls/petsc/petsc_laplace.cxx + ./src/invert/laplace/impls/petsc/petsc_laplace.hxx + ./src/invert/laplace/impls/petsc3damg/petsc3damg.cxx + ./src/invert/laplace/impls/petsc3damg/petsc3damg.hxx + ./src/invert/laplace/impls/serial_band/serial_band.cxx + ./src/invert/laplace/impls/serial_band/serial_band.hxx + ./src/invert/laplace/impls/serial_tri/serial_tri.cxx + ./src/invert/laplace/impls/serial_tri/serial_tri.hxx + ./src/invert/laplace/impls/spt/spt.cxx + ./src/invert/laplace/impls/spt/spt.hxx + ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.cxx + ./src/invert/laplace/impls/hypre3d/hypre3d_laplace.hxx + ./src/invert/laplace/invert_laplace.cxx + ./src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx + ./src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx + ./src/invert/laplacexy/impls/petsc/laplacexy-petsc.cxx + ./src/invert/laplacexy/impls/petsc/laplacexy-petsc.hxx + ./src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.cxx + ./src/invert/laplacexy/impls/petsc2/laplacexy-petsc2.hxx + ./src/invert/laplacexy/laplacexy.cxx + ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.cxx + ./src/invert/laplacexz/impls/cyclic/laplacexz-cyclic.hxx + ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.cxx + ./src/invert/laplacexz/impls/petsc/laplacexz-petsc.hxx + ./src/invert/laplacexz/laplacexz.cxx + ./src/invert/parderiv/impls/cyclic/cyclic.cxx + ./src/invert/parderiv/impls/cyclic/cyclic.hxx + ./src/invert/parderiv/invert_parderiv.cxx + ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.cxx + ./src/invert/pardiv/impls/cyclic/pardiv_cyclic.hxx + ./src/invert/pardiv/invert_pardiv.cxx + ./src/mesh/boundary_factory.cxx + ./src/mesh/boundary_region.cxx + ./src/mesh/boundary_standard.cxx + ./src/mesh/coordinates.cxx + ./src/mesh/coordinates_accessor.cxx + ./src/mesh/data/gridfromfile.cxx + ./src/mesh/data/gridfromoptions.cxx + ./src/mesh/difops.cxx + ./src/mesh/fv_ops.cxx + ./src/mesh/impls/bout/boutmesh.cxx + ./src/mesh/impls/bout/boutmesh.hxx + ./src/mesh/index_derivs.cxx + ./src/mesh/interpolation_xz.cxx + ./src/mesh/interpolation/bilinear_xz.cxx + ./src/mesh/interpolation/hermite_spline_xz.cxx + ./src/mesh/interpolation/hermite_spline_z.cxx + ./src/mesh/interpolation/interpolation_z.cxx + ./src/mesh/interpolation/lagrange_4pt_xz.cxx + ./src/mesh/interpolation/monotonic_hermite_spline_xz.cxx + ./src/mesh/invert3x3.hxx + ./src/mesh/mesh.cxx + ./src/mesh/parallel/fci.cxx + ./src/mesh/parallel/fci.hxx + ./src/mesh/parallel/identity.cxx + ./src/mesh/parallel/shiftedmetric.cxx + ./src/mesh/parallel/shiftedmetricinterp.cxx + ./src/mesh/parallel/shiftedmetricinterp.hxx + ./src/mesh/parallel_boundary_op.cxx + ./src/mesh/parallel_boundary_region.cxx + ./src/mesh/surfaceiter.cxx + ./src/physics/gyro_average.cxx + ./src/physics/physicsmodel.cxx + ./src/physics/smoothing.cxx + ./src/physics/snb.cxx + ./src/physics/sourcex.cxx + ./src/solver/impls/adams_bashforth/adams_bashforth.cxx + ./src/solver/impls/adams_bashforth/adams_bashforth.hxx + ./src/solver/impls/arkode/arkode.cxx + ./src/solver/impls/arkode/arkode.hxx + ./src/solver/impls/cvode/cvode.cxx + ./src/solver/impls/cvode/cvode.hxx + ./src/solver/impls/euler/euler.cxx + ./src/solver/impls/euler/euler.hxx + ./src/solver/impls/ida/ida.cxx + ./src/solver/impls/ida/ida.hxx + ./src/solver/impls/imex-bdf2/imex-bdf2.cxx + ./src/solver/impls/imex-bdf2/imex-bdf2.hxx + ./src/solver/impls/petsc/petsc.cxx + ./src/solver/impls/petsc/petsc.hxx + ./src/solver/impls/power/power.cxx + ./src/solver/impls/power/power.hxx + ./src/solver/impls/pvode/pvode.cxx + ./src/solver/impls/pvode/pvode.hxx + ./src/solver/impls/rk3-ssp/rk3-ssp.cxx + ./src/solver/impls/rk3-ssp/rk3-ssp.hxx + ./src/solver/impls/rk4/rk4.cxx + ./src/solver/impls/rk4/rk4.hxx + ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.cxx + ./src/solver/impls/rkgeneric/impls/cashkarp/cashkarp.hxx + ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.cxx + ./src/solver/impls/rkgeneric/impls/rk4simple/rk4simple.hxx + ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.cxx + ./src/solver/impls/rkgeneric/impls/rkf34/rkf34.hxx + ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.cxx + ./src/solver/impls/rkgeneric/impls/rkf45/rkf45.hxx + ./src/solver/impls/rkgeneric/rkgeneric.cxx + ./src/solver/impls/rkgeneric/rkgeneric.hxx + ./src/solver/impls/rkgeneric/rkscheme.cxx + ./src/solver/impls/slepc/slepc.cxx + ./src/solver/impls/slepc/slepc.hxx + ./src/solver/impls/snes/snes.cxx + ./src/solver/impls/snes/snes.hxx + ./src/solver/impls/split-rk/split-rk.cxx + ./src/solver/impls/split-rk/split-rk.hxx + ./src/solver/solver.cxx + ./src/sys/adios_object.cxx + ./src/sys/bout_types.cxx + ./src/sys/boutcomm.cxx + ./src/sys/boutexception.cxx + ./src/sys/derivs.cxx + ./src/sys/expressionparser.cxx + ./src/sys/generator_context.cxx + ./include/bout/hyprelib.hxx + ./src/sys/hyprelib.cxx + ./src/sys/msg_stack.cxx + ./src/sys/options.cxx + ./src/sys/options/optionparser.hxx + ./src/sys/options/options_ini.cxx + ./src/sys/options/options_ini.hxx + ./src/sys/options/options_io.cxx + ./src/sys/options/options_netcdf.cxx + ./src/sys/options/options_netcdf.hxx + ./src/sys/options/options_adios.cxx + ./src/sys/options/options_adios.hxx + ./src/sys/optionsreader.cxx + ./src/sys/output.cxx + ./src/sys/petsclib.cxx + ./src/sys/range.cxx + ./src/sys/slepclib.cxx + ./src/sys/timer.cxx + ./src/sys/type_name.cxx + ./src/sys/utils.cxx + ${CMAKE_CURRENT_BINARY_DIR}/include/bout/revision.hxx + ${CMAKE_CURRENT_BINARY_DIR}/include/bout/version.hxx +) find_package(Python3) find_package(ClangFormat) -if (Python3_FOUND AND ClangFormat_FOUND) +if(Python3_FOUND AND ClangFormat_FOUND) set(BOUT_GENERATE_FIELDOPS_DEFAULT ON) else() set(BOUT_GENERATE_FIELDOPS_DEFAULT OFF) endif() -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import importlib.util ; import sys; sys.exit(importlib.util.find_spec(\"zoidberg\") is None)" - RESULT_VARIABLE zoidberg_FOUND) -if (zoidberg_FOUND EQUAL 0) +execute_process( + COMMAND + ${Python3_EXECUTABLE} -c + "import importlib.util ; import sys; sys.exit(importlib.util.find_spec(\"zoidberg\") is None)" + RESULT_VARIABLE zoidberg_FOUND +) +if(zoidberg_FOUND EQUAL 0) set(zoidberg_FOUND ON) else() set(zoidberg_FOUND OFF) endif() -option(BOUT_GENERATE_FIELDOPS "Automatically re-generate the Field arithmetic operators from the Python templates. \ +option( + BOUT_GENERATE_FIELDOPS + "Automatically re-generate the Field arithmetic operators from the Python templates. \ Requires Python3, clang-format, and Jinja2. Turn this OFF to skip generating them if, for example, \ -you are unable to install the Jinja2 Python module. This is only important for BOUT++ developers." ${BOUT_GENERATE_FIELDOPS_DEFAULT}) +you are unable to install the Jinja2 Python module. This is only important for BOUT++ developers." + ${BOUT_GENERATE_FIELDOPS_DEFAULT} +) -if (BOUT_GENERATE_FIELDOPS) - if (NOT Python3_FOUND) - message(FATAL_ERROR "python not found, but you have requested to generate code!") +if(BOUT_GENERATE_FIELDOPS) + if(NOT Python3_FOUND) + message( + FATAL_ERROR "python not found, but you have requested to generate code!" + ) endif() - if (NOT ClangFormat_FOUND) - message(FATAL_ERROR "clang-format not found, but you have requested to generate code!") + if(NOT ClangFormat_FOUND) + message( + FATAL_ERROR + "clang-format not found, but you have requested to generate code!" + ) endif() - add_custom_command( OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/field/generated_fieldops.cxx - COMMAND ${Python3_EXECUTABLE} gen_fieldops.py --filename generated_fieldops.cxx.tmp + add_custom_command( + OUTPUT ${CMAKE_CURRENT_SOURCE_DIR}/src/field/generated_fieldops.cxx + COMMAND ${Python3_EXECUTABLE} gen_fieldops.py --filename + generated_fieldops.cxx.tmp COMMAND ${ClangFormat_BIN} generated_fieldops.cxx.tmp -i - COMMAND ${CMAKE_COMMAND} -E rename generated_fieldops.cxx.tmp generated_fieldops.cxx - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.jinja ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.py + COMMAND ${CMAKE_COMMAND} -E rename generated_fieldops.cxx.tmp + generated_fieldops.cxx + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.jinja + ${CMAKE_CURRENT_SOURCE_DIR}/src/field/gen_fieldops.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}/src/field/ - COMMENT "Generating source code" ) + COMMENT "Generating source code" + ) else() - message(AUTHOR_WARNING "'src/field/generated_fieldops.cxx' will not be \ + message( + AUTHOR_WARNING + "'src/field/generated_fieldops.cxx' will not be \ regenerated when you make changes to either \ 'src/field/gen_fieldops.py' or 'src/field/gen_fieldops.jinja'. \ This is because either Python3 or clang-format is missing \ (see above messages for more information) \ or BOUT_GENERATE_FIELDOPS is OFF (current value: ${BOUT_GENERATE_FIELDOPS}). \ This warning is only important for BOUT++ developers and can otherwise be \ -safely ignored.") +safely ignored." + ) endif() include(GNUInstallDirs) @@ -420,21 +463,29 @@ set(CMAKE_INSTALL_RPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") # which point to directories outside the build tree to the install RPATH set(CMAKE_INSTALL_RPATH_USE_LINK_PATH TRUE) - -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import site ; print('/'.join(site.getusersitepackages().split('/')[-2:]))" +execute_process( + COMMAND + ${Python3_EXECUTABLE} -c + "import site ; print('/'.join(site.getusersitepackages().split('/')[-2:]))" RESULT_VARIABLE PYTHON_WORKING OUTPUT_VARIABLE PYTHON_SITEPATH_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE ) -set(CMAKE_INSTALL_PYTHON_SITEARCH lib/${PYTHON_SITEPATH_SUFFIX} CACHE STRING "Location to install python arch-specific modules") +set(CMAKE_INSTALL_PYTHON_SITEARCH + lib/${PYTHON_SITEPATH_SUFFIX} + CACHE STRING "Location to install python arch-specific modules" +) set(ON_OFF_AUTO ON OFF AUTO) -set(BOUT_ENABLE_PYTHON AUTO CACHE STRING "Build the Python interface") +set(BOUT_ENABLE_PYTHON + AUTO + CACHE STRING "Build the Python interface" +) set_property(CACHE BOUT_ENABLE_PYTHON PROPERTY STRINGS ${ON_OFF_AUTO}) -if (NOT BOUT_ENABLE_PYTHON IN_LIST ON_OFF_AUTO) +if(NOT BOUT_ENABLE_PYTHON IN_LIST ON_OFF_AUTO) message(FATAL_ERROR "BOUT_ENABLE_PYTHON must be one of ${ON_OFF_AUTO}") endif() -if (BOUT_ENABLE_PYTHON OR BOUT_ENABLE_PYTHON STREQUAL "AUTO") +if(BOUT_ENABLE_PYTHON OR BOUT_ENABLE_PYTHON STREQUAL "AUTO") add_subdirectory(tools/pylib/_boutpp_build) else() set(BOUT_ENABLE_PYTHON OFF) @@ -444,33 +495,35 @@ set(BOUT_USE_PYTHON ${BOUT_ENABLE_PYTHON}) # Ensure that the compile date/time is up-to-date when any of the sources change add_custom_command( OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx - COMMAND ${CMAKE_COMMAND} -P "${CMAKE_CURRENT_LIST_DIR}/cmake/GenerateDateTimeFile.cmake" + COMMAND ${CMAKE_COMMAND} -P + "${CMAKE_CURRENT_LIST_DIR}/cmake/GenerateDateTimeFile.cmake" DEPENDS ${BOUT_SOURCES} MAIN_DEPENDENCY "${CMAKE_CURRENT_LIST_DIR}/cmake/GenerateDateTimeFile.cmake" - ) - +) -add_library(bout++ - ${BOUT_SOURCES} - ${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx - ) +add_library(bout++ ${BOUT_SOURCES} ${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx) add_library(bout++::bout++ ALIAS bout++) target_link_libraries(bout++ PUBLIC MPI::MPI_CXX) -target_include_directories(bout++ PUBLIC - $ - $ - $ - ) +target_include_directories( + bout++ + PUBLIC $ + $ + $ +) set(BOUT_LIB_PATH "${CMAKE_CURRENT_BINARY_DIR}/lib") -set_target_properties(bout++ PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" - ARCHIVE_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" - SOVERSION 5.2.0) +set_target_properties( + bout++ + PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" + ARCHIVE_OUTPUT_DIRECTORY "${BOUT_LIB_PATH}" + SOVERSION 5.2.0 +) # Set some variables for the bout-config script set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -L\$BOUT_LIB_PATH -lbout++") set(BOUT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/include") -set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${BOUT_INCLUDE_PATH} -I${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CXX_FLAGS} -std=c++17") +set(CONFIG_CFLAGS + "${CONFIG_CFLAGS} -I\${BOUT_INCLUDE_PATH} -I${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CXX_FLAGS} -std=c++17" +) target_compile_features(bout++ PUBLIC cxx_std_17) set_target_properties(bout++ PROPERTIES CXX_EXTENSIONS OFF) @@ -504,58 +557,77 @@ message(STATUS "Git revision: ${BOUT_REVISION}") # Build the file containing the version information configure_file( "${PROJECT_SOURCE_DIR}/include/bout/version.hxx.in" - "${PROJECT_BINARY_DIR}/include/bout/version.hxx") + "${PROJECT_BINARY_DIR}/include/bout/version.hxx" +) # Build the file containing just the commit hash # This will be rebuilt on every commit! configure_file( "${PROJECT_SOURCE_DIR}/include/bout/revision.hxx.in" - "${PROJECT_BINARY_DIR}/include/bout/revision.hxx") + "${PROJECT_BINARY_DIR}/include/bout/revision.hxx" +) ################################################## option(BOUT_ENABLE_WARNINGS "Enable compiler warnings" ON) -if (BOUT_ENABLE_WARNINGS) - target_compile_options(bout++ PRIVATE - $<$>: - $<$,$,$>: - -Wall -Wextra > > - $<$: - /W4 > - $<$:-Xcompiler=-Wall -Xcompiler=-Wextra > - ) - - include(EnableCXXWarningIfSupport) - # Note we explicitly turn off -Wcast-function-type as PETSc *requires* - # we cast a function to the wrong type in MatFDColoringSetFunction - target_enable_cxx_warning_if_supported(bout++ - FLAGS -Wnull-dereference -Wno-cast-function-type - ) +if(BOUT_ENABLE_WARNINGS) + target_compile_options( + bout++ + PRIVATE + $<$>: + $<$,$,$>: + -Wall + -Wextra + > + > + $<$: + /W4 + > + $<$:-Xcompiler=-Wall + -Xcompiler=-Wextra + > + ) + + include(EnableCXXWarningIfSupport) + # Note we explicitly turn off -Wcast-function-type as PETSc *requires* + # we cast a function to the wrong type in MatFDColoringSetFunction + target_enable_cxx_warning_if_supported( + bout++ FLAGS -Wnull-dereference -Wno-cast-function-type + ) endif() # Compile time features set(CHECK_LEVELS 0 1 2 3 4) -set(CHECK 2 CACHE STRING "Set run-time checking level") +set(CHECK + 2 + CACHE STRING "Set run-time checking level" +) set_property(CACHE CHECK PROPERTY STRINGS ${CHECK_LEVELS}) -if (NOT CHECK IN_LIST CHECK_LEVELS) +if(NOT CHECK IN_LIST CHECK_LEVELS) message(FATAL_ERROR "CHECK must be one of ${CHECK_LEVELS}") endif() message(STATUS "Runtime checking level: CHECK=${CHECK}") target_compile_definitions(bout++ PUBLIC "CHECK=${CHECK}") set(BOUT_CHECK_LEVEL ${CHECK}) -if (CHECK GREATER 1) +if(CHECK GREATER 1) set(bout_use_msgstack_default ON) else() set(bout_use_msgstack_default OFF) endif() -set(BOUT_ENABLE_MSGSTACK ${bout_use_msgstack_default} CACHE BOOL "Enable debug message stack") +set(BOUT_ENABLE_MSGSTACK + ${bout_use_msgstack_default} + CACHE BOOL "Enable debug message stack" +) message(STATUS "Message stack: BOUT_USE_MSGSTACK=${BOUT_ENABLE_MSGSTACK}") set(BOUT_USE_MSGSTACK ${BOUT_ENABLE_MSGSTACK}) -cmake_dependent_option(BOUT_ENABLE_OUTPUT_DEBUG "Enable extra debug output" OFF - "CHECK LESS 3" ON) -message(STATUS "Extra debug output: BOUT_USE_OUTPUT_DEBUG=${BOUT_ENABLE_OUTPUT_DEBUG}") +cmake_dependent_option( + BOUT_ENABLE_OUTPUT_DEBUG "Enable extra debug output" OFF "CHECK LESS 3" ON +) +message( + STATUS "Extra debug output: BOUT_USE_OUTPUT_DEBUG=${BOUT_ENABLE_OUTPUT_DEBUG}" +) set(BOUT_USE_OUTPUT_DEBUG ${BOUT_ENABLE_OUTPUT_DEBUG}) option(BOUT_ENABLE_SIGNAL "SegFault handling" ON) @@ -571,7 +643,10 @@ message(STATUS "Field name tracking: BOUT_USE_TRACK=${BOUT_ENABLE_TRACK}") set(BOUT_USE_TRACK ${BOUT_ENABLE_TRACK}) option(BOUT_ENABLE_SIGFPE "Signalling floating point exceptions" OFF) -message(STATUS "Signalling floating point exceptions: BOUT_USE_SIGFPE=${BOUT_ENABLE_SIGFPE}") +message( + STATUS + "Signalling floating point exceptions: BOUT_USE_SIGFPE=${BOUT_ENABLE_SIGFPE}" +) set(BOUT_USE_SIGFPE ${BOUT_ENABLE_SIGFPE}) option(BOUT_ENABLE_METRIC_3D "Enable 3D metric support" OFF) @@ -583,12 +658,15 @@ endif() set(BOUT_USE_METRIC_3D ${BOUT_ENABLE_METRIC_3D}) include(CheckCXXSourceCompiles) -check_cxx_source_compiles("int main() { const char* name = __PRETTY_FUNCTION__; }" - HAS_PRETTY_FUNCTION) +check_cxx_source_compiles( + "int main() { const char* name = __PRETTY_FUNCTION__; }" HAS_PRETTY_FUNCTION +) set(BOUT_HAS_PRETTY_FUNCTION ${HAS_PRETTY_FUNCTION}) # Locations of the various Python modules, including the generated boutconfig module -set(BOUT_PYTHONPATH "${CMAKE_CURRENT_BINARY_DIR}/tools/pylib:${CMAKE_CURRENT_SOURCE_DIR}/tools/pylib") +set(BOUT_PYTHONPATH + "${CMAKE_CURRENT_BINARY_DIR}/tools/pylib:${CMAKE_CURRENT_SOURCE_DIR}/tools/pylib" +) # Variables for boutconfig module -- note that these will contain # generator expressions and CMake targets, and not generally be very # useful @@ -601,18 +679,22 @@ get_target_property(BOUT_CFLAGS bout++ INTERFACE_INCLUDE_DIRECTORIES) # 1. Get the macro definitions. They come as a ;-separated list and # without the -D. We also need to also stick a -D on the front of # the first item -get_property(BOUT_COMPILE_DEFINITIONS +get_property( + BOUT_COMPILE_DEFINITIONS TARGET bout++ - PROPERTY COMPILE_DEFINITIONS) + PROPERTY COMPILE_DEFINITIONS +) string(REPLACE ";" " -D" BOUT_COMPILE_DEFINITIONS "${BOUT_COMPILE_DEFINITIONS}") string(CONCAT BOUT_COMPILE_DEFINITIONS " -D" "${BOUT_COMPILE_DEFINITIONS}") # 2. Get the compiler options. Again, they come as a ;-separated # list. Note that they don't include optimisation or debug flags: # they're in the CMAKE_CXX_FLAGS* variables -get_property(BOUT_COMPILE_OPTIONS +get_property( + BOUT_COMPILE_OPTIONS TARGET bout++ - PROPERTY COMPILE_OPTIONS) + PROPERTY COMPILE_OPTIONS +) string(REPLACE ";" " " BOUT_COMPILE_OPTIONS "${BOUT_COMPILE_OPTIONS}") # 3. The optimisation and/or debug flags are in the CMAKE_CXX_FLAGS* @@ -624,31 +706,33 @@ string(REPLACE ";" " " BOUT_COMPILE_OPTIONS "${BOUT_COMPILE_OPTIONS}") include(BuildType) # Here CMAKE_BUILD_TYPE is always set string(TOUPPER "${CMAKE_BUILD_TYPE}" CMAKE_BUILD_TYPE_UPPER) -string(CONCAT BOUT_COMPILE_BUILD_FLAGS - " " - "${CMAKE_CXX_FLAGS}" - "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}") +string(CONCAT BOUT_COMPILE_BUILD_FLAGS " " "${CMAKE_CXX_FLAGS}" + "${CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE_UPPER}}" +) # 4. Now we join all the flags from the first three steps together -string(CONCAT BOUT_FLAGS_STRING - "${BOUT_COMPILE_OPTIONS}" - "${BOUT_COMPILE_DEFINITIONS}" - "${BOUT_COMPILE_BUILD_FLAGS}") +string(CONCAT BOUT_FLAGS_STRING "${BOUT_COMPILE_OPTIONS}" + "${BOUT_COMPILE_DEFINITIONS}" "${BOUT_COMPILE_BUILD_FLAGS}" +) # 5. Finally actually add the flags as a define -target_compile_definitions(bout++ - PRIVATE BOUT_FLAGS_STRING=${BOUT_FLAGS_STRING}) +target_compile_definitions( + bout++ PRIVATE BOUT_FLAGS_STRING=${BOUT_FLAGS_STRING} +) ################################################## # Tests # Are we building BOUT++ directly, or as part of another project -string(COMPARE EQUAL - "${PROJECT_NAME}" "${CMAKE_PROJECT_NAME}" - PROJECT_IS_TOP_LEVEL +string(COMPARE EQUAL "${PROJECT_NAME}" "${CMAKE_PROJECT_NAME}" + PROJECT_IS_TOP_LEVEL ) option(BOUT_TESTS "Build the tests" ${PROJECT_IS_TOP_LEVEL}) -option(BOUT_ENABLE_ALL_TESTS "Enable running all of the tests, rather then the standard selection of fast tests" OFF) +option( + BOUT_ENABLE_ALL_TESTS + "Enable running all of the tests, rather then the standard selection of fast tests" + OFF +) if(BOUT_TESTS) enable_testing() # Targets for just building the tests @@ -659,31 +743,35 @@ if(BOUT_TESTS) # Build all the tests add_custom_target(build-check) - add_dependencies(build-check build-check-unit-tests build-check-integrated-tests build-check-mms-tests) + add_dependencies( + build-check build-check-unit-tests build-check-integrated-tests + build-check-mms-tests + ) add_subdirectory(tests/unit EXCLUDE_FROM_ALL) add_subdirectory(tests/integrated EXCLUDE_FROM_ALL) add_subdirectory(tests/MMS EXCLUDE_FROM_ALL) # Targets for running the tests - if (BOUT_ENABLE_UNIT_TESTS) - add_custom_target(check-unit-tests - COMMAND ctest -R serial_tests --output-on-failure) + if(BOUT_ENABLE_UNIT_TESTS) + add_custom_target( + check-unit-tests COMMAND ctest -R serial_tests --output-on-failure + ) add_dependencies(check-unit-tests build-check-unit-tests) endif() - add_custom_target(check-integrated-tests - COMMAND ctest -R "test-" --output-on-failure) + add_custom_target( + check-integrated-tests COMMAND ctest -R "test-" --output-on-failure + ) add_dependencies(check-integrated-tests build-check-integrated-tests) - add_custom_target(check-mms-tests - COMMAND ctest -R "MMS-" --output-on-failure) + add_custom_target(check-mms-tests COMMAND ctest -R "MMS-" --output-on-failure) add_dependencies(check-mms-tests build-check-mms-tests) # Run all the tests add_custom_target(check) add_dependencies(check check-integrated-tests check-mms-tests) - if (BOUT_ENABLE_UNIT_TESTS) + if(BOUT_ENABLE_UNIT_TESTS) add_dependencies(check check-unit-tests) endif() @@ -695,21 +783,22 @@ if(BOUT_BUILD_EXAMPLES) add_subdirectory(examples EXCLUDE_FROM_ALL) endif() - ################################################## # L10N: localisation - include translations find_package(Gettext) -if (GETTEXT_FOUND) +if(GETTEXT_FOUND) #add_custom_target(mofiles ALL) set(bout_langs es de fr zh_CN zh_TW) foreach(_lang IN LISTS bout_langs) set(_gmoFile ${CMAKE_CURRENT_BINARY_DIR}/locale/${_lang}/libbout.gmo) set(_poFile ${CMAKE_CURRENT_SOURCE_DIR}/locale/${_lang}/libbout.po) - add_custom_command(OUTPUT ${_gmoFile} _mo_file_${_lang} - COMMAND ${CMAKE_COMMAND} -E make_directory ${CMAKE_CURRENT_BINARY_DIR}/locale/${_lang}/ + add_custom_command( + OUTPUT ${_gmoFile} _mo_file_${_lang} + COMMAND ${CMAKE_COMMAND} -E make_directory + ${CMAKE_CURRENT_BINARY_DIR}/locale/${_lang}/ COMMAND ${GETTEXT_MSGFMT_EXECUTABLE} -o ${_gmoFile} ${_poFile} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" DEPENDS ${_poFile} @@ -717,41 +806,46 @@ if (GETTEXT_FOUND) list(APPEND _gmoFiles ${_gmoFile}) - install(FILES ${_gmoFile} DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${_lang}/LC_MESSAGES/ RENAME libbout.mo) + install( + FILES ${_gmoFile} + DESTINATION ${CMAKE_INSTALL_LOCALEDIR}/${_lang}/LC_MESSAGES/ + RENAME libbout.mo + ) endforeach() - add_custom_target(mofiles ALL - DEPENDS ${_gmoFiles}) + add_custom_target(mofiles ALL DEPENDS ${_gmoFiles}) endif() - ################################################## # Documentation option(BOUT_BUILD_DOCS "Build the documentation" OFF) -if (BOUT_BUILD_DOCS) +if(BOUT_BUILD_DOCS) add_subdirectory(manual) endif() - -add_custom_target(dist - COMMAND ${Python3_EXECUTABLE} ${CMAKE_SOURCE_DIR}/tools/pylib/_boutpp_build/backend.py dist +add_custom_target( + dist + COMMAND ${Python3_EXECUTABLE} + ${CMAKE_SOURCE_DIR}/tools/pylib/_boutpp_build/backend.py dist # there is no cmake equivalent to `mv` - so only works on systems that are not inentionally non-POSIX complient COMMAND mv BOUT++-v${BOUT_FULL_VERSION}.tar.gz ${CMAKE_CURRENT_BINARY_DIR}/ WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} - ) +) ################################################## # Generate the build config header -if (EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx") +if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx") # If we do in source builds, this is fine - if (NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) - message(FATAL_ERROR "Generated build_defines.hxx header already exists; please remove '${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx' before continuing") + if(NOT ${CMAKE_CURRENT_SOURCE_DIR} STREQUAL ${CMAKE_CURRENT_BINARY_DIR}) + message( + FATAL_ERROR + "Generated build_defines.hxx header already exists; please remove '${CMAKE_CURRENT_SOURCE_DIR}/include/bout/build_defines.hxx' before continuing" + ) endif() endif() configure_file(cmake_build_defines.hxx.in include/bout/build_defines.hxx) - ################################################## # Generate the bout-config script @@ -763,9 +857,11 @@ set(BOUT_HAS_PNETCDF OFF) # For shared libraries we only need to know how to link against BOUT++, # while for static builds we need the dependencies too -if (BUILD_SHARED_LIBS) +if(BUILD_SHARED_LIBS) # Include rpath linker flag so user doesn't need to set LD_LIBRARY_PATH - set(CONFIG_LDFLAGS "${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}\$BOUT_LIB_PATH -L\$BOUT_LIB_PATH -lbout++ -lfmt ${CONFIG_LDFLAGS_SHARED}") + set(CONFIG_LDFLAGS + "${CMAKE_SHARED_LIBRARY_RUNTIME_CXX_FLAG}\$BOUT_LIB_PATH -L\$BOUT_LIB_PATH -lbout++ -lfmt ${CONFIG_LDFLAGS_SHARED}" + ) else() set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS}") endif() @@ -774,9 +870,9 @@ set(ISINSTALLED "FALSE") set(_CONFIG_LDFLAGS) string(REPLACE " " ";" CONFIG_LDFLAGS_LIST ${CONFIG_LDFLAGS}) -foreach (flag ${CONFIG_LDFLAGS_LIST}) +foreach(flag ${CONFIG_LDFLAGS_LIST}) string(REGEX MATCH "^-.*$" isopt "${flag}") - if (isopt) + if(isopt) # message("${flag} is an option") set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} ${flag}") # All good @@ -786,73 +882,93 @@ foreach (flag ${CONFIG_LDFLAGS_LIST}) set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} ${flag}") else() string(FIND "${flag}" "::" hascolcol) - if (${hascolcol} EQUAL -1) - message("Fixing ${flag} to -l${flag}") - set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag}") + if(${hascolcol} EQUAL -1) + message("Fixing ${flag} to -l${flag}") + set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag}") else() - string(REGEX MATCH "[^:]*$" flag2 "${flag}") - message("Fixing ${flag} to -l${flag2}") - set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag2}") + string(REGEX MATCH "[^:]*$" flag2 "${flag}") + message("Fixing ${flag} to -l${flag2}") + set(_CONFIG_LDFLAGS "${_CONFIG_LDFLAGS} -l${flag2}") endif() endif() endif() endforeach() -set( CONFIG_LDFLAGS ${_CONFIG_LDFLAGS}) +set(CONFIG_LDFLAGS ${_CONFIG_LDFLAGS}) # This version of the file allows the build directory to be used directly configure_file(bin/bout-config.in bin/bout-config @ONLY) -configure_file(tools/pylib/boutconfig/__init__.py.cin tools/pylib/boutconfig/__init__.py @ONLY) +configure_file( + tools/pylib/boutconfig/__init__.py.cin tools/pylib/boutconfig/__init__.py + @ONLY +) configure_file(bout++Config.cmake.in bout++Config.cmake @ONLY) # We need to generate a separate version for installation, with the # correct install paths. So first we need to replace the build # directory library path with the installation path -string(REPLACE - "${CMAKE_BINARY_DIR}/lib" "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" - CONFIG_LDFLAGS "${CONFIG_LDFLAGS}") +string(REPLACE "${CMAKE_BINARY_DIR}/lib" + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}" CONFIG_LDFLAGS + "${CONFIG_LDFLAGS}" +) set(BOUT_LIB_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_LIBDIR}") # Update mpark.variant and fmt include paths if we're building them -if (NOT BOUT_USE_SYSTEM_MPARK_VARIANT) - set(MPARK_VARIANT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") +if(NOT BOUT_USE_SYSTEM_MPARK_VARIANT) + set(MPARK_VARIANT_INCLUDE_PATH + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}" + ) endif() -if (NOT BOUT_USE_SYSTEM_FMT) +if(NOT BOUT_USE_SYSTEM_FMT) set(FMT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") endif() -if (NOT BOUT_USE_SYSTEM_CPPTRACE) - set(CPPTRACE_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") +if(NOT BOUT_USE_SYSTEM_CPPTRACE) + set(CPPTRACE_INCLUDE_PATH + "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}" + ) endif() set(BOUT_INCLUDE_PATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_INCLUDEDIR}") # We don't need the build include path any more -string(REPLACE "-I${CMAKE_CURRENT_BINARY_DIR}/include" "" CONFIG_CFLAGS "${CONFIG_CFLAGS}") +string(REPLACE "-I${CMAKE_CURRENT_BINARY_DIR}/include" "" CONFIG_CFLAGS + "${CONFIG_CFLAGS}" +) set(PYTHONCONFIGPATH "${CMAKE_INSTALL_PREFIX}/${CMAKE_INSTALL_PYTHON_SITEARCH}") set(ISINSTALLED "TRUE") # This version now has the correct paths to use the final installation configure_file(bin/bout-config.in bin/bout-config-install @ONLY) -configure_file(tools/pylib/boutconfig/__init__.py.cin tools/pylib/boutconfig/__init__.py-install @ONLY) +configure_file( + tools/pylib/boutconfig/__init__.py.cin + tools/pylib/boutconfig/__init__.py-install @ONLY +) configure_file(bout++Config.cmake.in bout++Config.cmake-install @ONLY) ################################################## # Installation -install(TARGETS bout++ +install( + TARGETS bout++ EXPORT bout++Targets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) + INCLUDES + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) # Repo files -install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} - FILES_MATCHING PATTERN "*.hxx") +install( + DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} + FILES_MATCHING + PATTERN "*.hxx" +) # Generated headers install(DIRECTORY "${PROJECT_BINARY_DIR}/include/" - DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR} +) # The various helper scripts -install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/" - USE_SOURCE_PERMISSIONS +install( + DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/" USE_SOURCE_PERMISSIONS DESTINATION "${CMAKE_INSTALL_BINDIR}" REGEX "bout-squashoutput" EXCLUDE REGEX "bout-config\.in" EXCLUDE @@ -863,69 +979,71 @@ install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/bin/" # it. Note this MUST be done after the installation of bin/, to make # sure we clobber any versions of bout-config hanging around from an # autotools build -install(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/bin/bout-config-install" +install( + PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/bin/bout-config-install" DESTINATION "${CMAKE_INSTALL_BINDIR}" RENAME "bout-config" - ) +) install( FILES "${CMAKE_CURRENT_BINARY_DIR}/tools/pylib/boutconfig/__init__.py-install" DESTINATION "${CMAKE_INSTALL_PYTHON_SITEARCH}/boutconfig" RENAME "__init__.py" - ) +) include(CMakePackageConfigHelpers) write_basic_package_version_file( bout++ConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY SameMajorVersion - ) +) -install(EXPORT bout++Targets +install( + EXPORT bout++Targets FILE bout++Targets.cmake NAMESPACE bout++:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bout++" - ) +) # CMake configuration files install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/bout++ConfigVersion.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/BOUT++functions.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CorrectWindowsPaths.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindClangFormat.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindFFTW.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindHYPRE.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDF.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDFCxx.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPackageMultipass.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPETSc.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindScoreP.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSLEPc.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSUNDIALS.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSphinx.cmake" - "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ResolveCompilerPaths.cmake" + FILES "${CMAKE_CURRENT_BINARY_DIR}/bout++ConfigVersion.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/BOUT++functions.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/CorrectWindowsPaths.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindClangFormat.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindFFTW.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindHYPRE.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDF.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindnetCDFCxx.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPackageMultipass.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindLibuuid.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindPETSc.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindScoreP.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSLEPc.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSUNDIALS.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/FindSphinx.cmake" + "${CMAKE_CURRENT_SOURCE_DIR}/cmake/ResolveCompilerPaths.cmake" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bout++" - ) +) install( - FILES - "${CMAKE_CURRENT_BINARY_DIR}/bout++Config.cmake-install" + FILES "${CMAKE_CURRENT_BINARY_DIR}/bout++Config.cmake-install" DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/bout++" RENAME "bout++Config.cmake" - ) +) -export(EXPORT bout++Targets +export( + EXPORT bout++Targets FILE "${CMAKE_CURRENT_BINARY_DIR}/bout++Targets.cmake" NAMESPACE bout++:: - ) +) export(PACKAGE bout) ################################################## # Configure summary -message(" +message( + " -------------------------------- BOUT++ Configuration Summary -------------------------------- @@ -965,4 +1083,5 @@ message(" export PYTHONPATH=${BOUT_PYTHONPATH}:\$PYTHONPATH *** Now run `cmake --build ${CMAKE_BINARY_DIR}` to compile BOUT++ *** -") +" +) diff --git a/cmake/BOUT++functions.cmake b/cmake/BOUT++functions.cmake index 074d803fb6..cff7e4b808 100644 --- a/cmake/BOUT++functions.cmake +++ b/cmake/BOUT++functions.cmake @@ -3,9 +3,9 @@ # Copy FILENAME from source directory to build directory macro(bout_copy_file FILENAME) configure_file( - ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME} - ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME} - COPYONLY) + ${CMAKE_CURRENT_SOURCE_DIR}/${FILENAME} + ${CMAKE_CURRENT_BINARY_DIR}/${FILENAME} COPYONLY + ) endmacro() # Handle the REQUIRES and CONFLICTS arguments for models, examples, @@ -15,16 +15,22 @@ macro(bout_handle_requires_conflicts TYPENAME TYPEVAR) set(multiValueArgs REQUIRES CONFLICTS) cmake_parse_arguments(BOUT_HANDLE_OPTIONS "" "" "${multiValueArgs}" ${ARGN}) - foreach (REQUIREMENT IN LISTS BOUT_HANDLE_OPTIONS_REQUIRES) - if (NOT ${REQUIREMENT}) - message(STATUS "Not building ${TYPENAME} ${TYPEVAR}, requirement not met: ${REQUIREMENT}") + foreach(REQUIREMENT IN LISTS BOUT_HANDLE_OPTIONS_REQUIRES) + if(NOT ${REQUIREMENT}) + message( + STATUS + "Not building ${TYPENAME} ${TYPEVAR}, requirement not met: ${REQUIREMENT}" + ) return() endif() endforeach() - foreach (CONFLICT IN LISTS BOUT_HANDLE_OPTIONS_CONFLICTS) - if (${CONFLICT}) - message(STATUS "Not building ${TYPENAME} ${TYPEVAR}, conflicts with: ${CONFLICT}") + foreach(CONFLICT IN LISTS BOUT_HANDLE_OPTIONS_CONFLICTS) + if(${CONFLICT}) + message( + STATUS + "Not building ${TYPENAME} ${TYPEVAR}, conflicts with: ${CONFLICT}" + ) return() endif() endforeach() @@ -46,25 +52,29 @@ function(bout_add_model MODEL) set(multiValueArgs SOURCES REQUIRES CONFLICTS) cmake_parse_arguments(BOUT_MODEL_OPTIONS "" "" "${multiValueArgs}" ${ARGN}) - bout_handle_requires_conflicts("model" MODEL + bout_handle_requires_conflicts( + "model" MODEL REQUIRES ${BOUT_MODEL_OPTIONS_REQUIRES} CONFLICTS ${BOUT_MODEL_OPTIONS_CONFLICTS} - ) + ) - if (NOT BOUT_MODEL_OPTIONS_SOURCES) - message(FATAL_ERROR "Required argument SOURCES missing from 'bout_add_model'") + if(NOT BOUT_MODEL_OPTIONS_SOURCES) + message( + FATAL_ERROR "Required argument SOURCES missing from 'bout_add_model'" + ) endif() - if ("SOURCES" IN_LIST BOUT_MODEL_OPTIONS_KEYWORDS_MISSING_VALUES) + if("SOURCES" IN_LIST BOUT_MODEL_OPTIONS_KEYWORDS_MISSING_VALUES) message(FATAL_ERROR "SOURCES missing values from 'bout_add_model'") endif() add_executable(${MODEL} ${BOUT_MODEL_OPTIONS_SOURCES}) target_link_libraries(${MODEL} bout++::bout++) - target_include_directories(${MODEL} PRIVATE $) + target_include_directories( + ${MODEL} PRIVATE $ + ) endfunction() - # Build a BOUT++ example # # If called from a standalone project, just builds the example as a @@ -85,37 +95,38 @@ function(bout_add_example EXAMPLENAME) set(multiValueArgs SOURCES REQUIRES CONFLICTS DATA_DIRS EXTRA_FILES) cmake_parse_arguments(BOUT_EXAMPLE_OPTIONS "" "" "${multiValueArgs}" ${ARGN}) - bout_handle_requires_conflicts("example" ${EXAMPLENAME} + bout_handle_requires_conflicts( + "example" ${EXAMPLENAME} REQUIRES ${BOUT_EXAMPLE_OPTIONS_REQUIRES} CONFLICTS ${BOUT_EXAMPLE_OPTIONS_CONFLICTS} - ) + ) bout_add_model(${EXAMPLENAME} SOURCES ${BOUT_EXAMPLE_OPTIONS_SOURCES}) # If this is a standalone project, we can stop here. Otherwise, we # need to copy the various input files to the build directory get_directory_property(HAS_PARENT PARENT_DIRECTORY) - if (NOT HAS_PARENT) + if(NOT HAS_PARENT) return() endif() # Copy the documentation if it exists - if (EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/README.md) + if(EXISTS ${CMAKE_CURRENT_SOURCE_DIR}/README.md) bout_copy_file(README.md) endif() # Copy the input file - if (NOT BOUT_EXAMPLE_OPTIONS_DATA_DIRS) + if(NOT BOUT_EXAMPLE_OPTIONS_DATA_DIRS) bout_copy_file(data/BOUT.inp) else() - foreach (DATA_DIR IN LISTS BOUT_EXAMPLE_OPTIONS_DATA_DIRS) + foreach(DATA_DIR IN LISTS BOUT_EXAMPLE_OPTIONS_DATA_DIRS) bout_copy_file(${DATA_DIR}/BOUT.inp) endforeach() endif() # Copy any other needed files - if (BOUT_EXAMPLE_OPTIONS_EXTRA_FILES) - foreach (FILE ${BOUT_EXAMPLE_OPTIONS_EXTRA_FILES}) + if(BOUT_EXAMPLE_OPTIONS_EXTRA_FILES) + foreach(FILE ${BOUT_EXAMPLE_OPTIONS_EXTRA_FILES}) bout_copy_file("${FILE}") endforeach() endif() @@ -125,7 +136,6 @@ function(bout_add_example EXAMPLENAME) add_dependencies(build-all-examples ${EXAMPLENAME}) endfunction() - # Add a new integrated or MMS test. By default, the executable is # named like the first source, stripped of its file extension. If no # sources are given, then you probably at least want to set @@ -163,37 +173,51 @@ endfunction() function(bout_add_integrated_or_mms_test BUILD_CHECK_TARGET TESTNAME) set(options USE_RUNTEST USE_DATA_BOUT_INP) set(oneValueArgs EXECUTABLE_NAME PROCESSORS DOWNLOAD DOWNLOAD_NAME) - set(multiValueArgs SOURCES EXTRA_FILES REQUIRES CONFLICTS TESTARGS EXTRA_DEPENDS) - cmake_parse_arguments(BOUT_TEST_OPTIONS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN}) + set(multiValueArgs SOURCES EXTRA_FILES REQUIRES CONFLICTS TESTARGS + EXTRA_DEPENDS + ) + cmake_parse_arguments( + BOUT_TEST_OPTIONS "${options}" "${oneValueArgs}" "${multiValueArgs}" + ${ARGN} + ) - bout_handle_requires_conflicts("test" ${TESTNAME} + bout_handle_requires_conflicts( + "test" ${TESTNAME} REQUIRES ${BOUT_TEST_OPTIONS_REQUIRES} CONFLICTS ${BOUT_TEST_OPTIONS_CONFLICTS} - ) + ) - if (BOUT_TEST_OPTIONS_SOURCES) + if(BOUT_TEST_OPTIONS_SOURCES) # We've got some sources, so compile them into an executable and # link against BOUT++ add_executable(${TESTNAME} ${BOUT_TEST_OPTIONS_SOURCES}) target_link_libraries(${TESTNAME} bout++) - target_include_directories(${TESTNAME} PRIVATE $) + target_include_directories( + ${TESTNAME} PRIVATE $ + ) set_target_properties(${TESTNAME} PROPERTIES FOLDER tests/integrated) # Set the name of the executable. We either take it as an option, # or use the first source file, stripping the file suffix - if (BOUT_TEST_OPTIONS_EXECUTABLE_NAME) - set_target_properties(${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_OPTIONS_EXECUTABLE_NAME}) + if(BOUT_TEST_OPTIONS_EXECUTABLE_NAME) + set_target_properties( + ${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_OPTIONS_EXECUTABLE_NAME} + ) else() # If more than one source file, just get the first one list(LENGTH ${BOUT_TEST_OPTIONS_SOURCES} BOUT_SOURCES_LENGTH) - if (BOUT_SOURCES_LENGTH GREATER 0) + if(BOUT_SOURCES_LENGTH GREATER 0) list(GET ${BOUT_TEST_OPTIONS_SOURCES} 0 BOUT_TEST_FIRST_SOURCE) else() set(BOUT_TEST_FIRST_SOURCE ${BOUT_TEST_OPTIONS_SOURCES}) endif() # Strip the directory and file extension from the source file - get_filename_component(BOUT_TEST_EXECUTABLE_NAME ${BOUT_TEST_FIRST_SOURCE} NAME_WE) - set_target_properties(${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_EXECUTABLE_NAME}) + get_filename_component( + BOUT_TEST_EXECUTABLE_NAME ${BOUT_TEST_FIRST_SOURCE} NAME_WE + ) + set_target_properties( + ${TESTNAME} PROPERTIES OUTPUT_NAME ${BOUT_TEST_EXECUTABLE_NAME} + ) endif() # Add the test to the build-check-integrated-tests target @@ -202,54 +226,57 @@ function(bout_add_integrated_or_mms_test BUILD_CHECK_TARGET TESTNAME) add_custom_target(${TESTNAME}) endif() - if (BOUT_TEST_OPTIONS_DOWNLOAD) - if (NOT BOUT_TEST_OPTIONS_DOWNLOAD_NAME) + if(BOUT_TEST_OPTIONS_DOWNLOAD) + if(NOT BOUT_TEST_OPTIONS_DOWNLOAD_NAME) message(FATAL_ERROR "We need DOWNLOAD_NAME if we should DOWNLOAD!") endif() - set(output ) - add_custom_command(OUTPUT ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} - COMMAND wget ${BOUT_TEST_OPTIONS_DOWNLOAD} -O ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} $ENV{BOUT_TEST_DOWNLOAD_FLAGS} + set(output) + add_custom_command( + OUTPUT ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + COMMAND wget ${BOUT_TEST_OPTIONS_DOWNLOAD} -O + ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} $ENV{BOUT_TEST_DOWNLOAD_FLAGS} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Downloading ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME}" - ) - add_custom_target(download_test_data DEPENDS ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME}) + ) + add_custom_target( + download_test_data DEPENDS ${BOUT_TEST_OPTIONS_DOWNLOAD_NAME} + ) add_dependencies(${TESTNAME} download_test_data) endif() - if (BOUT_TEST_OPTIONS_EXTRA_DEPENDS) + if(BOUT_TEST_OPTIONS_EXTRA_DEPENDS) add_dependencies(${TESTNAME} ${BOUT_TEST_OPTIONS_EXTRA_DEPENDS}) endif() - if (NOT BOUT_TEST_OPTIONS_PROCESSORS) + if(NOT BOUT_TEST_OPTIONS_PROCESSORS) set(BOUT_TEST_OPTIONS_PROCESSORS 1) endif() # Set the actual test command - if (BOUT_TEST_OPTIONS_USE_RUNTEST) - add_test(NAME ${TESTNAME} - COMMAND ./runtest ${BOUT_TEST_OPTIONS_TESTARGS} - ) - set_tests_properties(${TESTNAME} PROPERTIES - ENVIRONMENT PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} - ) + if(BOUT_TEST_OPTIONS_USE_RUNTEST) + add_test(NAME ${TESTNAME} COMMAND ./runtest ${BOUT_TEST_OPTIONS_TESTARGS}) + set_tests_properties( + ${TESTNAME} PROPERTIES ENVIRONMENT + PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} + ) bout_copy_file(runtest) else() add_test(NAME ${TESTNAME} COMMAND ${TESTNAME} ${BOUT_TEST_OPTIONS_TESTARGS}) endif() - set_tests_properties(${TESTNAME} PROPERTIES - PROCESSORS ${BOUT_TEST_OPTIONS_PROCESSORS} - PROCESSOR_AFFINITY ON - ) + set_tests_properties( + ${TESTNAME} PROPERTIES PROCESSORS ${BOUT_TEST_OPTIONS_PROCESSORS} + PROCESSOR_AFFINITY ON + ) # Copy the input file if needed - if (BOUT_TEST_OPTIONS_USE_DATA_BOUT_INP) + if(BOUT_TEST_OPTIONS_USE_DATA_BOUT_INP) bout_copy_file(data/BOUT.inp) endif() # Copy any other needed files - if (BOUT_TEST_OPTIONS_EXTRA_FILES) - foreach (FILE ${BOUT_TEST_OPTIONS_EXTRA_FILES}) + if(BOUT_TEST_OPTIONS_EXTRA_FILES) + foreach(FILE ${BOUT_TEST_OPTIONS_EXTRA_FILES}) bout_copy_file("${FILE}") endforeach() endif() @@ -257,7 +284,9 @@ endfunction() # Add a new integrated test. See `bout_add_integrated_or_mms_test` for arguments function(bout_add_integrated_test TESTNAME) - bout_add_integrated_or_mms_test(build-check-integrated-tests ${TESTNAME} ${ARGV}) + bout_add_integrated_or_mms_test( + build-check-integrated-tests ${TESTNAME} ${ARGV} + ) endfunction() # Add a new MMS test. See `bout_add_integrated_or_mms_test` for arguments @@ -270,13 +299,18 @@ endfunction() # Taken from https://github.com/conan-io/conan/issues/2125#issuecomment-351176653 function(bout_add_library_alias dst src) add_library(${dst} INTERFACE IMPORTED) - foreach(name INTERFACE_LINK_LIBRARIES INTERFACE_INCLUDE_DIRECTORIES INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS) - get_property(value TARGET ${src} PROPERTY ${name} ) + foreach(name INTERFACE_LINK_LIBRARIES INTERFACE_INCLUDE_DIRECTORIES + INTERFACE_COMPILE_DEFINITIONS INTERFACE_COMPILE_OPTIONS + ) + get_property( + value + TARGET ${src} + PROPERTY ${name} + ) set_property(TARGET ${dst} PROPERTY ${name} ${value}) endforeach() endfunction() - # Call nx-config with an argument, and append the resulting path to a list # Taken from https://github.com/LiamBindle/geos-chem/blob/feature/CMake/CMakeScripts/FindNetCDF.cmake function(bout_inspect_netcdf_config VAR NX_CONFIG ARG) @@ -285,7 +319,10 @@ function(bout_inspect_netcdf_config VAR NX_CONFIG ARG) OUTPUT_VARIABLE NX_CONFIG_OUTPUT OUTPUT_STRIP_TRAILING_WHITESPACE ) - if (NX_CONFIG_OUTPUT) - set(${VAR} ${NX_CONFIG_OUTPUT} PARENT_SCOPE) + if(NX_CONFIG_OUTPUT) + set(${VAR} + ${NX_CONFIG_OUTPUT} + PARENT_SCOPE + ) endif() endfunction() diff --git a/cmake/BuildType.cmake b/cmake/BuildType.cmake index c4dd7ff73d..7aced6d8ad 100644 --- a/cmake/BuildType.cmake +++ b/cmake/BuildType.cmake @@ -12,10 +12,17 @@ set(default_build_type "RelWithDebInfo") if(NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES) - message(STATUS "Setting build type to '${default_build_type}' as none was specified.") - set(CMAKE_BUILD_TYPE "${default_build_type}" CACHE - STRING "Choose the type of build." FORCE) + message( + STATUS + "Setting build type to '${default_build_type}' as none was specified." + ) + set(CMAKE_BUILD_TYPE + "${default_build_type}" + CACHE STRING "Choose the type of build." FORCE + ) # Set the possible values of build type for cmake-gui - set_property(CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS - "Debug" "Release" "MinSizeRel" "RelWithDebInfo") + set_property( + CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "MinSizeRel" + "RelWithDebInfo" + ) endif() diff --git a/cmake/CorrectWindowsPaths.cmake b/cmake/CorrectWindowsPaths.cmake index 09bcdd67dc..cd2ac529ca 100644 --- a/cmake/CorrectWindowsPaths.cmake +++ b/cmake/CorrectWindowsPaths.cmake @@ -4,11 +4,9 @@ # This uses the command cygpath (provided by cygwin) to convert # unix-style paths into paths useable by cmake on windows -macro (CONVERT_CYGWIN_PATH _path) - if (WIN32) - EXECUTE_PROCESS(COMMAND cygpath.exe -m ${${_path}} - OUTPUT_VARIABLE ${_path}) - string (STRIP ${${_path}} ${_path}) - endif (WIN32) -endmacro (CONVERT_CYGWIN_PATH) - +macro(CONVERT_CYGWIN_PATH _path) + if(WIN32) + execute_process(COMMAND cygpath.exe -m ${${_path}} OUTPUT_VARIABLE ${_path}) + string(STRIP ${${_path}} ${_path}) + endif(WIN32) +endmacro(CONVERT_CYGWIN_PATH) diff --git a/cmake/EnableCXXWarningIfSupport.cmake b/cmake/EnableCXXWarningIfSupport.cmake index 6d7a64265f..9b9ae52af5 100644 --- a/cmake/EnableCXXWarningIfSupport.cmake +++ b/cmake/EnableCXXWarningIfSupport.cmake @@ -5,7 +5,7 @@ function(target_enable_cxx_warning_if_supported TARGET) set(multiValueArgs FLAGS) cmake_parse_arguments(TARGET_ENABLE_WARNING "" "" "${multiValueArgs}" ${ARGN}) - foreach (WARNING_FLAG IN LISTS TARGET_ENABLE_WARNING_FLAGS) + foreach(WARNING_FLAG IN LISTS TARGET_ENABLE_WARNING_FLAGS) string(REPLACE "-" "_" WARNING_FLAG_STRIPPED ${WARNING_FLAG}) # Note that gcc ignores unknown flags of the form "-Wno-warning" @@ -13,31 +13,33 @@ function(target_enable_cxx_warning_if_supported TARGET) # positive form as an additional flag which it will choke on (if # it doesn't exist). See: https://gcc.gnu.org/wiki/FAQ#wnowarning string(FIND ${WARNING_FLAG} "Wno-" NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) - if (NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} EQUAL -1) + if(NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} EQUAL -1) set(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} FALSE) else() set(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED} TRUE) endif() - if (IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) + if(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) set(ORIGINAL_FLAG ${WARNING_FLAG}) string(REPLACE "no-" "" WARNING_FLAG ${WARNING_FLAG}) message(STATUS "Found negative flag: ${ORIGINAL_FLAG}\n" - " replaced with ${WARNING_FLAG}") + " replaced with ${WARNING_FLAG}" + ) endif() check_cxx_compiler_flag(${WARNING_FLAG} HAS_FLAG_${WARNING_FLAG_STRIPPED}) - if (IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) + if(IS_NEGATIVE_FLAG_${WARNING_FLAG_STRIPPED}) set(WARNING_FLAG ${ORIGINAL_FLAG}) endif() - if (HAS_FLAG_${WARNING_FLAG_STRIPPED}) - message(STATUS "Warning flag is supported by compiler: ${WARNING_FLAG}") + if(HAS_FLAG_${WARNING_FLAG_STRIPPED}) + message(STATUS "Warning flag is supported by compiler: ${WARNING_FLAG}") - target_compile_options(${TARGET} PRIVATE - $<$>:${WARNING_FLAG} > - $<$:-Xcompiler=${WARNING_FLAG} > + target_compile_options( + ${TARGET} + PRIVATE $<$>:${WARNING_FLAG} > + $<$:-Xcompiler=${WARNING_FLAG} > ) else() message(STATUS "Warning flag not supported by compiler: ${WARNING_FLAG}") diff --git a/cmake/FindBash.cmake b/cmake/FindBash.cmake index feb195f9f1..faa5303646 100644 --- a/cmake/FindBash.cmake +++ b/cmake/FindBash.cmake @@ -11,27 +11,30 @@ # Bash_VERSION - Bash version # Bash_EXECUTABLE - Path to bash executable -find_program(Bash_EXECUTABLE - bash - ) +find_program(Bash_EXECUTABLE bash) mark_as_advanced(Bash_EXECUTABLE) -if (Bash_EXECUTABLE) - execute_process(COMMAND "${Bash_EXECUTABLE}" --version +if(Bash_EXECUTABLE) + execute_process( + COMMAND "${Bash_EXECUTABLE}" --version RESULT_VARIABLE _bash_runs OUTPUT_VARIABLE _bash_stdout OUTPUT_STRIP_TRAILING_WHITESPACE - ) + ) if(_bash_stdout MATCHES "version ([0-9]+\\.[0-9]+\\.[0-9]+)") set(Bash_VERSION "${CMAKE_MATCH_1}") else() - message (WARNING "Failed to determine version of Bash interpreter (${Bash_EXECUTABLE})! Error:\n${_Bash_STDERR}") + message( + WARNING + "Failed to determine version of Bash interpreter (${Bash_EXECUTABLE})! Error:\n${_Bash_STDERR}" + ) endif() endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Bash +find_package_handle_standard_args( + Bash VERSION_VAR Bash_VERSION REQUIRED_VARS Bash_EXECUTABLE - ) +) diff --git a/cmake/FindClangFormat.cmake b/cmake/FindClangFormat.cmake index c940002c3b..cd5ca5865b 100644 --- a/cmake/FindClangFormat.cmake +++ b/cmake/FindClangFormat.cmake @@ -3,27 +3,23 @@ # Taken from https://github.com/ttroy50/cmake-examples commit 64bd54a # This file is under MIT Licence -if (NOT ClangFormat_BIN_NAME) +if(NOT ClangFormat_BIN_NAME) set(ClangFormat_BIN_NAME clang-format) endif() # if custom path check there first -if (ClangFormat_ROOT_DIR) - find_program(ClangFormat_BIN - NAMES - ${ClangFormat_BIN_NAME} - PATHS - "${ClangFormat_ROOT_DIR}" - NO_DEFAULT_PATH) +if(ClangFormat_ROOT_DIR) + find_program( + ClangFormat_BIN + NAMES ${ClangFormat_BIN_NAME} + PATHS "${ClangFormat_ROOT_DIR}" + NO_DEFAULT_PATH + ) endif() find_program(ClangFormat_BIN NAMES ${ClangFormat_BIN_NAME}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args( - ClangFormat - DEFAULT_MSG - ClangFormat_BIN) +find_package_handle_standard_args(ClangFormat DEFAULT_MSG ClangFormat_BIN) -mark_as_advanced( - ClangFormat_BIN) +mark_as_advanced(ClangFormat_BIN) diff --git a/cmake/FindCython.cmake b/cmake/FindCython.cmake index 3b98cde89e..5a58cf4335 100644 --- a/cmake/FindCython.cmake +++ b/cmake/FindCython.cmake @@ -10,20 +10,22 @@ # CYTHON_FOUND - true if Cython was found # CYTHON_VERSION - Cython version -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import cython ; print(cython.__version__)" +execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import cython ; print(cython.__version__)" RESULT_VARIABLE _cython_runs OUTPUT_VARIABLE CYTHON_VERSION OUTPUT_STRIP_TRAILING_WHITESPACE - ) +) -if (${_cython_runs} EQUAL 0) +if(${_cython_runs} EQUAL 0) set(CYTHON_RUNS TRUE) else() set(CYTHON_RUNS FALSE) endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Cython +find_package_handle_standard_args( + Cython VERSION_VAR CYTHON_VERSION REQUIRED_VARS CYTHON_RUNS - ) +) diff --git a/cmake/FindFFTW.cmake b/cmake/FindFFTW.cmake index e1940c687d..7ca5518a6f 100644 --- a/cmake/FindFFTW.cmake +++ b/cmake/FindFFTW.cmake @@ -24,72 +24,78 @@ # ``FFTW_DEBUG`` # Set to TRUE to get extra debugging output -if (FFTW_INCLUDE_DIRS) +if(FFTW_INCLUDE_DIRS) # Already in cache, be silent - set (FFTW_FIND_QUIETLY TRUE) -endif (FFTW_INCLUDE_DIRS) + set(FFTW_FIND_QUIETLY TRUE) +endif(FFTW_INCLUDE_DIRS) -if (EXISTS ${FFTW_ROOT}) +if(EXISTS ${FFTW_ROOT}) # Make sure FFTW_ROOT is an absolute path by setting it as a 'FILEPATH' - set (FFTW_ROOT "" CACHE FILEPATH "Location of the FFTW library") + set(FFTW_ROOT + "" + CACHE FILEPATH "Location of the FFTW library" + ) endif() -find_program(FFTW_WISDOM "fftw-wisdom" +find_program( + FFTW_WISDOM "fftw-wisdom" PATHS "${FFTW_ROOT}" PATH_SUFFIXES bin NO_DEFAULT_PATH DOC "Path to fftw-wisdom executable" - ) +) -find_program(FFTW_WISDOM "fftw-wisdom" - DOC "Path to fftw-wisdom executable" - ) -if (FFTW_DEBUG) +find_program(FFTW_WISDOM "fftw-wisdom" DOC "Path to fftw-wisdom executable") +if(FFTW_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " FFTW_WISDOM = ${FFTW_WISDOM}" - ) + " FFTW_WISDOM = ${FFTW_WISDOM}" + ) endif() get_filename_component(FFTW_WISDOM_TMP "${FFTW_WISDOM}" DIRECTORY) get_filename_component(FFTW_HINT_DIR "${FFTW_WISDOM_TMP}" DIRECTORY) -find_path(FFTW_INCLUDE_DIRS +find_path( + FFTW_INCLUDE_DIRS NAMES fftw3.h DOC "FFTW include directory" HINTS "${FFTW_HINT_DIR}" PATH_SUFFIXES "include" - ) -if (FFTW_DEBUG) +) +if(FFTW_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " FFTW_INCLUDE_DIRS = ${FFTW_INCLUDE_DIRS}" - " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" - ) + " FFTW_INCLUDE_DIRS = ${FFTW_INCLUDE_DIRS}" + " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" + ) endif() -find_library (FFTW_LIBRARIES +find_library( + FFTW_LIBRARIES NAMES fftw3 DOC "FFTW library location" HINTS "${FFTW_HINT_DIR}" PATH_SUFFIXES "lib" "lib64" - ) -if (FFTW_DEBUG) +) +if(FFTW_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " FFTW_LIBRARIES = ${FFTW_LIBRARIES}" - " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" - ) + " FFTW_LIBRARIES = ${FFTW_LIBRARIES}" + " FFTW_HINT_DIR = ${FFTW_HINT_DIR}" + ) endif() # handle the QUIETLY and REQUIRED arguments and set FFTW_FOUND to TRUE if # all listed variables are TRUE -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDE_DIRS) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + FFTW DEFAULT_MSG FFTW_LIBRARIES FFTW_INCLUDE_DIRS +) -mark_as_advanced (FFTW_LIBRARIES FFTW_INCLUDE_DIRS) +mark_as_advanced(FFTW_LIBRARIES FFTW_INCLUDE_DIRS) -if (FFTW_FOUND AND NOT TARGET FFTW::FFTW) +if(FFTW_FOUND AND NOT TARGET FFTW::FFTW) add_library(FFTW::FFTW UNKNOWN IMPORTED) - set_target_properties(FFTW::FFTW PROPERTIES - IMPORTED_LOCATION "${FFTW_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" - ) + set_target_properties( + FFTW::FFTW PROPERTIES IMPORTED_LOCATION "${FFTW_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${FFTW_INCLUDE_DIRS}" + ) endif() diff --git a/cmake/FindHYPRE.cmake b/cmake/FindHYPRE.cmake index 1b9a5ca6f9..d2a1b4746c 100644 --- a/cmake/FindHYPRE.cmake +++ b/cmake/FindHYPRE.cmake @@ -6,54 +6,62 @@ include(FindPackageHandleStandardArgs) find_package(HYPRE CONFIG QUIET) -if (HYPRE_FOUND) +if(HYPRE_FOUND) message(STATUS "Found HYPRE: ${HYPRE_VERSION}") return() endif() -find_path(HYPRE_INCLUDE_DIR +find_path( + HYPRE_INCLUDE_DIR NAMES HYPRE.h - DOC "HYPRE include directories" - REQUIRED + DOC "HYPRE include directories" REQUIRED PATH_SUFFIXES include include/hypre ) -find_library(HYPRE_LIBRARY +find_library( + HYPRE_LIBRARY NAMES HYPRE - DOC "HYPRE library" - REQUIRED + DOC "HYPRE library" REQUIRED PATH_SUFFIXES lib64 lib - ) +) -if (HYPRE_INCLUDE_DIR) +if(HYPRE_INCLUDE_DIR) file(READ "${HYPRE_INCLUDE_DIR}/HYPRE_config.h" HYPRE_CONFIG_FILE) - string(REGEX MATCH ".*#define HYPRE_RELEASE_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*" - _ "${HYPRE_CONFIG_FILE}") + string( + REGEX MATCH + ".*#define HYPRE_RELEASE_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*" + _ "${HYPRE_CONFIG_FILE}" + ) set(HYPRE_VERSION_MAJOR ${CMAKE_MATCH_1}) set(HYPRE_VERSION_MINOR ${CMAKE_MATCH_2}) set(HYPRE_VERSION_PATCH ${CMAKE_MATCH_3}) - set(HYPRE_VERSION "${HYPRE_VERSION_MAJOR}.${HYPRE_VERSION_MINOR}.${HYPRE_VERSION_PATCH}") + set(HYPRE_VERSION + "${HYPRE_VERSION_MAJOR}.${HYPRE_VERSION_MINOR}.${HYPRE_VERSION_PATCH}" + ) endif() -if (HYPRE_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ]" - " HYPRE_ROOT = ${HYPRE_ROOT}" - " HYPRE_INCLUDE_DIR = ${HYPRE_INCLUDE_DIR}" - " HYPRE_LIBRARY = ${HYPRE_LIBRARY}" - ) +if(HYPRE_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ]" + " HYPRE_ROOT = ${HYPRE_ROOT}" + " HYPRE_INCLUDE_DIR = ${HYPRE_INCLUDE_DIR}" + " HYPRE_LIBRARY = ${HYPRE_LIBRARY}" + ) endif() mark_as_advanced(HYPRE_INCLUDE_DIR HYPRE_LIBRARY) -find_package_handle_standard_args(HYPRE +find_package_handle_standard_args( + HYPRE REQUIRED_VARS HYPRE_LIBRARY HYPRE_INCLUDE_DIR VERSION_VAR HYPRE_VERSION - ) +) -if (HYPRE_FOUND AND NOT TARGET HYPRE::HYPRE) +if(HYPRE_FOUND AND NOT TARGET HYPRE::HYPRE) add_library(HYPRE::HYPRE UNKNOWN IMPORTED) - set_target_properties(HYPRE::HYPRE PROPERTIES - IMPORTED_LOCATION "${HYPRE_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${HYPRE_INCLUDE_DIR}" - ) + set_target_properties( + HYPRE::HYPRE + PROPERTIES IMPORTED_LOCATION "${HYPRE_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${HYPRE_INCLUDE_DIR}" + ) endif() diff --git a/cmake/FindLibuuid.cmake b/cmake/FindLibuuid.cmake index 91880487a2..2492388b32 100644 --- a/cmake/FindLibuuid.cmake +++ b/cmake/FindLibuuid.cmake @@ -21,38 +21,41 @@ # ``Libuuid_DEBUG`` # Set to TRUE to get extra debugging output -include (FindPackageHandleStandardArgs) +include(FindPackageHandleStandardArgs) -if (WIN32) +if(WIN32) find_package_handle_standard_args(Libuuid DEFAULT_MSG) return() endif() -if (APPLE) +if(APPLE) find_library(CFLIB CoreFoundation) find_package_handle_standard_args(Libuuid DEFAULT_MSG CFLIB) mark_as_advanced(${CFLIB}) - if (Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) + if(Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) add_library(Libuuid::libuuid UNKNOWN IMPORTED) - set_target_properties(Libuuid::libuuid PROPERTIES - IMPORTED_LOCATION ${CFLIB} - ) + set_target_properties( + Libuuid::libuuid PROPERTIES IMPORTED_LOCATION ${CFLIB} + ) endif() return() -endif () +endif() find_path(Libuuid_INCLUDE_DIRS uuid/uuid.h) find_library(Libuuid_LIBRARIES uuid) -find_package_handle_standard_args(Libuuid DEFAULT_MSG Libuuid_LIBRARIES Libuuid_INCLUDE_DIRS) +find_package_handle_standard_args( + Libuuid DEFAULT_MSG Libuuid_LIBRARIES Libuuid_INCLUDE_DIRS +) mark_as_advanced(Libuuid_LIBRARIES Libuuid_INCLUDE_DIRS) -if (Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) +if(Libuuid_FOUND AND NOT TARGET Libuuid::libuuid) add_library(Libuuid::libuuid UNKNOWN IMPORTED) - set_target_properties(Libuuid::libuuid PROPERTIES - IMPORTED_LOCATION "${Libuuid_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${Libuuid_INCLUDE_DIRS}" - ) + set_target_properties( + Libuuid::libuuid + PROPERTIES IMPORTED_LOCATION "${Libuuid_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${Libuuid_INCLUDE_DIRS}" + ) endif() diff --git a/cmake/FindNumpy.cmake b/cmake/FindNumpy.cmake index b6de6e3e35..cfdbdcc96c 100644 --- a/cmake/FindNumpy.cmake +++ b/cmake/FindNumpy.cmake @@ -11,46 +11,57 @@ # Numpy_VERSION # Numpy_INCLUDE_DIR - find_package(Python3 3.6 COMPONENTS Interpreter Development) -if (NOT Python3_FOUND) - message(STATUS "Could not find numpy as python was not found. Maybe the developement package is missing?") +if(NOT Python3_FOUND) + message( + STATUS + "Could not find numpy as python was not found. Maybe the developement package is missing?" + ) set(Numpy_FOUND ${Python3_FOUND}) return() endif() -if (NOT Numpy_FOUND) - execute_process(COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.__version__)" +if(NOT Numpy_FOUND) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.__version__)" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE Numpy_VERSION - ) - execute_process(COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.get_include())" + ) + execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import numpy ; print(numpy.get_include())" OUTPUT_STRIP_TRAILING_WHITESPACE OUTPUT_VARIABLE _numpy_include_dirs - ) + ) endif() -if (Numpy_DEBUG) - message(STATUS "Looking for numpy headers in: ${_numpy_include_dirs} ${Python3_INCLUDE_DIRS}") +if(Numpy_DEBUG) + message( + STATUS + "Looking for numpy headers in: ${_numpy_include_dirs} ${Python3_INCLUDE_DIRS}" + ) endif() -find_path(Numpy_INCLUDE_DIR - numpy/arrayobject.h +find_path( + Numpy_INCLUDE_DIR numpy/arrayobject.h PATHS "${_numpy_include_dirs}" "${Python3_INCLUDE_DIRS}" PATH_SUFFIXES numpy/core/include - ) +) -if (NOT Numpy_INCLUDE_DIR) - message(STATUS "Numpy headers not found -- do you need to install the development package?") +if(NOT Numpy_INCLUDE_DIR) + message( + STATUS + "Numpy headers not found -- do you need to install the development package?" + ) endif() set(Numpy_INCLUDE_DIRS ${Numpy_INCLUDE_DIR}) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(Numpy +find_package_handle_standard_args( + Numpy VERSION_VAR Numpy_VERSION REQUIRED_VARS Numpy_INCLUDE_DIR - ) +) mark_as_advanced(Numpy_INCLUDE_DIR) diff --git a/cmake/FindPETSc.cmake b/cmake/FindPETSc.cmake index c93d464673..f0595a7a68 100644 --- a/cmake/FindPETSc.cmake +++ b/cmake/FindPETSc.cmake @@ -25,23 +25,23 @@ find_package(MPI REQUIRED) -set(PETSC_VALID_COMPONENTS - C - CXX) +set(PETSC_VALID_COMPONENTS C CXX) if(NOT PETSc_FIND_COMPONENTS) - get_property (_enabled_langs GLOBAL PROPERTY ENABLED_LANGUAGES) - if ("C" IN_LIST _enabled_langs) + get_property(_enabled_langs GLOBAL PROPERTY ENABLED_LANGUAGES) + if("C" IN_LIST _enabled_langs) set(PETSC_LANGUAGE_BINDINGS "C") - else () + else() set(PETSC_LANGUAGE_BINDINGS "CXX") - endif () + endif() else() # Right now, this is designed for compatability with the --with-clanguage option, so # only allow one item in the components list. list(LENGTH ${PETSc_FIND_COMPONENTS} components_length) if(${components_length} GREATER 1) - message(FATAL_ERROR "Only one component for PETSc is allowed to be specified") + message( + FATAL_ERROR "Only one component for PETSc is allowed to be specified" + ) endif() # This is a stub for allowing multiple components should that time ever come. Perhaps # to also test Fortran bindings? @@ -61,218 +61,328 @@ if(NOT PETSC_DIR) endif() endif() -function (petsc_get_version) - if (EXISTS "${PETSC_DIR}/include/petscversion.h") - file (STRINGS "${PETSC_DIR}/include/petscversion.h" vstrings REGEX "#define PETSC_VERSION_(RELEASE|MAJOR|MINOR|SUBMINOR|PATCH) ") - foreach (line ${vstrings}) - string (REGEX REPLACE " +" ";" fields ${line}) # break line into three fields (the first is always "#define") - list (GET fields 1 var) - list (GET fields 2 val) - set (${var} ${val} PARENT_SCOPE) - set (${var} ${val}) # Also in local scope so we have access below - endforeach () - if (PETSC_VERSION_RELEASE) - if ($(PETSC_VERSION_PATCH) GREATER 0) - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}p${PETSC_VERSION_PATCH}" CACHE INTERNAL "PETSc version") - else () - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}" CACHE INTERNAL "PETSc version") - endif () - else () +function(petsc_get_version) + if(EXISTS "${PETSC_DIR}/include/petscversion.h") + file(STRINGS "${PETSC_DIR}/include/petscversion.h" vstrings + REGEX "#define PETSC_VERSION_(RELEASE|MAJOR|MINOR|SUBMINOR|PATCH) " + ) + foreach(line ${vstrings}) + string(REGEX REPLACE " +" ";" fields ${line} + )# break line into three fields (the first is always "#define") + list(GET fields 1 var) + list(GET fields 2 val) + set(${var} + ${val} + PARENT_SCOPE + ) + set(${var} ${val}) # Also in local scope so we have access below + endforeach() + if(PETSC_VERSION_RELEASE) + if($(PETSC_VERSION_PATCH) GREATER 0) + set(PETSC_VERSION + "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}p${PETSC_VERSION_PATCH}" + CACHE INTERNAL "PETSc version" + ) + else() + set(PETSC_VERSION + "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}" + CACHE INTERNAL "PETSc version" + ) + endif() + else() # make dev version compare higher than any patch level of a released version - set (PETSC_VERSION "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}.99" CACHE INTERNAL "PETSc version") - endif () - else () - message (SEND_ERROR "PETSC_DIR can not be used, ${PETSC_DIR}/include/petscversion.h does not exist") - endif () -endfunction () + set(PETSC_VERSION + "${PETSC_VERSION_MAJOR}.${PETSC_VERSION_MINOR}.${PETSC_VERSION_SUBMINOR}.99" + CACHE INTERNAL "PETSc version" + ) + endif() + else() + message( + SEND_ERROR + "PETSC_DIR can not be used, ${PETSC_DIR}/include/petscversion.h does not exist" + ) + endif() +endfunction() # Debian uses versioned paths e.g /usr/lib/petscdir/3.5/ -file (GLOB DEB_PATHS "/usr/lib/petscdir/*") +file(GLOB DEB_PATHS "/usr/lib/petscdir/*") -find_path (PETSC_DIR include/petsc.h +find_path( + PETSC_DIR include/petsc.h HINTS ENV PETSC_DIR - PATHS - /usr/lib/petsc - # Debian paths - ${DEB_PATHS} - # Arch Linux path - /opt/petsc/linux-c-opt - # MacPorts path - /opt/local/lib/petsc - $ENV{HOME}/petsc - DOC "PETSc Directory") - -find_program (MAKE_EXECUTABLE NAMES make gmake) - -if (PETSC_DIR AND NOT PETSC_ARCH) - set (_petsc_arches - $ENV{PETSC_ARCH} # If set, use environment variable first - linux-gnu-c-debug linux-gnu-c-opt # Debian defaults - x86_64-unknown-linux-gnu i386-unknown-linux-gnu) - set (petscconf "NOTFOUND" CACHE FILEPATH "Cleared" FORCE) - foreach (arch ${_petsc_arches}) - if (NOT PETSC_ARCH) - find_path (petscconf petscconf.h + PATHS /usr/lib/petsc + # Debian paths + ${DEB_PATHS} + # Arch Linux path + /opt/petsc/linux-c-opt + # MacPorts path + /opt/local/lib/petsc + $ENV{HOME}/petsc + DOC "PETSc Directory" +) + +find_program(MAKE_EXECUTABLE NAMES make gmake) + +if(PETSC_DIR AND NOT PETSC_ARCH) + set(_petsc_arches + $ENV{PETSC_ARCH} # If set, use environment variable first + linux-gnu-c-debug linux-gnu-c-opt # Debian defaults + x86_64-unknown-linux-gnu i386-unknown-linux-gnu + ) + set(petscconf + "NOTFOUND" + CACHE FILEPATH "Cleared" FORCE + ) + foreach(arch ${_petsc_arches}) + if(NOT PETSC_ARCH) + find_path( + petscconf petscconf.h HINTS ${PETSC_DIR} PATH_SUFFIXES ${arch}/include bmake/${arch} - NO_DEFAULT_PATH) - if (petscconf) - set (PETSC_ARCH "${arch}" CACHE STRING "PETSc build architecture") - endif (petscconf) - endif (NOT PETSC_ARCH) - endforeach (arch) - set (petscconf "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE) -endif (PETSC_DIR AND NOT PETSC_ARCH) - -set (petsc_slaves LIBRARIES_SYS LIBRARIES_VEC LIBRARIES_MAT LIBRARIES_DM LIBRARIES_KSP LIBRARIES_SNES LIBRARIES_TS - INCLUDE_DIR INCLUDE_CONF) -include (FindPackageMultipass) -find_package_multipass (PETSc petsc_config_current - STATES DIR ARCH - DEPENDENTS INCLUDES LIBRARIES COMPILER MPIEXEC ${petsc_slaves}) + NO_DEFAULT_PATH + ) + if(petscconf) + set(PETSC_ARCH + "${arch}" + CACHE STRING "PETSc build architecture" + ) + endif(petscconf) + endif(NOT PETSC_ARCH) + endforeach(arch) + set(petscconf + "NOTFOUND" + CACHE INTERNAL "Scratch variable" FORCE + ) +endif(PETSC_DIR AND NOT PETSC_ARCH) + +set(petsc_slaves + LIBRARIES_SYS + LIBRARIES_VEC + LIBRARIES_MAT + LIBRARIES_DM + LIBRARIES_KSP + LIBRARIES_SNES + LIBRARIES_TS + INCLUDE_DIR + INCLUDE_CONF +) +include(FindPackageMultipass) +find_package_multipass( + PETSc + petsc_config_current + STATES + DIR + ARCH + DEPENDENTS + INCLUDES + LIBRARIES + COMPILER + MPIEXEC + ${petsc_slaves} +) # Determine whether the PETSc layout is old-style (through 2.3.3) or # new-style (>= 3.0.0) -if (EXISTS "${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/petscvariables") # > 3.5 - set (petsc_conf_rules "${PETSC_DIR}/lib/petsc/conf/rules") - set (petsc_conf_variables "${PETSC_DIR}/lib/petsc/conf/variables") -elseif (EXISTS "${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h") # > 2.3.3 - set (petsc_conf_rules "${PETSC_DIR}/conf/rules") - set (petsc_conf_variables "${PETSC_DIR}/conf/variables") -elseif (EXISTS "${PETSC_DIR}/bmake/${PETSC_ARCH}/petscconf.h") # <= 2.3.3 - set (petsc_conf_rules "${PETSC_DIR}/bmake/common/rules") - set (petsc_conf_variables "${PETSC_DIR}/bmake/common/variables") -elseif (PETSC_DIR) - message (SEND_ERROR "The pair PETSC_DIR=${PETSC_DIR} PETSC_ARCH=${PETSC_ARCH} do not specify a valid PETSc installation") -endif () - -if (petsc_conf_rules AND petsc_conf_variables AND NOT petsc_config_current) +if(EXISTS "${PETSC_DIR}/${PETSC_ARCH}/lib/petsc/conf/petscvariables") # > 3.5 + set(petsc_conf_rules "${PETSC_DIR}/lib/petsc/conf/rules") + set(petsc_conf_variables "${PETSC_DIR}/lib/petsc/conf/variables") +elseif(EXISTS "${PETSC_DIR}/${PETSC_ARCH}/include/petscconf.h") # > 2.3.3 + set(petsc_conf_rules "${PETSC_DIR}/conf/rules") + set(petsc_conf_variables "${PETSC_DIR}/conf/variables") +elseif(EXISTS "${PETSC_DIR}/bmake/${PETSC_ARCH}/petscconf.h") # <= 2.3.3 + set(petsc_conf_rules "${PETSC_DIR}/bmake/common/rules") + set(petsc_conf_variables "${PETSC_DIR}/bmake/common/variables") +elseif(PETSC_DIR) + message( + SEND_ERROR + "The pair PETSC_DIR=${PETSC_DIR} PETSC_ARCH=${PETSC_ARCH} do not specify a valid PETSc installation" + ) +endif() + +if(petsc_conf_rules + AND petsc_conf_variables + AND NOT petsc_config_current +) petsc_get_version() # Put variables into environment since they are needed to get # configuration (petscvariables) in the PETSc makefile - set (ENV{PETSC_DIR} "${PETSC_DIR}") - set (ENV{PETSC_ARCH} "${PETSC_ARCH}") + set(ENV{PETSC_DIR} "${PETSC_DIR}") + set(ENV{PETSC_ARCH} "${PETSC_ARCH}") # A temporary makefile to probe the PETSc configuration - set (petsc_config_makefile "${PROJECT_BINARY_DIR}/Makefile.petsc") - file (WRITE "${petsc_config_makefile}" -"## This file was autogenerated by FindPETSc.cmake + set(petsc_config_makefile "${PROJECT_BINARY_DIR}/Makefile.petsc") + file( + WRITE "${petsc_config_makefile}" + "## This file was autogenerated by FindPETSc.cmake # PETSC_DIR = ${PETSC_DIR} # PETSC_ARCH = ${PETSC_ARCH} include ${petsc_conf_rules} include ${petsc_conf_variables} show : \t-@echo -n \${\${VARIABLE}} -") - - macro (PETSC_GET_VARIABLE name var) - set (${var} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - execute_process (COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${petsc_config_makefile} show VARIABLE=${name} +" + ) + + macro(PETSC_GET_VARIABLE name var) + set(${var} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + execute_process( + COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f + ${petsc_config_makefile} show VARIABLE=${name} OUTPUT_VARIABLE ${var} - RESULT_VARIABLE petsc_return) - endmacro (PETSC_GET_VARIABLE) - petsc_get_variable (PETSC_LIB_DIR petsc_lib_dir) - petsc_get_variable (PETSC_EXTERNAL_LIB_BASIC petsc_libs_external) - petsc_get_variable (PETSC_CCPPFLAGS petsc_cpp_line) - petsc_get_variable (PETSC_INCLUDE petsc_include) - petsc_get_variable (PCC petsc_cc) - petsc_get_variable (PCC_FLAGS petsc_cc_flags) - petsc_get_variable (MPIEXEC petsc_mpiexec) + RESULT_VARIABLE petsc_return + ) + endmacro(PETSC_GET_VARIABLE) + petsc_get_variable(PETSC_LIB_DIR petsc_lib_dir) + petsc_get_variable(PETSC_EXTERNAL_LIB_BASIC petsc_libs_external) + petsc_get_variable(PETSC_CCPPFLAGS petsc_cpp_line) + petsc_get_variable(PETSC_INCLUDE petsc_include) + petsc_get_variable(PCC petsc_cc) + petsc_get_variable(PCC_FLAGS petsc_cc_flags) + petsc_get_variable(MPIEXEC petsc_mpiexec) # We are done with the temporary Makefile, calling PETSC_GET_VARIABLE after this point is invalid! - file (REMOVE ${petsc_config_makefile}) + file(REMOVE ${petsc_config_makefile}) - include (ResolveCompilerPaths) + include(ResolveCompilerPaths) # Extract include paths and libraries from compile command line - resolve_includes (petsc_includes_all "${petsc_cpp_line}") + resolve_includes(petsc_includes_all "${petsc_cpp_line}") #on windows we need to make sure we're linking against the right #runtime library - if (WIN32) - if (petsc_cc_flags MATCHES "-MT") + if(WIN32) + if(petsc_cc_flags MATCHES "-MT") set(using_md False) - foreach(flag_var - CMAKE_C_FLAGS CMAKE_C_FLAGS_DEBUG CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_MINSIZEREL CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS CMAKE_CXX_FLAGS_DEBUG CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_MINSIZEREL CMAKE_CXX_FLAGS_RELWITHDEBINFO) + foreach( + flag_var + CMAKE_C_FLAGS + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_MINSIZEREL + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_MINSIZEREL + CMAKE_CXX_FLAGS_RELWITHDEBINFO + ) if(${flag_var} MATCHES "/MD") set(using_md True) endif(${flag_var} MATCHES "/MD") endforeach(flag_var) if(${using_md} MATCHES "True") - message(WARNING "PETSc was built with /MT, but /MD is currently set. - See http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F") + message( + WARNING + "PETSc was built with /MT, but /MD is currently set. + See http://www.cmake.org/Wiki/CMake_FAQ#How_can_I_build_my_MSVC_application_with_a_static_runtime.3F" + ) endif(${using_md} MATCHES "True") - endif (petsc_cc_flags MATCHES "-MT") - endif (WIN32) + endif(petsc_cc_flags MATCHES "-MT") + endif(WIN32) - include (CorrectWindowsPaths) + include(CorrectWindowsPaths) convert_cygwin_path(petsc_lib_dir) - message (STATUS "petsc_lib_dir ${petsc_lib_dir}") - - macro (PETSC_FIND_LIBRARY suffix name) - set (PETSC_LIBRARY_${suffix} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) # Clear any stale value, if we got here, we need to find it again - if (WIN32) - set (libname lib${name}) #windows expects "libfoo", linux expects "foo" - else (WIN32) - set (libname ${name}) - endif (WIN32) - find_library (PETSC_LIBRARY_${suffix} NAMES ${libname} HINTS ${petsc_lib_dir} NO_DEFAULT_PATH) - set (PETSC_LIBRARIES_${suffix} "${PETSC_LIBRARY_${suffix}}") - mark_as_advanced (PETSC_LIBRARY_${suffix}) - endmacro (PETSC_FIND_LIBRARY suffix name) + message(STATUS "petsc_lib_dir ${petsc_lib_dir}") + + macro(PETSC_FIND_LIBRARY suffix name) + set(PETSC_LIBRARY_${suffix} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) # Clear any stale value, if we got here, we need to find it again + if(WIN32) + set(libname lib${name}) #windows expects "libfoo", linux expects "foo" + else(WIN32) + set(libname ${name}) + endif(WIN32) + find_library( + PETSC_LIBRARY_${suffix} + NAMES ${libname} + HINTS ${petsc_lib_dir} + NO_DEFAULT_PATH + ) + set(PETSC_LIBRARIES_${suffix} "${PETSC_LIBRARY_${suffix}}") + mark_as_advanced(PETSC_LIBRARY_${suffix}) + endmacro( + PETSC_FIND_LIBRARY + suffix + name + ) # Look for petscvec first, if it doesn't exist, we must be using single-library - petsc_find_library (VEC petscvec) - if (PETSC_LIBRARY_VEC) - petsc_find_library (SYS "petscsys;petsc") # libpetscsys is called libpetsc prior to 3.1 (when single-library was introduced) - petsc_find_library (MAT petscmat) - petsc_find_library (DM petscdm) - petsc_find_library (KSP petscksp) - petsc_find_library (SNES petscsnes) - petsc_find_library (TS petscts) - macro (PETSC_JOIN libs deps) - list (APPEND PETSC_LIBRARIES_${libs} ${PETSC_LIBRARIES_${deps}}) - endmacro (PETSC_JOIN libs deps) - petsc_join (VEC SYS) - petsc_join (MAT VEC) - petsc_join (DM MAT) - petsc_join (KSP DM) - petsc_join (SNES KSP) - petsc_join (TS SNES) - petsc_join (ALL TS) - else () - set (PETSC_LIBRARY_VEC "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) # There is no libpetscvec - petsc_find_library (SINGLE petsc) + petsc_find_library(VEC petscvec) + if(PETSC_LIBRARY_VEC) + petsc_find_library(SYS "petscsys;petsc") + # libpetscsys is called libpetsc prior to 3.1 (when single-library was introduced) + petsc_find_library(MAT petscmat) + petsc_find_library(DM petscdm) + petsc_find_library(KSP petscksp) + petsc_find_library(SNES petscsnes) + petsc_find_library(TS petscts) + macro(PETSC_JOIN libs deps) + list(APPEND PETSC_LIBRARIES_${libs} ${PETSC_LIBRARIES_${deps}}) + endmacro( + PETSC_JOIN + libs + deps + ) + petsc_join(VEC SYS) + petsc_join(MAT VEC) + petsc_join(DM MAT) + petsc_join(KSP DM) + petsc_join(SNES KSP) + petsc_join(TS SNES) + petsc_join(ALL TS) + else() + set(PETSC_LIBRARY_VEC + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) # There is no libpetscvec + petsc_find_library(SINGLE petsc) # Debian 9/Ubuntu 16.04 uses _real and _complex extensions when using libraries in /usr/lib/petsc. - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library (SINGLE petsc_real) + if(NOT PETSC_LIBRARY_SINGLE) + petsc_find_library(SINGLE petsc_real) endif() - if (NOT PETSC_LIBRARY_SINGLE) - petsc_find_library (SINGLE petsc_complex) + if(NOT PETSC_LIBRARY_SINGLE) + petsc_find_library(SINGLE petsc_complex) endif() - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - set (PETSC_LIBRARIES_${pkg} "${PETSC_LIBRARY_SINGLE}") - endforeach () - endif () - if (PETSC_LIBRARY_TS) - message (STATUS "Recognized PETSc install with separate libraries for each package") - else () - message (STATUS "Recognized PETSc install with single library for all packages") - endif () + foreach( + pkg + SYS + VEC + MAT + DM + KSP + SNES + TS + ALL + ) + set(PETSC_LIBRARIES_${pkg} "${PETSC_LIBRARY_SINGLE}") + endforeach() + endif() + if(PETSC_LIBRARY_TS) + message( + STATUS "Recognized PETSc install with separate libraries for each package" + ) + else() + message( + STATUS "Recognized PETSc install with single library for all packages" + ) + endif() include(Check${PETSC_LANGUAGE_BINDINGS}SourceRuns) - macro (petsc_test_compiles includes libraries runs) - message(STATUS "PETSc test with : ${includes} ${libraries}" ) - if (PETSC_VERSION VERSION_GREATER 3.1) - set (_PETSC_TSDestroy "TSDestroy(&ts)") - else () - set (_PETSC_TSDestroy "TSDestroy(ts)") - endif () + macro(petsc_test_compiles includes libraries runs) + message(STATUS "PETSc test with : ${includes} ${libraries}") + if(PETSC_VERSION VERSION_GREATER 3.1) + set(_PETSC_TSDestroy "TSDestroy(&ts)") + else() + set(_PETSC_TSDestroy "TSDestroy(ts)") + endif() - set(_PETSC_TEST_SOURCE " + set(_PETSC_TEST_SOURCE + " static const char help[] = \"PETSc test program.\"; #include int main(int argc,char *argv[]) { @@ -286,116 +396,197 @@ int main(int argc,char *argv[]) { ierr = PetscFinalize();CHKERRQ(ierr); return 0; } -") - multipass_source_compiles ("${includes}" "${libraries}" "${_PETSC_TEST_SOURCE}" ${runs} "${PETSC_LANGUAGE_BINDINGS}") - if (${${runs}}) - set (PETSC_EXECUTABLE_COMPILES "YES" CACHE BOOL - "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." FORCE) - endif (${${runs}}) - endmacro () - +" + ) + multipass_source_compiles( + "${includes}" "${libraries}" "${_PETSC_TEST_SOURCE}" ${runs} + "${PETSC_LANGUAGE_BINDINGS}" + ) + if(${${runs}}) + set(PETSC_EXECUTABLE_COMPILES + "YES" + CACHE + BOOL + "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." + FORCE + ) + endif(${${runs}}) + endmacro() - find_path (PETSC_INCLUDE_DIR petscts.h + find_path( + PETSC_INCLUDE_DIR petscts.h HINTS "${PETSC_DIR}" PATH_SUFFIXES include - NO_DEFAULT_PATH) - find_path (PETSC_INCLUDE_CONF petscconf.h + NO_DEFAULT_PATH + ) + find_path( + PETSC_INCLUDE_CONF petscconf.h HINTS "${PETSC_DIR}" PATH_SUFFIXES "${PETSC_ARCH}/include" "bmake/${PETSC_ARCH}" - NO_DEFAULT_PATH) - mark_as_advanced (PETSC_INCLUDE_DIR PETSC_INCLUDE_CONF) - set (petsc_includes_minimal ${PETSC_INCLUDE_CONF} ${PETSC_INCLUDE_DIR}) - - file (STRINGS "${PETSC_INCLUDE_CONF}/petscconf.h" PETSC_HAS_OPENMP REGEX "#define PETSC_HAVE_OPENMP 1") - if (PETSC_HAS_OPENMP) + NO_DEFAULT_PATH + ) + mark_as_advanced(PETSC_INCLUDE_DIR PETSC_INCLUDE_CONF) + set(petsc_includes_minimal ${PETSC_INCLUDE_CONF} ${PETSC_INCLUDE_DIR}) + + file(STRINGS "${PETSC_INCLUDE_CONF}/petscconf.h" PETSC_HAS_OPENMP + REGEX "#define PETSC_HAVE_OPENMP 1" + ) + if(PETSC_HAS_OPENMP) find_package(OpenMP REQUIRED) - set (petsc_openmp_library ";OpenMP::OpenMP_${PETSC_LANGUAGE_BINDINGS}") + set(petsc_openmp_library ";OpenMP::OpenMP_${PETSC_LANGUAGE_BINDINGS}") endif() - set (petsc_mpi_include_dirs "${MPI_${PETSC_LANGUAGE_BINDINGS}_INCLUDE_DIRS}") + set(petsc_mpi_include_dirs "${MPI_${PETSC_LANGUAGE_BINDINGS}_INCLUDE_DIRS}") #set (petsc_additional_libraries "MPI::MPI_${PETSC_LANGUAGE_BINDINGS}${petsc_openmp_library}") - petsc_test_compiles ("${petsc_includes_minimal};${petsc_mpi_include_dirs}" - "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_minimal) - if (petsc_works_minimal) - message (STATUS "Minimal PETSc includes and libraries work. This probably means we are building with shared libs.") - set (petsc_includes_needed "${petsc_includes_minimal}") - else (petsc_works_minimal) # Minimal includes fail, see if just adding full includes fixes it - petsc_test_compiles ("${petsc_includes_all};${petsc_mpi_include_dirs}" + petsc_test_compiles( + "${petsc_includes_minimal};${petsc_mpi_include_dirs}" + "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" petsc_works_minimal + ) + if(petsc_works_minimal) + message( + STATUS + "Minimal PETSc includes and libraries work. This probably means we are building with shared libs." + ) + set(petsc_includes_needed "${petsc_includes_minimal}") + else(petsc_works_minimal + )# Minimal includes fail, see if just adding full includes fixes it + petsc_test_compiles( + "${petsc_includes_all};${petsc_mpi_include_dirs}" "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_allincludes) - if (petsc_works_allincludes) # It does, we just need all the includes ( - message (STATUS "PETSc requires extra include paths, but links correctly with only interface libraries. This is an unexpected configuration (but it seems to work fine).") - set (petsc_includes_needed ${petsc_includes_all}) - else (petsc_works_allincludes) # We are going to need to link the external libs explicitly - resolve_libraries (petsc_libraries_external "${petsc_libs_external}") - foreach (pkg SYS VEC MAT DM KSP SNES TS ALL) - list (APPEND PETSC_LIBRARIES_${pkg} ${petsc_libraries_external}) - endforeach (pkg) - petsc_test_compiles ("${petsc_includes_minimal};${petsc_mpi_include_dirs}" + petsc_works_allincludes + ) + if(petsc_works_allincludes) # It does, we just need all the includes ( + message( + STATUS + "PETSc requires extra include paths, but links correctly with only interface libraries. This is an unexpected configuration (but it seems to work fine)." + ) + set(petsc_includes_needed ${petsc_includes_all}) + else(petsc_works_allincludes + )# We are going to need to link the external libs explicitly + resolve_libraries(petsc_libraries_external "${petsc_libs_external}") + foreach( + pkg + SYS + VEC + MAT + DM + KSP + SNES + TS + ALL + ) + list(APPEND PETSC_LIBRARIES_${pkg} ${petsc_libraries_external}) + endforeach(pkg) + petsc_test_compiles( + "${petsc_includes_minimal};${petsc_mpi_include_dirs}" "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_alllibraries) - if (petsc_works_alllibraries) - message (STATUS "PETSc only need minimal includes, but requires explicit linking to all dependencies. This is expected when PETSc is built with static libraries.") - set (petsc_includes_needed ${petsc_includes_minimal}) - else (petsc_works_alllibraries) + petsc_works_alllibraries + ) + if(petsc_works_alllibraries) + message( + STATUS + "PETSc only need minimal includes, but requires explicit linking to all dependencies. This is expected when PETSc is built with static libraries." + ) + set(petsc_includes_needed ${petsc_includes_minimal}) + else(petsc_works_alllibraries) # It looks like we really need everything, should have listened to Matt - set (petsc_includes_needed ${petsc_includes_all}) - petsc_test_compiles ("${petsc_includes_all};${petsc_mpi_include_dirs}" - "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" - petsc_works_all) - if (petsc_works_all) # We fail anyways - message (STATUS "PETSc requires extra include paths and explicit linking to all dependencies. This probably means you have static libraries and something unexpected in PETSc headers.") - else (petsc_works_all) # We fail anyways - message (STATUS "PETSc could not be used, maybe the install is broken.") - endif (petsc_works_all) - endif (petsc_works_alllibraries) - endif (petsc_works_allincludes) - endif (petsc_works_minimal) + set(petsc_includes_needed ${petsc_includes_all}) + petsc_test_compiles( + "${petsc_includes_all};${petsc_mpi_include_dirs}" + "${PETSC_LIBRARIES_TS};${petsc_additional_libraries}" petsc_works_all + ) + if(petsc_works_all) # We fail anyways + message( + STATUS + "PETSc requires extra include paths and explicit linking to all dependencies. This probably means you have static libraries and something unexpected in PETSc headers." + ) + else(petsc_works_all) # We fail anyways + message( + STATUS "PETSc could not be used, maybe the install is broken." + ) + endif(petsc_works_all) + endif(petsc_works_alllibraries) + endif(petsc_works_allincludes) + endif(petsc_works_minimal) # We do an out-of-source build so __FILE__ will be an absolute path, hence __INSDIR__ is superfluous - if (${PETSC_VERSION} VERSION_LESS 3.1) - set (PETSC_DEFINITIONS "-D__SDIR__=\"\"" CACHE STRING "PETSc definitions" FORCE) - else () - set (PETSC_DEFINITIONS "-D__INSDIR__=" CACHE STRING "PETSc definitions" FORCE) - endif () + if(${PETSC_VERSION} VERSION_LESS 3.1) + set(PETSC_DEFINITIONS + "-D__SDIR__=\"\"" + CACHE STRING "PETSc definitions" FORCE + ) + else() + set(PETSC_DEFINITIONS + "-D__INSDIR__=" + CACHE STRING "PETSc definitions" FORCE + ) + endif() # Sometimes this can be used to assist FindMPI.cmake - set (PETSC_MPIEXEC ${petsc_mpiexec} CACHE FILEPATH "Executable for running PETSc MPI programs" FORCE) - set (PETSC_INCLUDES ${petsc_includes_needed} CACHE STRING "PETSc include path" FORCE) - set (PETSC_LIBRARIES ${PETSC_LIBRARIES_ALL} CACHE STRING "PETSc libraries" FORCE) - set (PETSC_COMPILER ${petsc_cc} CACHE FILEPATH "PETSc compiler" FORCE) -endif () + set(PETSC_MPIEXEC + ${petsc_mpiexec} + CACHE FILEPATH "Executable for running PETSc MPI programs" FORCE + ) + set(PETSC_INCLUDES + ${petsc_includes_needed} + CACHE STRING "PETSc include path" FORCE + ) + set(PETSC_LIBRARIES + ${PETSC_LIBRARIES_ALL} + CACHE STRING "PETSc libraries" FORCE + ) + set(PETSC_COMPILER + ${petsc_cc} + CACHE FILEPATH "PETSc compiler" FORCE + ) +endif() -if (NOT PETSC_INCLUDES AND NOT TARGET PETSc::PETSc) +if(NOT PETSC_INCLUDES AND NOT TARGET PETSc::PETSc) find_package(PkgConfig) - if (PkgConfig_FOUND) + if(PkgConfig_FOUND) pkg_search_module(PkgPETSC PETSc>3.4.0 petsc>3.4.0) - set (PETSC_LIBRARIES ${PkgPETSC_LINK_LIBRARIES} CACHE STRING "PETSc libraries" FORCE) - set (PETSC_INCLUDES ${PkgPETSC_INCLUDE_DIRS} CACHE STRING "PETSc include path" FORCE) - set (PETSC_EXECUTABLE_COMPILES "YES" CACHE BOOL - "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." FORCE) + set(PETSC_LIBRARIES + ${PkgPETSC_LINK_LIBRARIES} + CACHE STRING "PETSc libraries" FORCE + ) + set(PETSC_INCLUDES + ${PkgPETSC_INCLUDE_DIRS} + CACHE STRING "PETSc include path" FORCE + ) + set(PETSC_EXECUTABLE_COMPILES + "YES" + CACHE + BOOL + "Can the system successfully run a PETSc executable? This variable can be manually set to \"YES\" to force CMake to accept a given PETSc configuration, but this will almost always result in a broken build. If you change PETSC_DIR, PETSC_ARCH, or PETSC_CURRENT you would have to reset this variable." + FORCE + ) endif() endif() # Note that we have forced values for all these choices. If you # change these, you are telling the system to trust you that they # work. It is likely that you will end up with a broken build. -mark_as_advanced (PETSC_INCLUDES PETSC_LIBRARIES PETSC_COMPILER PETSC_DEFINITIONS PETSC_MPIEXEC PETSC_EXECUTABLE_COMPILES) - -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args (PETSc +mark_as_advanced( + PETSC_INCLUDES PETSC_LIBRARIES PETSC_COMPILER PETSC_DEFINITIONS PETSC_MPIEXEC + PETSC_EXECUTABLE_COMPILES +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + PETSc REQUIRED_VARS PETSC_INCLUDES PETSC_LIBRARIES VERSION_VAR PETSC_VERSION - FAIL_MESSAGE "PETSc could not be found. Be sure to set PETSC_DIR and PETSC_ARCH.") + FAIL_MESSAGE + "PETSc could not be found. Be sure to set PETSC_DIR and PETSC_ARCH." +) -if (PETSC_FOUND) - if (NOT TARGET PETSc::PETSc) +if(PETSC_FOUND) + if(NOT TARGET PETSc::PETSc) add_library(PETSc::PETSc UNKNOWN IMPORTED) list(GET PETSC_LIBRARIES 0 PETSC_LIBRARY) target_link_libraries(PETSc::PETSc INTERFACE "${PETSC_LIBRARIES}") - set_target_properties(PETSc::PETSc PROPERTIES - IMPORTED_LOCATION "${PETSC_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDES}" - ) + set_target_properties( + PETSc::PETSc PROPERTIES IMPORTED_LOCATION "${PETSC_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${PETSC_INCLUDES}" + ) endif() endif() diff --git a/cmake/FindPackageMultipass.cmake b/cmake/FindPackageMultipass.cmake index 99bbace448..b2762f5d28 100644 --- a/cmake/FindPackageMultipass.cmake +++ b/cmake/FindPackageMultipass.cmake @@ -34,99 +34,124 @@ include(CheckCXXSourceCompiles) -macro (FIND_PACKAGE_MULTIPASS _name _current) - string (TOUPPER ${_name} _NAME) - set (_args ${ARGV}) - list (REMOVE_AT _args 0 1) +macro(FIND_PACKAGE_MULTIPASS _name _current) + string(TOUPPER ${_name} _NAME) + set(_args ${ARGV}) + list(REMOVE_AT _args 0 1) - set (_states_current "YES") - list (GET _args 0 _cmd) - if (_cmd STREQUAL "STATES") - list (REMOVE_AT _args 0) - list (GET _args 0 _state) - while (_state AND NOT _state STREQUAL "DEPENDENTS") + set(_states_current "YES") + list(GET _args 0 _cmd) + if(_cmd STREQUAL "STATES") + list(REMOVE_AT _args 0) + list(GET _args 0 _state) + while(_state AND NOT _state STREQUAL "DEPENDENTS") # The name of the stored value for the given state - set (_stored_var PACKAGE_MULTIPASS_${_NAME}_${_state}) - if (NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") - set (_states_current "NO") - endif (NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") - set (${_stored_var} "${${_NAME}_${_state}}" CACHE INTERNAL "Stored state for ${_name}." FORCE) - list (REMOVE_AT _args 0) - list (GET _args 0 _state) - endwhile (_state AND NOT _state STREQUAL "DEPENDENTS") - endif (_cmd STREQUAL "STATES") + set(_stored_var PACKAGE_MULTIPASS_${_NAME}_${_state}) + if(NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") + set(_states_current "NO") + endif(NOT "${${_stored_var}}" STREQUAL "${${_NAME}_${_state}}") + set(${_stored_var} + "${${_NAME}_${_state}}" + CACHE INTERNAL "Stored state for ${_name}." FORCE + ) + list(REMOVE_AT _args 0) + list(GET _args 0 _state) + endwhile(_state AND NOT _state STREQUAL "DEPENDENTS") + endif(_cmd STREQUAL "STATES") - set (_stored ${_NAME}_CURRENT) - if (NOT ${_stored}) - set (${_stored} "YES" CACHE BOOL "Is the configuration for ${_name} current? Set to \"NO\" to reconfigure." FORCE) - set (_states_current "NO") - endif (NOT ${_stored}) + set(_stored ${_NAME}_CURRENT) + if(NOT ${_stored}) + set(${_stored} + "YES" + CACHE + BOOL + "Is the configuration for ${_name} current? Set to \"NO\" to reconfigure." + FORCE + ) + set(_states_current "NO") + endif(NOT ${_stored}) - set (${_current} ${_states_current}) - if (NOT ${_current} AND PACKAGE_MULTIPASS_${_name}_CALLED) - message (STATUS "Clearing ${_name} dependent variables") + set(${_current} ${_states_current}) + if(NOT ${_current} AND PACKAGE_MULTIPASS_${_name}_CALLED) + message(STATUS "Clearing ${_name} dependent variables") # Clear all the dependent variables so that the module can reset them - list (GET _args 0 _cmd) - if (_cmd STREQUAL "DEPENDENTS") - list (REMOVE_AT _args 0) - foreach (dep ${_args}) - set (${_NAME}_${dep} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - endforeach (dep) - endif (_cmd STREQUAL "DEPENDENTS") - set (${_NAME}_FOUND "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - endif () - set (PACKAGE_MULTIPASS_${name}_CALLED YES CACHE INTERNAL "Private" FORCE) -endmacro (FIND_PACKAGE_MULTIPASS) - + list(GET _args 0 _cmd) + if(_cmd STREQUAL "DEPENDENTS") + list(REMOVE_AT _args 0) + foreach(dep ${_args}) + set(${_NAME}_${dep} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + endforeach(dep) + endif(_cmd STREQUAL "DEPENDENTS") + set(${_NAME}_FOUND + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + endif() + set(PACKAGE_MULTIPASS_${name}_CALLED + YES + CACHE INTERNAL "Private" FORCE + ) +endmacro(FIND_PACKAGE_MULTIPASS) -macro (MULTIPASS_SOURCE_RUNS includes libraries source runs language) - include (Check${language}SourceRuns) +macro(MULTIPASS_SOURCE_RUNS includes libraries source runs language) + include(Check${language}SourceRuns) # This is a ridiculous hack. CHECK_${language}_SOURCE_* thinks that if the # *name* of the return variable doesn't change, then the test does # not need to be re-run. We keep an internal count which we # increment to guarantee that every test name is unique. If we've # gotten here, then the configuration has changed enough that the # test *needs* to be rerun. - if (NOT MULTIPASS_TEST_COUNT) - set (MULTIPASS_TEST_COUNT 00) - endif (NOT MULTIPASS_TEST_COUNT) - math (EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1") # Why can't I add to a cache variable? - set (MULTIPASS_TEST_COUNT ${_tmp} CACHE INTERNAL "Unique test ID") - set (testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) - set (CMAKE_REQUIRED_INCLUDES ${includes}) - set (CMAKE_REQUIRED_LIBRARIES ${libraries}) + if(NOT MULTIPASS_TEST_COUNT) + set(MULTIPASS_TEST_COUNT 00) + endif(NOT MULTIPASS_TEST_COUNT) + math(EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1" + )# Why can't I add to a cache variable? + set(MULTIPASS_TEST_COUNT + ${_tmp} + CACHE INTERNAL "Unique test ID" + ) + set(testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) + set(CMAKE_REQUIRED_INCLUDES ${includes}) + set(CMAKE_REQUIRED_LIBRARIES ${libraries}) if(${language} STREQUAL "C") - check_c_source_runs ("${source}" ${testname}) + check_c_source_runs("${source}" ${testname}) elseif(${language} STREQUAL "CXX") - check_cxx_source_runs ("${source}" ${testname}) + check_cxx_source_runs("${source}" ${testname}) endif() - set (${runs} "${${testname}}") -endmacro (MULTIPASS_SOURCE_RUNS) + set(${runs} "${${testname}}") +endmacro(MULTIPASS_SOURCE_RUNS) -macro (MULTIPASS_C_SOURCE_RUNS includes libraries source runs) +macro(MULTIPASS_C_SOURCE_RUNS includes libraries source runs) multipass_source_runs("${includes}" "${libraries}" "${source}" ${runs} "C") -endmacro (MULTIPASS_C_SOURCE_RUNS) +endmacro(MULTIPASS_C_SOURCE_RUNS) -macro (MULTIPASS_SOURCE_COMPILES includes libraries source runs language) - include (Check${language}SourceCompiles) +macro(MULTIPASS_SOURCE_COMPILES includes libraries source runs language) + include(Check${language}SourceCompiles) # This is a ridiculous hack. CHECK_${language}_SOURCE_* thinks that if the # *name* of the return variable doesn't change, then the test does # not need to be re-run. We keep an internal count which we # increment to guarantee that every test name is unique. If we've # gotten here, then the configuration has changed enough that the # test *needs* to be rerun. - if (NOT MULTIPASS_TEST_COUNT) - set (MULTIPASS_TEST_COUNT 00) - endif (NOT MULTIPASS_TEST_COUNT) - math (EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1") # Why can't I add to a cache variable? - set (MULTIPASS_TEST_COUNT ${_tmp} CACHE INTERNAL "Unique test ID") - set (testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) - set (CMAKE_REQUIRED_INCLUDES ${includes}) - set (CMAKE_REQUIRED_LIBRARIES ${libraries}) + if(NOT MULTIPASS_TEST_COUNT) + set(MULTIPASS_TEST_COUNT 00) + endif(NOT MULTIPASS_TEST_COUNT) + math(EXPR _tmp "${MULTIPASS_TEST_COUNT} + 1" + )# Why can't I add to a cache variable? + set(MULTIPASS_TEST_COUNT + ${_tmp} + CACHE INTERNAL "Unique test ID" + ) + set(testname MULTIPASS_TEST_${MULTIPASS_TEST_COUNT}_${runs}) + set(CMAKE_REQUIRED_INCLUDES ${includes}) + set(CMAKE_REQUIRED_LIBRARIES ${libraries}) if(${language} STREQUAL "C") - check_c_source_compiles ("${source}" ${testname}) + check_c_source_compiles("${source}" ${testname}) elseif(${language} STREQUAL "CXX") - check_cxx_source_compiles ("${source}" ${testname}) + check_cxx_source_compiles("${source}" ${testname}) endif() - set (${runs} "${${testname}}") -endmacro () + set(${runs} "${${testname}}") +endmacro() diff --git a/cmake/FindSLEPc.cmake b/cmake/FindSLEPc.cmake index 3add8eba8b..4df855569b 100644 --- a/cmake/FindSLEPc.cmake +++ b/cmake/FindSLEPc.cmake @@ -48,74 +48,92 @@ find_package(PETSc REQUIRED) find_package(MPI REQUIRED) # Set debian_arches (PETSC_ARCH for Debian-style installations) -foreach (debian_arches linux kfreebsd) - if ("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") - set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-debug ${debian_arches}-gnu-c-opt ${DEBIAN_FLAVORS}) +foreach(debian_arches linux kfreebsd) + if("${CMAKE_BUILD_TYPE}" STREQUAL "Debug") + set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-debug ${debian_arches}-gnu-c-opt + ${DEBIAN_FLAVORS} + ) else() - set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-opt ${debian_arches}-gnu-c-debug ${DEBIAN_FLAVORS}) + set(DEBIAN_FLAVORS ${debian_arches}-gnu-c-opt ${debian_arches}-gnu-c-debug + ${DEBIAN_FLAVORS} + ) endif() endforeach() # List of possible locations for SLEPC_DIR set(slepc_dir_locations "") list(APPEND slepc_dir_locations "/usr/lib/slepc") -list(APPEND slepc_dir_locations "/opt/local/lib/petsc") # Macports +list(APPEND slepc_dir_locations "/opt/local/lib/petsc") # Macports list(APPEND slepc_dir_locations "/usr/local/lib/slepc") list(APPEND slepc_dir_locations "$ENV{HOME}/slepc") # Try to figure out SLEPC_DIR by finding slepc.h -find_path(SLEPC_DIR include/slepc.h +find_path( + SLEPC_DIR include/slepc.h HINTS ${SLEPC_DIR} $ENV{SLEPC_DIR} PATHS ${slepc_dir_locations} - DOC "SLEPc directory") + DOC "SLEPc directory" +) # Report result of search for SLEPC_DIR -if (DEFINED SLEPC_DIR) +if(DEFINED SLEPC_DIR) message(STATUS "SLEPC_DIR is ${SLEPC_DIR}") else() message(STATUS "SLEPC_DIR is empty") endif() # Get variables from SLEPc configuration -if (SLEPC_DIR) +if(SLEPC_DIR) - find_library(SLEPC_LIBRARY + find_library( + SLEPC_LIBRARY NAMES slepc - HINTS - ${SLEPC_DIR}/lib - $ENV{SLEPC_DIR}/lib - ${SLEPC_DIR}/${PETSC_ARCH}/lib - $ENV{SLEPC_DIR}/$ENV{PETSC_ARCH}/lib + HINTS ${SLEPC_DIR}/lib $ENV{SLEPC_DIR}/lib ${SLEPC_DIR}/${PETSC_ARCH}/lib + $ENV{SLEPC_DIR}/$ENV{PETSC_ARCH}/lib NO_DEFAULT_PATH - DOC "The SLEPc library") - find_library(SLEPC_LIBRARY + DOC "The SLEPc library" + ) + find_library( + SLEPC_LIBRARY NAMES slepc - DOC "The SLEPc library") + DOC "The SLEPc library" + ) mark_as_advanced(SLEPC_LIBRARY) # Find SLEPc config file - find_file(SLEPC_CONFIG_FILE NAMES slepc_common PATHS - ${SLEPC_DIR}/lib/slepc/conf - ${SLEPC_DIR}/lib/slepc-conf ${SLEPC_DIR}/conf) + find_file( + SLEPC_CONFIG_FILE + NAMES slepc_common + PATHS ${SLEPC_DIR}/lib/slepc/conf ${SLEPC_DIR}/lib/slepc-conf + ${SLEPC_DIR}/conf + ) # Create a temporary Makefile to probe the SLEPc configuration set(slepc_config_makefile ${PROJECT_BINARY_DIR}/Makefile.slepc) - file(WRITE ${slepc_config_makefile} -"# This file was autogenerated by FindSLEPc.cmake + file( + WRITE ${slepc_config_makefile} + "# This file was autogenerated by FindSLEPc.cmake SLEPC_DIR = ${SLEPC_DIR} PETSC_ARCH = ${PETSC_ARCH} PETSC_DIR = ${PETSC_DIR} include ${SLEPC_CONFIG_FILE} show : -@echo -n \${\${VARIABLE}} -") +" + ) # Define macro for getting SLEPc variables from Makefile macro(SLEPC_GET_VARIABLE var name) - set(${var} "NOTFOUND" CACHE INTERNAL "Cleared" FORCE) - execute_process(COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f ${slepc_config_makefile} show VARIABLE=${name} + set(${var} + "NOTFOUND" + CACHE INTERNAL "Cleared" FORCE + ) + execute_process( + COMMAND ${MAKE_EXECUTABLE} --no-print-directory -f + ${slepc_config_makefile} show VARIABLE=${name} OUTPUT_VARIABLE ${var} - RESULT_VARIABLE slepc_return) + RESULT_VARIABLE slepc_return + ) endmacro() # Call macro to get the SLEPc variables @@ -131,15 +149,21 @@ show : resolve_libraries(SLEPC_EXTERNAL_LIBRARIES "${SLEPC_EXTERNAL_LIB}") # Add variables to CMake cache and mark as advanced - set(SLEPC_INCLUDE_DIRS ${SLEPC_INCLUDE_DIRS} CACHE STRING "SLEPc include paths." FORCE) - set(SLEPC_LIBRARIES ${SLEPC_LIBRARY} CACHE STRING "SLEPc libraries." FORCE) + set(SLEPC_INCLUDE_DIRS + ${SLEPC_INCLUDE_DIRS} + CACHE STRING "SLEPc include paths." FORCE + ) + set(SLEPC_LIBRARIES + ${SLEPC_LIBRARY} + CACHE STRING "SLEPc libraries." FORCE + ) mark_as_advanced(SLEPC_INCLUDE_DIRS SLEPC_LIBRARIES) endif() -if (SLEPC_SKIP_BUILD_TESTS) +if(SLEPC_SKIP_BUILD_TESTS) set(SLEPC_VERSION "UNKNOWN") set(SLEPC_VERSION_OK TRUE) -elseif (SLEPC_LIBRARIES AND SLEPC_INCLUDE_DIRS) +elseif(SLEPC_LIBRARIES AND SLEPC_INCLUDE_DIRS) # Set flags for building test program set(CMAKE_REQUIRED_INCLUDES ${SLEPC_INCLUDE_DIRS}) @@ -147,8 +171,11 @@ elseif (SLEPC_LIBRARIES AND SLEPC_INCLUDE_DIRS) # Check SLEPc version set(SLEPC_CONFIG_TEST_VERSION_CPP - "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/slepc_config_test_version.cpp") - file(WRITE ${SLEPC_CONFIG_TEST_VERSION_CPP} " + "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/slepc_config_test_version.cpp" + ) + file( + WRITE ${SLEPC_CONFIG_TEST_VERSION_CPP} + " #include #include \"slepcversion.h\" @@ -158,56 +185,69 @@ int main() { << SLEPC_VERSION_SUBMINOR; return 0; } -") +" + ) try_run( - SLEPC_CONFIG_TEST_VERSION_EXITCODE - SLEPC_CONFIG_TEST_VERSION_COMPILED - ${CMAKE_CURRENT_BINARY_DIR} - ${SLEPC_CONFIG_TEST_VERSION_CPP} - CMAKE_FLAGS - "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}" + SLEPC_CONFIG_TEST_VERSION_EXITCODE SLEPC_CONFIG_TEST_VERSION_COMPILED + ${CMAKE_CURRENT_BINARY_DIR} ${SLEPC_CONFIG_TEST_VERSION_CPP} + CMAKE_FLAGS "-DINCLUDE_DIRECTORIES:STRING=${CMAKE_REQUIRED_INCLUDES}" COMPILE_OUTPUT_VARIABLE COMPILE_OUTPUT RUN_OUTPUT_VARIABLE OUTPUT - ) + ) - if (SLEPC_CONFIG_TEST_VERSION_EXITCODE EQUAL 0) - set(SLEPC_VERSION "${OUTPUT}" CACHE STRING "SLEPC version number") + if(SLEPC_CONFIG_TEST_VERSION_EXITCODE EQUAL 0) + set(SLEPC_VERSION + "${OUTPUT}" + CACHE STRING "SLEPC version number" + ) string(REPLACE "." ";" SLEPC_VERSION_LIST ${SLEPC_VERSION}) list(GET SLEPC_VERSION_LIST 0 SLEPC_VERSION_MAJOR) list(GET SLEPC_VERSION_LIST 1 SLEPC_VERSION_MINOR) list(GET SLEPC_VERSION_LIST 2 SLEPC_VERSION_SUBMINOR) mark_as_advanced(SLEPC_VERSION) - mark_as_advanced(SLEPC_VERSION_MAJOR SLEPC_VERSION_MINOR SLEPC_VERSION_SUBMINOR) + mark_as_advanced( + SLEPC_VERSION_MAJOR SLEPC_VERSION_MINOR SLEPC_VERSION_SUBMINOR + ) endif() - if (SLEPc_FIND_VERSION) + if(SLEPc_FIND_VERSION) # Check if version found is >= required version - if (NOT "${SLEPC_VERSION}" VERSION_LESS "${SLEPc_FIND_VERSION}") - set(SLEPC_VERSION_OK TRUE CACHE BOOL "") + if(NOT "${SLEPC_VERSION}" VERSION_LESS "${SLEPc_FIND_VERSION}") + set(SLEPC_VERSION_OK + TRUE + CACHE BOOL "" + ) endif() else() # No specific version requested - set(SLEPC_VERSION_OK TRUE CACHE BOOL "") + set(SLEPC_VERSION_OK + TRUE + CACHE BOOL "" + ) endif() mark_as_advanced(SLEPC_VERSION_OK) endif() # Standard package handling include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(SLEPc +find_package_handle_standard_args( + SLEPc FOUND_VAR SLEPC_FOUND - FAIL_MESSAGE "SLEPc could not be found. Be sure to set SLEPC_DIR, PETSC_DIR, and PETSC_ARCH." + FAIL_MESSAGE + "SLEPc could not be found. Be sure to set SLEPC_DIR, PETSC_DIR, and PETSC_ARCH." VERSION_VAR SLEPC_VERSION - REQUIRED_VARS SLEPC_LIBRARIES SLEPC_DIR SLEPC_INCLUDE_DIRS SLEPC_VERSION_OK) + REQUIRED_VARS SLEPC_LIBRARIES SLEPC_DIR SLEPC_INCLUDE_DIRS SLEPC_VERSION_OK +) -if (SLEPC_FOUND) - if (NOT TARGET SLEPc::SLEPc) +if(SLEPC_FOUND) + if(NOT TARGET SLEPc::SLEPc) add_library(SLEPc::SLEPc UNKNOWN IMPORTED) - set_target_properties(SLEPc::SLEPc PROPERTIES - IMPORTED_LOCATION "${SLEPC_LIBRARIES}" - INTERFACE_INCLUDE_DIRECTORIES "${SLEPC_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES PETSc::PETSc - ) + set_target_properties( + SLEPc::SLEPc + PROPERTIES IMPORTED_LOCATION "${SLEPC_LIBRARIES}" + INTERFACE_INCLUDE_DIRECTORIES "${SLEPC_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES PETSc::PETSc + ) endif() endif() diff --git a/cmake/FindSUNDIALS.cmake b/cmake/FindSUNDIALS.cmake index 15b266d06a..e5cec34510 100644 --- a/cmake/FindSUNDIALS.cmake +++ b/cmake/FindSUNDIALS.cmake @@ -33,47 +33,48 @@ include(FindPackageHandleStandardArgs) find_package(SUNDIALS CONFIG QUIET) -if (SUNDIALS_FOUND) - if (TARGET SUNDIALS::nvecparallel) +if(SUNDIALS_FOUND) + if(TARGET SUNDIALS::nvecparallel) return() else() message(STATUS "SUNDIALS found but not SUNDIALS::nvecparallel") endif() endif() -find_path(SUNDIALS_INCLUDE_DIR - sundials_config.h - HINTS - "${SUNDIALS_ROOT}" - ENV SUNDIALS_DIR +find_path( + SUNDIALS_INCLUDE_DIR sundials_config.h + HINTS "${SUNDIALS_ROOT}" ENV SUNDIALS_DIR PATH_SUFFIXES include include/sundials - DOC "SUNDIALS Directory") + DOC "SUNDIALS Directory" +) -if (SUNDIALS_DEBUG) +if(SUNDIALS_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_INCLUDE_DIR = ${SUNDIALS_INCLUDE_DIR}" - " SUNDIALS_ROOT = ${SUNDIALS_ROOT}") + " SUNDIALS_INCLUDE_DIR = ${SUNDIALS_INCLUDE_DIR}" + " SUNDIALS_ROOT = ${SUNDIALS_ROOT}" + ) endif() set(SUNDIALS_INCLUDE_DIRS - "${SUNDIALS_INCLUDE_DIR}" - "${SUNDIALS_INCLUDE_DIR}/.." - CACHE STRING "SUNDIALS include directories") + "${SUNDIALS_INCLUDE_DIR}" "${SUNDIALS_INCLUDE_DIR}/.." + CACHE STRING "SUNDIALS include directories" +) -find_library(SUNDIALS_nvecparallel_LIBRARY +find_library( + SUNDIALS_nvecparallel_LIBRARY NAMES sundials_nvecparallel - HINTS - "${SUNDIALS_INCLUDE_DIR}/.." - "${SUNDIALS_INCLUDE_DIR}/../.." + HINTS "${SUNDIALS_INCLUDE_DIR}/.." "${SUNDIALS_INCLUDE_DIR}/../.." PATH_SUFFIXES lib lib64 - ) +) -if (SUNDIALS_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_nvecparallel_LIBRARY = ${SUNDIALS_nvecparallel_LIBRARY}") +if(SUNDIALS_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " SUNDIALS_nvecparallel_LIBRARY = ${SUNDIALS_nvecparallel_LIBRARY}" + ) endif() -if (NOT SUNDIALS_nvecparallel_LIBRARY) +if(NOT SUNDIALS_nvecparallel_LIBRARY) message(FATAL_ERROR "Sundials requested but SUNDIALS nvecparallel not found.") endif() list(APPEND SUNDIALS_LIBRARIES "${SUNDIALS_nvecparallel_LIBRARY}") @@ -81,62 +82,86 @@ mark_as_advanced(SUNDIALS_nvecparallel_LIBRARY) set(SUNDIALS_COMPONENTS arkode cvode ida) -foreach (LIB ${SUNDIALS_COMPONENTS}) - find_library(SUNDIALS_${LIB}_LIBRARY +foreach(LIB ${SUNDIALS_COMPONENTS}) + find_library( + SUNDIALS_${LIB}_LIBRARY NAMES sundials_${LIB} - HINTS - "${SUNDIALS_INCLUDE_DIR}/.." - "${SUNDIALS_INCLUDE_DIR}/../.." + HINTS "${SUNDIALS_INCLUDE_DIR}/.." "${SUNDIALS_INCLUDE_DIR}/../.." PATH_SUFFIXES lib lib64 - ) + ) - if (SUNDIALS_DEBUG) + if(SUNDIALS_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_${LIB}_LIBRARY = ${SUNDIALS_${LIB}_LIBRARY}") + " SUNDIALS_${LIB}_LIBRARY = ${SUNDIALS_${LIB}_LIBRARY}" + ) endif() - if (NOT SUNDIALS_${LIB}_LIBRARY) + if(NOT SUNDIALS_${LIB}_LIBRARY) message(FATAL_ERROR "Sundials requested but SUNDIALS ${LIB} not found.") endif() list(APPEND SUNDIALS_LIBRARIES "${SUNDIALS_${LIB}_LIBRARY}") mark_as_advanced(SUNDIALS_${LIB}_LIBRARY) endforeach() -if (SUNDIALS_INCLUDE_DIR) +if(SUNDIALS_INCLUDE_DIR) file(READ "${SUNDIALS_INCLUDE_DIR}/sundials_config.h" SUNDIALS_CONFIG_FILE) set(SUNDIALS_VERSION_REGEX_PATTERN - ".*#define SUNDIALS_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*") - string(REGEX MATCH ${SUNDIALS_VERSION_REGEX_PATTERN} _ "${SUNDIALS_CONFIG_FILE}") - set(SUNDIALS_VERSION_MAJOR ${CMAKE_MATCH_1} CACHE STRING "") - set(SUNDIALS_VERSION_MINOR ${CMAKE_MATCH_2} CACHE STRING "") - set(SUNDIALS_VERSION_PATCH ${CMAKE_MATCH_3} CACHE STRING "") - set(SUNDIALS_VERSION "${SUNDIALS_VERSION_MAJOR}.${SUNDIALS_VERSION_MINOR}.${SUNDIALS_VERSION_PATCH}" CACHE STRING "SUNDIALS version") + ".*#define SUNDIALS_VERSION \"([0-9]+)\\.([0-9]+)\\.([0-9]+)\".*" + ) + string(REGEX MATCH ${SUNDIALS_VERSION_REGEX_PATTERN} _ + "${SUNDIALS_CONFIG_FILE}" + ) + set(SUNDIALS_VERSION_MAJOR + ${CMAKE_MATCH_1} + CACHE STRING "" + ) + set(SUNDIALS_VERSION_MINOR + ${CMAKE_MATCH_2} + CACHE STRING "" + ) + set(SUNDIALS_VERSION_PATCH + ${CMAKE_MATCH_3} + CACHE STRING "" + ) + set(SUNDIALS_VERSION + "${SUNDIALS_VERSION_MAJOR}.${SUNDIALS_VERSION_MINOR}.${SUNDIALS_VERSION_PATCH}" + CACHE STRING "SUNDIALS version" + ) endif() -if (SUNDIALS_DEBUG) +if(SUNDIALS_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " SUNDIALS_VERSION = ${SUNDIALS_VERSION}") + " SUNDIALS_VERSION = ${SUNDIALS_VERSION}" + ) endif() -find_package_handle_standard_args(SUNDIALS +find_package_handle_standard_args( + SUNDIALS REQUIRED_VARS SUNDIALS_LIBRARIES SUNDIALS_INCLUDE_DIR SUNDIALS_INCLUDE_DIRS VERSION_VAR SUNDIALS_VERSION - ) +) -set(SUNDIALS_LIBRARIES "${SUNDIALS_LIBRARIES}" CACHE STRING "SUNDIALS libraries") +set(SUNDIALS_LIBRARIES + "${SUNDIALS_LIBRARIES}" + CACHE STRING "SUNDIALS libraries" +) mark_as_advanced(SUNDIALS_LIBRARIES SUNDIALS_INCLUDE_DIR SUNDIALS_INCLUDE_DIRS) -if (SUNDIALS_FOUND AND NOT TARGET SUNDIALS::SUNDIALS) +if(SUNDIALS_FOUND AND NOT TARGET SUNDIALS::SUNDIALS) add_library(SUNDIALS::nvecparallel UNKNOWN IMPORTED) - set_target_properties(SUNDIALS::nvecparallel PROPERTIES - IMPORTED_LOCATION "${SUNDIALS_nvecparallel_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}") + set_target_properties( + SUNDIALS::nvecparallel + PROPERTIES IMPORTED_LOCATION "${SUNDIALS_nvecparallel_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}" + ) - foreach (LIB ${SUNDIALS_COMPONENTS}) + foreach(LIB ${SUNDIALS_COMPONENTS}) add_library(SUNDIALS::${LIB} UNKNOWN IMPORTED) - set_target_properties(SUNDIALS::${LIB} PROPERTIES - IMPORTED_LOCATION "${SUNDIALS_${LIB}_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}" - INTERFACE_LINK_LIBRARIES SUNDIALS::nvecparallel) + set_target_properties( + SUNDIALS::${LIB} + PROPERTIES IMPORTED_LOCATION "${SUNDIALS_${LIB}_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${SUNDIALS_INCLUDE_DIRS}" + INTERFACE_LINK_LIBRARIES SUNDIALS::nvecparallel + ) endforeach() endif() diff --git a/cmake/FindScoreP.cmake b/cmake/FindScoreP.cmake index dd119545e8..80fe102749 100644 --- a/cmake/FindScoreP.cmake +++ b/cmake/FindScoreP.cmake @@ -61,76 +61,90 @@ # IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF # THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - find_program(ScoreP_CONFIG scorep-config) mark_as_advanced(ScoreP_CONFIG) get_filename_component(ScoreP_TMP "${ScoreP_CONFIG}" DIRECTORY) get_filename_component(ScoreP_EXEC_LOCATION "${ScoreP_TMP}" DIRECTORY) -if (ScoreP_DEBUG) +if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_CONFIG = ${ScoreP_CONFIG}" - " ScoreP_EXEC_LOCATION = ${ScoreP_EXEC_LOCATION}") + " ScoreP_CONFIG = ${ScoreP_CONFIG}" + " ScoreP_EXEC_LOCATION = ${ScoreP_EXEC_LOCATION}" + ) endif() if(ScoreP_CONFIG) message(STATUS "SCOREP library found. (using ${ScoreP_CONFIG})") - execute_process(COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--cppflags" - OUTPUT_VARIABLE ScoreP_CONFIG_FLAGS) + execute_process( + COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--cppflags" + OUTPUT_VARIABLE ScoreP_CONFIG_FLAGS + ) - string(REGEX MATCHALL "-I[^ ]*" ScoreP_CONFIG_INCLUDES "${ScoreP_CONFIG_FLAGS}") + string(REGEX MATCHALL "-I[^ ]*" ScoreP_CONFIG_INCLUDES + "${ScoreP_CONFIG_FLAGS}" + ) foreach(inc ${ScoreP_CONFIG_INCLUDES}) string(SUBSTRING ${inc} 2 -1 inc) list(APPEND ScoreP_INCLUDE_DIRS ${inc}) endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_INCLUDE_DIRS = ${ScoreP_INCLUDE_DIRS}") + " ScoreP_INCLUDE_DIRS = ${ScoreP_INCLUDE_DIRS}" + ) endif() - string(REGEX MATCHALL "(^| +)-[^I][^ ]*" ScoreP_CONFIG_CXXFLAGS "${ScoreP_CONFIG_FLAGS}") + string(REGEX MATCHALL "(^| +)-[^I][^ ]*" ScoreP_CONFIG_CXXFLAGS + "${ScoreP_CONFIG_FLAGS}" + ) foreach(flag ${ScoreP_CONFIG_CXXFLAGS}) string(STRIP ${flag} flag) list(APPEND ScoreP_CXX_FLAGS ${flag}) endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_CXX_FLAGS = ${ScoreP_CXX_FLAGS}") + " ScoreP_CXX_FLAGS = ${ScoreP_CXX_FLAGS}" + ) endif() unset(ScoreP_CONFIG_FLAGS) unset(ScoreP_CONFIG_INCLUDES) unset(ScoreP_CONFIG_CXXFLAGS) - execute_process(COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--ldflags" - OUTPUT_VARIABLE _LINK_LD_ARGS) - string( REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS} ) - foreach( _ARG ${_LINK_LD_ARGS} ) + execute_process( + COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--ldflags" + OUTPUT_VARIABLE _LINK_LD_ARGS + ) + string(REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS}) + foreach(_ARG ${_LINK_LD_ARGS}) if(${_ARG} MATCHES "^-L") - STRING(REGEX REPLACE "^-L" "" _ARG ${_ARG}) - SET(ScoreP_LINK_DIRS ${ScoreP_LINK_DIRS} ${_ARG}) + string(REGEX REPLACE "^-L" "" _ARG ${_ARG}) + set(ScoreP_LINK_DIRS ${ScoreP_LINK_DIRS} ${_ARG}) endif() endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_LINK_DIRS = ${ScoreP_LINK_DIRS}") + " ScoreP_LINK_DIRS = ${ScoreP_LINK_DIRS}" + ) endif() - execute_process(COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--libs" - OUTPUT_VARIABLE _LINK_LD_ARGS) - string( REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS} ) - foreach( _ARG ${_LINK_LD_ARGS} ) + execute_process( + COMMAND ${ScoreP_CONFIG} "--user" "--nocompiler" "--libs" + OUTPUT_VARIABLE _LINK_LD_ARGS + ) + string(REPLACE " " ";" _LINK_LD_ARGS ${_LINK_LD_ARGS}) + foreach(_ARG ${_LINK_LD_ARGS}) if(${_ARG} MATCHES "^-l") string(REGEX REPLACE "^-l" "" _ARG ${_ARG}) - find_library(_SCOREP_LIB_FROM_ARG NAMES ${_ARG} - PATHS - ${ScoreP_LINK_DIRS} - ) + find_library( + _SCOREP_LIB_FROM_ARG + NAMES ${_ARG} + PATHS ${ScoreP_LINK_DIRS} + ) if(_SCOREP_LIB_FROM_ARG) set(ScoreP_LIBRARIES ${ScoreP_LIBRARIES} ${_SCOREP_LIB_FROM_ARG}) endif() @@ -138,26 +152,26 @@ if(ScoreP_CONFIG) endif() endforeach() - if (ScoreP_DEBUG) + if(ScoreP_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " ScoreP_LIBRARIES = ${ScoreP_LIBRARIES}") + " ScoreP_LIBRARIES = ${ScoreP_LIBRARIES}" + ) endif() endif() -include (FindPackageHandleStandardArgs) -find_package_handle_standard_args(ScoreP DEFAULT_MSG - ScoreP_CONFIG - ScoreP_LIBRARIES - ScoreP_INCLUDE_DIRS - ) +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args( + ScoreP DEFAULT_MSG ScoreP_CONFIG ScoreP_LIBRARIES ScoreP_INCLUDE_DIRS +) -if (ScoreP_FOUND AND NOT TARGET ScoreP::ScoreP) +if(ScoreP_FOUND AND NOT TARGET ScoreP::ScoreP) add_library(ScoreP::ScoreP UNKNOWN IMPORTED) - set_target_properties(ScoreP::ScoreP PROPERTIES - INTERFACE_INCLUDE_DIRECTORIES "${ScoreP_INCLUDE_DIRS}" - IMPORTED_LINK_INTERFACE_LIBRARIES "${ScoreP_LIBRARIES}" - INTERFACE_INCLUDE_DEFINITIONS "${ScoreP_CXX_FLAGS}" - IMPORTED_LINK_INTERFACE_LANGUAGES "C" - ) + set_target_properties( + ScoreP::ScoreP + PROPERTIES INTERFACE_INCLUDE_DIRECTORIES "${ScoreP_INCLUDE_DIRS}" + IMPORTED_LINK_INTERFACE_LIBRARIES "${ScoreP_LIBRARIES}" + INTERFACE_INCLUDE_DEFINITIONS "${ScoreP_CXX_FLAGS}" + IMPORTED_LINK_INTERFACE_LANGUAGES "C" + ) endif() diff --git a/cmake/FindSphinx.cmake b/cmake/FindSphinx.cmake index fd377d0d00..a15ae8d6f9 100644 --- a/cmake/FindSphinx.cmake +++ b/cmake/FindSphinx.cmake @@ -14,13 +14,15 @@ # https://devblogs.microsoft.com/cppblog/clear-functional-c-documentation-with-sphinx-breathe-doxygen-cmake/ #Look for an executable called sphinx-build -find_program(SPHINX_EXECUTABLE - NAMES sphinx-build sphinx-build-3 - DOC "Path to sphinx-build executable") +find_program( + SPHINX_EXECUTABLE + NAMES sphinx-build sphinx-build-3 + DOC "Path to sphinx-build executable" +) include(FindPackageHandleStandardArgs) #Handle standard arguments to find_package like REQUIRED and QUIET -find_package_handle_standard_args(Sphinx - "Failed to find sphinx-build executable" - SPHINX_EXECUTABLE) +find_package_handle_standard_args( + Sphinx "Failed to find sphinx-build executable" SPHINX_EXECUTABLE +) diff --git a/cmake/FindnetCDF.cmake b/cmake/FindnetCDF.cmake index 361095954e..ca6eff8d75 100644 --- a/cmake/FindnetCDF.cmake +++ b/cmake/FindnetCDF.cmake @@ -28,119 +28,129 @@ include(BOUT++functions) include(CMakePrintHelpers) -if (NOT netCDF_ROOT AND EXISTS "${BOUT_USE_NETCDF}") +if(NOT netCDF_ROOT AND EXISTS "${BOUT_USE_NETCDF}") set(netCDF_ROOT "${BOUT_USE_NETCDF}") endif() enable_language(C) find_package(netCDF QUIET CONFIG) -if (netCDF_FOUND) +if(netCDF_FOUND) message(STATUS "netCDF CONFIG found") set(netCDF_FOUND TRUE) - if (NOT TARGET netCDF::netcdf) + if(NOT TARGET netCDF::netcdf) bout_add_library_alias(netCDF::netcdf netcdf) endif() - if (netCDF_DEBUG) - cmake_print_properties(TARGETS netcdf PROPERTIES LOCATION VERSION) - endif (netCDF_DEBUG) + if(netCDF_DEBUG) + cmake_print_properties(TARGETS netcdf PROPERTIES LOCATION VERSION) + endif(netCDF_DEBUG) return() endif() -find_program(NC_CONFIG "nc-config" +find_program( + NC_CONFIG "nc-config" PATHS "${netCDF_ROOT}" PATH_SUFFIXES bin DOC "Path to netCDF C config helper" NO_DEFAULT_PATH - ) +) -find_program(NC_CONFIG "nc-config" - DOC "Path to netCDF C config helper" - ) +find_program(NC_CONFIG "nc-config" DOC "Path to netCDF C config helper") get_filename_component(NC_CONFIG_TMP "${NC_CONFIG}" DIRECTORY) get_filename_component(NC_CONFIG_LOCATION "${NC_CONFIG_TMP}" DIRECTORY) -if (netCDF_DEBUG) +if(netCDF_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " NC_CONFIG_LOCATION = ${NC_CONFIG_LOCATION}" - " netCDF_ROOT = ${netCDF_ROOT}") + " NC_CONFIG_LOCATION = ${NC_CONFIG_LOCATION}" + " netCDF_ROOT = ${netCDF_ROOT}" + ) endif() bout_inspect_netcdf_config(NC_HINTS_INCLUDE_DIR "${NC_CONFIG}" "--includedir") bout_inspect_netcdf_config(NC_HINTS_PREFIX "${NC_CONFIG}" "--prefix") -find_path(netCDF_C_INCLUDE_DIR +find_path( + netCDF_C_INCLUDE_DIR NAMES netcdf.h DOC "netCDF C include directories" - HINTS - "${NC_HINTS_INCLUDE_DIR}" - "${NC_HINTS_PREFIX}" - "${NC_CONFIG_LOCATION}" - PATH_SUFFIXES - "include" + HINTS "${NC_HINTS_INCLUDE_DIR}" "${NC_HINTS_PREFIX}" "${NC_CONFIG_LOCATION}" + PATH_SUFFIXES "include" +) +if(netCDF_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_C_INCLUDE_DIR = ${netCDF_C_INCLUDE_DIR}" + " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" + " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" ) -if (netCDF_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_C_INCLUDE_DIR = ${netCDF_C_INCLUDE_DIR}" - " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" - " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" - ) endif() mark_as_advanced(netCDF_C_INCLUDE_DIR) -find_library(netCDF_C_LIBRARY +find_library( + netCDF_C_LIBRARY NAMES netcdf DOC "netCDF C library" - HINTS - "${NC_HINTS_INCLUDE_DIR}" - "${NC_HINTS_PREFIX}" - "${NC_CONFIG_LOCATION}" - PATH_SUFFIXES - "lib" "lib64" - ) -if (netCDF_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_C_LIBRARY = ${netCDF_C_LIBRARY}" - " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" - " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" - ) + HINTS "${NC_HINTS_INCLUDE_DIR}" "${NC_HINTS_PREFIX}" "${NC_CONFIG_LOCATION}" + PATH_SUFFIXES "lib" "lib64" +) +if(netCDF_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_C_LIBRARY = ${netCDF_C_LIBRARY}" + " NC_HINTS_INCLUDE_DIR = ${NC_HINTS_INCLUDE_DIR}" + " NC_HINTS_PREFIX = ${NC_HINTS_PREFIX}" + ) endif() mark_as_advanced(netCDF_C_LIBRARY) -if (netCDF_C_INCLUDE_DIR) +if(netCDF_C_INCLUDE_DIR) file(STRINGS "${netCDF_C_INCLUDE_DIR}/netcdf_meta.h" _netcdf_version_lines - REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)") - string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" _netcdf_version_major "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" _netcdf_version_minor "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" _netcdf_version_patch "${_netcdf_version_lines}") - string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" _netcdf_version_note "${_netcdf_version_lines}") - if (NOT _netcdf_version_note STREQUAL "") + REGEX "#define[ \t]+NC_VERSION_(MAJOR|MINOR|PATCH|NOTE)" + ) + string(REGEX REPLACE ".*NC_VERSION_MAJOR *\([0-9]*\).*" "\\1" + _netcdf_version_major "${_netcdf_version_lines}" + ) + string(REGEX REPLACE ".*NC_VERSION_MINOR *\([0-9]*\).*" "\\1" + _netcdf_version_minor "${_netcdf_version_lines}" + ) + string(REGEX REPLACE ".*NC_VERSION_PATCH *\([0-9]*\).*" "\\1" + _netcdf_version_patch "${_netcdf_version_lines}" + ) + string(REGEX REPLACE ".*NC_VERSION_NOTE *\"\([^\"]*\)\".*" "\\1" + _netcdf_version_note "${_netcdf_version_lines}" + ) + if(NOT _netcdf_version_note STREQUAL "") # Make development version compare higher than any patch level set(_netcdf_version_note ".99") endif() - set(netCDF_VERSION "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}") + set(netCDF_VERSION + "${_netcdf_version_major}.${_netcdf_version_minor}.${_netcdf_version_patch}${_netcdf_version_note}" + ) unset(_netcdf_version_major) unset(_netcdf_version_minor) unset(_netcdf_version_patch) unset(_netcdf_version_note) unset(_netcdf_version_lines) -endif () +endif() include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(netCDF +find_package_handle_standard_args( + netCDF REQUIRED_VARS netCDF_C_LIBRARY netCDF_C_INCLUDE_DIR - VERSION_VAR netCDF_VERSION) + VERSION_VAR netCDF_VERSION +) -if (netCDF_FOUND) +if(netCDF_FOUND) set(netCDF_INCLUDE_DIR "${netCDF_C_INCLUDE_DIR}") set(netCDF_INCLUDE_DIRS "${netCDF_C_INCLUDE_DIR}") set(netCDF_LIBRARIES "${netCDF_C_LIBRARY}") - if (NOT TARGET netCDF::netcdf) + if(NOT TARGET netCDF::netcdf) add_library(netCDF::netcdf UNKNOWN IMPORTED) - set_target_properties(netCDF::netcdf PROPERTIES - IMPORTED_LOCATION "${netCDF_C_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${netCDF_C_INCLUDE_DIR}" - ) - endif () -endif () + set_target_properties( + netCDF::netcdf + PROPERTIES IMPORTED_LOCATION "${netCDF_C_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${netCDF_C_INCLUDE_DIR}" + ) + endif() +endif() diff --git a/cmake/FindnetCDFCxx.cmake b/cmake/FindnetCDFCxx.cmake index d4155dc760..78bd93851e 100644 --- a/cmake/FindnetCDFCxx.cmake +++ b/cmake/FindnetCDFCxx.cmake @@ -27,16 +27,16 @@ include(BOUT++functions) -if (NOT netCDFCxx_ROOT AND EXISTS "${BOUT_USE_NETCDF}") +if(NOT netCDFCxx_ROOT AND EXISTS "${BOUT_USE_NETCDF}") set(netCDFCxx_ROOT "${BOUT_USE_NETCDF}") endif() -if (NOT EXISTS ${NCXX4_CONFIG}) +if(NOT EXISTS ${NCXX4_CONFIG}) # Only search if NCXX4_CONFIG was not set explicitly find_package(netCDFCxx QUIET CONFIG) - if (netCDFCxx_FOUND) + if(netCDFCxx_FOUND) set(netCDFCxx_FOUND TRUE) - if (NOT TARGET netCDF::netcdf-cxx4) + if(NOT TARGET netCDF::netcdf-cxx4) bout_add_library_alias(netCDF::netcdf-cxx4 netcdf-cxx4) endif() return() @@ -45,73 +45,74 @@ endif() find_package(netCDF REQUIRED) -find_program(NCXX4_CONFIG "ncxx4-config" +find_program( + NCXX4_CONFIG "ncxx4-config" PATHS "${netCDFCxx_ROOT}" PATH_SUFFIXES bin DOC "Path to netCDF C++ config helper" NO_DEFAULT_PATH - ) +) -find_program(NCXX4_CONFIG "ncxx4-config" - DOC "Path to netCDF C++ config helper" - ) +find_program(NCXX4_CONFIG "ncxx4-config" DOC "Path to netCDF C++ config helper") get_filename_component(NCXX4_CONFIG_TMP "${NCXX4_CONFIG}" DIRECTORY) get_filename_component(NCXX4_CONFIG_LOCATION "${NCXX4_CONFIG_TMP}" DIRECTORY) -if (netCDFCxx_DEBUG) +if(netCDFCxx_DEBUG) message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " NCXX4_CONFIG_LOCATION = ${NCXX4_CONFIG_LOCATION}") + " NCXX4_CONFIG_LOCATION = ${NCXX4_CONFIG_LOCATION}" + ) endif() -bout_inspect_netcdf_config(NCXX4_HINTS_INCLUDE_DIR "${NCXX4_CONFIG}" "--includedir") +bout_inspect_netcdf_config( + NCXX4_HINTS_INCLUDE_DIR "${NCXX4_CONFIG}" "--includedir" +) bout_inspect_netcdf_config(NCXX4_HINTS_PREFIX "${NCXX4_CONFIG}" "--prefix") -find_path(netCDF_CXX_INCLUDE_DIR +find_path( + netCDF_CXX_INCLUDE_DIR NAMES netcdf DOC "netCDF C++ include directories" - HINTS - "${netCDF_C_INCLUDE_DIR}" - "${NCXX4_HINTS_INCLUDE_DIR}" - "${NCXX4_HINTS_PREFIX}" - "${NCXX4_CONFIG_LOCATION}" - PATH_SUFFIXES - "include" + HINTS "${netCDF_C_INCLUDE_DIR}" "${NCXX4_HINTS_INCLUDE_DIR}" + "${NCXX4_HINTS_PREFIX}" "${NCXX4_CONFIG_LOCATION}" + PATH_SUFFIXES "include" +) +if(netCDFCxx_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_CXX_INCLUDE_DIR = ${netCDF_CXX_INCLUDE_DIR}" + " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" + " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" ) -if (netCDFCxx_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_CXX_INCLUDE_DIR = ${netCDF_CXX_INCLUDE_DIR}" - " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" - " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" - ) endif() mark_as_advanced(netCDF_CXX_INCLUDE_DIR) -find_library(netCDF_CXX_LIBRARY +find_library( + netCDF_CXX_LIBRARY NAMES netcdf_c++4 netcdf-cxx4 DOC "netCDF C++ library" - HINTS - "${NCXX4_HINTS_INCLUDE_DIR}" - "${NCXX4_HINTS_PREFIX}" - "${NCXX4_CONFIG_LOCATION}" - PATH_SUFFIXES - "lib" "lib64" + HINTS "${NCXX4_HINTS_INCLUDE_DIR}" "${NCXX4_HINTS_PREFIX}" + "${NCXX4_CONFIG_LOCATION}" + PATH_SUFFIXES "lib" "lib64" +) +if(netCDFCxx_DEBUG) + message( + STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " + " netCDF_CXX_LIBRARY = ${netCDF_CXX_LIBRARY}" + " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" + " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" ) -if (netCDFCxx_DEBUG) - message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] " - " netCDF_CXX_LIBRARY = ${netCDF_CXX_LIBRARY}" - " NCXX4_HINTS_INCLUDE_DIR = ${NCXX4_HINTS_INCLUDE_DIR}" - " NCXX4_HINTS_PREFIX = ${NCXX4_HINTS_PREFIX}" - ) endif() mark_as_advanced(netCDF_CXX_LIBRARY) bout_inspect_netcdf_config(_ncxx4_version "${NCXX4_CONFIG}" "--version") -if (_ncxx4_version) +if(_ncxx4_version) # Change to lower case before matching, to avoid case problems string(TOLOWER "${_ncxx4_version}" _ncxx4_version_lower) - string(REGEX REPLACE "netcdf-cxx4 \([0-9]+\\.[0-9]+\\.[0-9]+\).*" "\\1" netCDFCxx_VERSION "${_ncxx4_version_lower}") + string(REGEX REPLACE "netcdf-cxx4 \([0-9]+\\.[0-9]+\\.[0-9]+\).*" "\\1" + netCDFCxx_VERSION "${_ncxx4_version_lower}" + ) message(STATUS "Found netCDFCxx version ${netCDFCxx_VERSION}") -else () +else() message(WARNING "Couldn't get NetCDF version") endif() @@ -121,19 +122,23 @@ unset(_netcdf_version_minor) unset(_netcdf_version_patch) include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(netCDFCxx +find_package_handle_standard_args( + netCDFCxx REQUIRED_VARS netCDF_CXX_LIBRARY netCDF_CXX_INCLUDE_DIR - VERSION_VAR netCDFCxx_VERSION) + VERSION_VAR netCDFCxx_VERSION +) -if (netCDFCxx_FOUND) +if(netCDFCxx_FOUND) set(netCDFCxx_INCLUDE_DIRS "${netCDF_CXX_INCLUDE_DIR}") set(netCDFCxx_LIBRARIES "${netCDF_CXX_LIBRARY}") - if (NOT TARGET netCDF::netcdf-cxx4) + if(NOT TARGET netCDF::netcdf-cxx4) add_library(netCDF::netcdf-cxx4 UNKNOWN IMPORTED) - set_target_properties(netCDF::netcdf-cxx4 PROPERTIES - IMPORTED_LINK_INTERFACE_LIBRARIES netCDF::netcdf - IMPORTED_LOCATION "${netCDF_CXX_LIBRARY}" - INTERFACE_INCLUDE_DIRECTORIES "${netCDF_CXX_INCLUDE_DIR}") - endif () -endif () + set_target_properties( + netCDF::netcdf-cxx4 + PROPERTIES IMPORTED_LINK_INTERFACE_LIBRARIES netCDF::netcdf + IMPORTED_LOCATION "${netCDF_CXX_LIBRARY}" + INTERFACE_INCLUDE_DIRECTORIES "${netCDF_CXX_INCLUDE_DIR}" + ) + endif() +endif() diff --git a/cmake/GenerateDateTimeFile.cmake b/cmake/GenerateDateTimeFile.cmake index ef48fc4638..8ed742b31a 100644 --- a/cmake/GenerateDateTimeFile.cmake +++ b/cmake/GenerateDateTimeFile.cmake @@ -2,6 +2,9 @@ # compilation date and time as variables set(bout_date_time_file - "const char* boutcompiledate{__DATE__}; const char* boutcompiletime{__TIME__};") + "const char* boutcompiledate{__DATE__}; const char* boutcompiletime{__TIME__};" +) -file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx" "${bout_date_time_file}") +file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/bout++-time.cxx" + "${bout_date_time_file}" +) diff --git a/cmake/GetGitRevisionDescription.cmake b/cmake/GetGitRevisionDescription.cmake index 8ab03bc5f0..33f95dd589 100644 --- a/cmake/GetGitRevisionDescription.cmake +++ b/cmake/GetGitRevisionDescription.cmake @@ -37,7 +37,7 @@ # http://www.boost.org/LICENSE_1_0.txt) if(__get_git_revision_description) - return() + return() endif() set(__get_git_revision_description YES) @@ -46,123 +46,152 @@ set(__get_git_revision_description YES) get_filename_component(_gitdescmoddir ${CMAKE_CURRENT_LIST_FILE} PATH) function(get_git_head_revision _refspecvar _hashvar) - set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories - set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") - get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) - if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) - # We have reached the root directory, we are not in git - set(${_refspecvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - set(${_hashvar} "GITDIR-NOTFOUND" PARENT_SCOPE) - return() - endif() - set(GIT_DIR "${GIT_PARENT_DIR}/.git") - endwhile() - # check if this is a submodule - if(NOT IS_DIRECTORY ${GIT_DIR}) - file(READ ${GIT_DIR} submodule) - string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) - get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) - get_filename_component(GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE) - endif() - set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") - if(NOT EXISTS "${GIT_DATA}") - file(MAKE_DIRECTORY "${GIT_DATA}") - endif() + set(GIT_PARENT_DIR "${CMAKE_CURRENT_SOURCE_DIR}") + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + while(NOT EXISTS "${GIT_DIR}") # .git dir not found, search parent directories + set(GIT_PREVIOUS_PARENT "${GIT_PARENT_DIR}") + get_filename_component(GIT_PARENT_DIR ${GIT_PARENT_DIR} PATH) + if(GIT_PARENT_DIR STREQUAL GIT_PREVIOUS_PARENT) + # We have reached the root directory, we are not in git + set(${_refspecvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE + ) + set(${_hashvar} + "GITDIR-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + set(GIT_DIR "${GIT_PARENT_DIR}/.git") + endwhile() + # check if this is a submodule + if(NOT IS_DIRECTORY ${GIT_DIR}) + file(READ ${GIT_DIR} submodule) + string(REGEX REPLACE "gitdir: (.*)\n$" "\\1" GIT_DIR_RELATIVE ${submodule}) + get_filename_component(SUBMODULE_DIR ${GIT_DIR} PATH) + get_filename_component( + GIT_DIR ${SUBMODULE_DIR}/${GIT_DIR_RELATIVE} ABSOLUTE + ) + endif() + set(GIT_DATA "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/git-data") + if(NOT EXISTS "${GIT_DATA}") + file(MAKE_DIRECTORY "${GIT_DATA}") + endif() - if(NOT EXISTS "${GIT_DIR}/HEAD") - return() - endif() - set(HEAD_FILE "${GIT_DATA}/HEAD") - configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) + if(NOT EXISTS "${GIT_DIR}/HEAD") + return() + endif() + set(HEAD_FILE "${GIT_DATA}/HEAD") + configure_file("${GIT_DIR}/HEAD" "${HEAD_FILE}" COPYONLY) - configure_file("${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" - "${GIT_DATA}/grabRef.cmake" - @ONLY) - include("${GIT_DATA}/grabRef.cmake") + configure_file( + "${_gitdescmoddir}/GetGitRevisionDescription.cmake.in" + "${GIT_DATA}/grabRef.cmake" @ONLY + ) + include("${GIT_DATA}/grabRef.cmake") - set(${_refspecvar} "${HEAD_REF}" PARENT_SCOPE) - set(${_hashvar} "${HEAD_HASH}" PARENT_SCOPE) + set(${_refspecvar} + "${HEAD_REF}" + PARENT_SCOPE + ) + set(${_hashvar} + "${HEAD_HASH}" + PARENT_SCOPE + ) endfunction() function(git_describe _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() - # TODO sanitize - #if((${ARGN}" MATCHES "&&") OR - # (ARGN MATCHES "||") OR - # (ARGN MATCHES "\\;")) - # message("Please report the following error to the project!") - # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") - #endif() + # TODO sanitize + #if((${ARGN}" MATCHES "&&") OR + # (ARGN MATCHES "||") OR + # (ARGN MATCHES "\\;")) + # message("Please report the following error to the project!") + # message(FATAL_ERROR "Looks like someone's doing something nefarious with git_describe! Passed arguments ${ARGN}") + #endif() - #message(STATUS "Arguments to execute_process: ${ARGN}") + #message(STATUS "Arguments to execute_process: ${ARGN}") - execute_process(COMMAND - "${GIT_EXECUTABLE}" - describe - ${hash} - ${ARGN} - WORKING_DIRECTORY - "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(NOT res EQUAL 0) - set(out "${out}-${res}-NOTFOUND") - endif() + execute_process( + COMMAND "${GIT_EXECUTABLE}" describe ${hash} ${ARGN} + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(NOT res EQUAL 0) + set(out "${out}-${res}-NOTFOUND") + endif() - set(${_var} "${out}" PARENT_SCOPE) + set(${_var} + "${out}" + PARENT_SCOPE + ) endfunction() function(git_get_exact_tag _var) - git_describe(out --exact-match ${ARGN}) - set(${_var} "${out}" PARENT_SCOPE) + git_describe(out --exact-match ${ARGN}) + set(${_var} + "${out}" + PARENT_SCOPE + ) endfunction() function(git_local_changes _var) - if(NOT GIT_FOUND) - find_package(Git QUIET) - endif() - get_git_head_revision(refspec hash) - if(NOT GIT_FOUND) - set(${_var} "GIT-NOTFOUND" PARENT_SCOPE) - return() - endif() - if(NOT hash) - set(${_var} "HEAD-HASH-NOTFOUND" PARENT_SCOPE) - return() - endif() + if(NOT GIT_FOUND) + find_package(Git QUIET) + endif() + get_git_head_revision(refspec hash) + if(NOT GIT_FOUND) + set(${_var} + "GIT-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() + if(NOT hash) + set(${_var} + "HEAD-HASH-NOTFOUND" + PARENT_SCOPE + ) + return() + endif() - execute_process(COMMAND - "${GIT_EXECUTABLE}" - diff-index --quiet HEAD -- - WORKING_DIRECTORY - "${CMAKE_CURRENT_SOURCE_DIR}" - RESULT_VARIABLE - res - OUTPUT_VARIABLE - out - ERROR_QUIET - OUTPUT_STRIP_TRAILING_WHITESPACE) - if(res EQUAL 0) - set(${_var} "CLEAN" PARENT_SCOPE) - else() - set(${_var} "DIRTY" PARENT_SCOPE) - endif() + execute_process( + COMMAND "${GIT_EXECUTABLE}" diff-index --quiet HEAD -- + WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" + RESULT_VARIABLE res + OUTPUT_VARIABLE out + ERROR_QUIET OUTPUT_STRIP_TRAILING_WHITESPACE + ) + if(res EQUAL 0) + set(${_var} + "CLEAN" + PARENT_SCOPE + ) + else() + set(${_var} + "DIRTY" + PARENT_SCOPE + ) + endif() endfunction() diff --git a/cmake/ResolveCompilerPaths.cmake b/cmake/ResolveCompilerPaths.cmake index 54787fa38f..d4274496df 100644 --- a/cmake/ResolveCompilerPaths.cmake +++ b/cmake/ResolveCompilerPaths.cmake @@ -38,68 +38,77 @@ # # assuming both directories exist. # Note: as currently implemented, the -I/string will be picked up mistakenly (cry, cry) -include (CorrectWindowsPaths) +include(CorrectWindowsPaths) -macro (RESOLVE_LIBRARIES LIBS LINK_LINE) - string (REGEX MATCHALL "((-L|-l|-Wl)([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))" _all_tokens "${LINK_LINE}") - set (_libs_found "") - set (_directory_list "") - foreach (token ${_all_tokens}) - if (token MATCHES "-L([^\" ]+|\"[^\"]+\")") +macro(RESOLVE_LIBRARIES LIBS LINK_LINE) + string(REGEX MATCHALL + "((-L|-l|-Wl)([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))" + _all_tokens "${LINK_LINE}" + ) + set(_libs_found "") + set(_directory_list "") + foreach(token ${_all_tokens}) + if(token MATCHES "-L([^\" ]+|\"[^\"]+\")") # If it's a library path, add it to the list - string (REGEX REPLACE "^-L" "" token ${token}) - string (REGEX REPLACE "//" "/" token ${token}) + string(REGEX REPLACE "^-L" "" token ${token}) + string(REGEX REPLACE "//" "/" token ${token}) convert_cygwin_path(token) - list (APPEND _directory_list ${token}) - elseif (token MATCHES "^(-l([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))") + list(APPEND _directory_list ${token}) + elseif(token MATCHES "^(-l([^\" ]+|\"[^\"]+\")|[^\" ]+\\.(a|so|dll|lib))") # It's a library, resolve the path by looking in the list and then (by default) in system directories - if (WIN32) #windows expects "libfoo", linux expects "foo" - string (REGEX REPLACE "^-l" "lib" token ${token}) - else (WIN32) - string (REGEX REPLACE "^-l" "" token ${token}) - endif (WIN32) - set (_root "") - if (token MATCHES "^/") # We have an absolute path + if(WIN32) #windows expects "libfoo", linux expects "foo" + string(REGEX REPLACE "^-l" "lib" token ${token}) + else(WIN32) + string(REGEX REPLACE "^-l" "" token ${token}) + endif(WIN32) + set(_root "") + if(token MATCHES "^/") # We have an absolute path #separate into a path and a library name: - string (REGEX MATCH "[^/]*\\.(a|so|dll|lib)$" libname ${token}) - string (REGEX MATCH ".*[^${libname}$]" libpath ${token}) + string(REGEX MATCH "[^/]*\\.(a|so|dll|lib)$" libname ${token}) + string(REGEX MATCH ".*[^${libname}$]" libpath ${token}) convert_cygwin_path(libpath) - set (_directory_list ${_directory_list} ${libpath}) - set (token ${libname}) - endif (token MATCHES "^/") - set (_lib "NOTFOUND" CACHE FILEPATH "Cleared" FORCE) - find_library (_lib ${token} HINTS ${_directory_list} ${_root}) - if (_lib) - string (REPLACE "//" "/" _lib ${_lib}) - list (APPEND _libs_found ${_lib}) - else (_lib) - message (STATUS "Unable to find library ${token}") - endif (_lib) - endif (token MATCHES "-L([^\" ]+|\"[^\"]+\")") - endforeach (token) - set (_lib "NOTFOUND" CACHE INTERNAL "Scratch variable" FORCE) + set(_directory_list ${_directory_list} ${libpath}) + set(token ${libname}) + endif(token MATCHES "^/") + set(_lib + "NOTFOUND" + CACHE FILEPATH "Cleared" FORCE + ) + find_library(_lib ${token} HINTS ${_directory_list} ${_root}) + if(_lib) + string(REPLACE "//" "/" _lib ${_lib}) + list(APPEND _libs_found ${_lib}) + else(_lib) + message(STATUS "Unable to find library ${token}") + endif(_lib) + endif(token MATCHES "-L([^\" ]+|\"[^\"]+\")") + endforeach(token) + set(_lib + "NOTFOUND" + CACHE INTERNAL "Scratch variable" FORCE + ) # only the LAST occurence of each library is required since there should be no circular dependencies - if (_libs_found) - list (REVERSE _libs_found) - list (REMOVE_DUPLICATES _libs_found) - list (REVERSE _libs_found) - endif (_libs_found) - set (${LIBS} "${_libs_found}") -endmacro (RESOLVE_LIBRARIES) + if(_libs_found) + list(REVERSE _libs_found) + list(REMOVE_DUPLICATES _libs_found) + list(REVERSE _libs_found) + endif(_libs_found) + set(${LIBS} "${_libs_found}") +endmacro(RESOLVE_LIBRARIES) -macro (RESOLVE_INCLUDES INCS COMPILE_LINE) - string (REGEX MATCHALL "-I([^\" ]+|\"[^\"]+\")" _all_tokens "${COMPILE_LINE}") - set (_incs_found "") - foreach (token ${_all_tokens}) - string (REGEX REPLACE "^-I" "" token ${token}) - string (REGEX REPLACE "//" "/" token ${token}) +macro(RESOLVE_INCLUDES INCS COMPILE_LINE) + string(REGEX MATCHALL "-I([^\" ]+|\"[^\"]+\")" _all_tokens "${COMPILE_LINE}") + set(_incs_found "") + foreach(token ${_all_tokens}) + string(REGEX REPLACE "^-I" "" token ${token}) + string(REGEX REPLACE "//" "/" token ${token}) convert_cygwin_path(token) - if (EXISTS ${token}) - list (APPEND _incs_found ${token}) - else (EXISTS ${token}) - message (STATUS "Include directory ${token} does not exist") - endif (EXISTS ${token}) - endforeach (token) - list (REMOVE_DUPLICATES _incs_found) - set (${INCS} "${_incs_found}") -endmacro (RESOLVE_INCLUDES) + if(EXISTS ${token}) + list(APPEND _incs_found ${token}) + else(EXISTS ${token}) + message(STATUS "Include directory ${token} does not exist") + endif(EXISTS ${token}) + endforeach(token) + list(REMOVE_DUPLICATES _incs_found) + set(${INCS} "${_incs_found}") +endmacro(RESOLVE_INCLUDES) diff --git a/cmake/Sanitizers.cmake b/cmake/Sanitizers.cmake index 715a08ab88..aab58d6f70 100644 --- a/cmake/Sanitizers.cmake +++ b/cmake/Sanitizers.cmake @@ -4,7 +4,9 @@ function(enable_sanitizers target_name) - if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") + if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" OR CMAKE_CXX_COMPILER_ID MATCHES + ".*Clang" + ) option(ENABLE_COVERAGE "Enable coverage reporting for gcc/clang" FALSE) message(STATUS "Enable coverage: ${ENABLE_COVERAGE}") @@ -17,33 +19,40 @@ function(enable_sanitizers target_name) find_program(genhtml_FOUND genhtml) message(STATUS "Looking for genhtml: ${genhtml_FOUND}") - if (lcov_FOUND AND genhtml_FOUND) - set(COVERAGE_NAME coverage CACHE STRING "Name of coverage output file") + if(lcov_FOUND AND genhtml_FOUND) + set(COVERAGE_NAME + coverage + CACHE STRING "Name of coverage output file" + ) set(COVERAGE_FILE "${COVERAGE_NAME}.info") - set(COVERAGE_MSG "Open file://${PROJECT_SOURCE_DIR}/${COVERAGE_NAME}/index.html in your browser to view coverage HTML output") + set(COVERAGE_MSG + "Open file://${PROJECT_SOURCE_DIR}/${COVERAGE_NAME}/index.html in your browser to view coverage HTML output" + ) - add_custom_target(code-coverage-capture + add_custom_target( + code-coverage-capture COMMAND - lcov -c --directory "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/bout++.dir/src" - --output-file "${COVERAGE_FILE}" - COMMAND - genhtml --output-directory "${COVERAGE_NAME}" --demangle-cpp --legend --show-details "${COVERAGE_FILE}" - COMMAND - "${CMAKE_COMMAND}" -E echo ${COVERAGE_MSG} + lcov -c --directory + "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/bout++.dir/src" + --output-file "${COVERAGE_FILE}" + COMMAND genhtml --output-directory "${COVERAGE_NAME}" --demangle-cpp + --legend --show-details "${COVERAGE_FILE}" + COMMAND "${CMAKE_COMMAND}" -E echo ${COVERAGE_MSG} WORKING_DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}" COMMENT "Capturing coverage information" - BYPRODUCTS - "${COVERAGE_FILE}" - "${COVERAGE_NAME}/index.html" - ) + BYPRODUCTS "${COVERAGE_FILE}" "${COVERAGE_NAME}/index.html" + ) - add_custom_target(code-coverage-clean - COMMAND - lcov --zerocounters + add_custom_target( + code-coverage-clean + COMMAND lcov --zerocounters COMMENT "Cleaning coverage information" - ) + ) else() - message(FATAL_ERROR "Coverage enabled, but coverage-capture not available. Please install lcov") + message( + FATAL_ERROR + "Coverage enabled, but coverage-capture not available. Please install lcov" + ) endif() endif() @@ -60,7 +69,9 @@ function(enable_sanitizers target_name) list(APPEND SANITIZERS "leak") endif() - option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR "Enable undefined behavior sanitizer" FALSE) + option(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR + "Enable undefined behavior sanitizer" FALSE + ) if(ENABLE_SANITIZER_UNDEFINED_BEHAVIOR) list(APPEND SANITIZERS "undefined") endif() @@ -68,7 +79,10 @@ function(enable_sanitizers target_name) option(ENABLE_SANITIZER_THREAD "Enable thread sanitizer" FALSE) if(ENABLE_SANITIZER_THREAD) if("address" IN_LIST SANITIZERS OR "leak" IN_LIST SANITIZERS) - message(WARNING "Thread sanitizer does not work with Address and Leak sanitizer enabled") + message( + WARNING + "Thread sanitizer does not work with Address and Leak sanitizer enabled" + ) else() list(APPEND SANITIZERS "thread") endif() @@ -78,32 +92,40 @@ function(enable_sanitizers target_name) if(ENABLE_SANITIZER_MEMORY AND CMAKE_CXX_COMPILER_ID MATCHES ".*Clang") if("address" IN_LIST SANITIZERS OR "thread" IN_LIST SANITIZERS - OR "leak" IN_LIST SANITIZERS) - message(WARNING "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled") + OR "leak" IN_LIST SANITIZERS + ) + message( + WARNING + "Memory sanitizer does not work with Address, Thread and Leak sanitizer enabled" + ) else() list(APPEND SANITIZERS "memory") endif() endif() - list( - JOIN - SANITIZERS - "," - LIST_OF_SANITIZERS) + list(JOIN SANITIZERS "," LIST_OF_SANITIZERS) endif() # Default value gets overridden below - set(BOUT_USE_SANITIZERS "None" PARENT_SCOPE) + set(BOUT_USE_SANITIZERS + "None" + PARENT_SCOPE + ) if(LIST_OF_SANITIZERS) - if(NOT - "${LIST_OF_SANITIZERS}" - STREQUAL - "") - set(BOUT_USE_SANITIZERS ${LIST_OF_SANITIZERS} PARENT_SCOPE) - target_compile_options(${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS} -fno-omit-frame-pointer) - target_link_options(${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS}) + if(NOT "${LIST_OF_SANITIZERS}" STREQUAL "") + set(BOUT_USE_SANITIZERS + ${LIST_OF_SANITIZERS} + PARENT_SCOPE + ) + target_compile_options( + ${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS} + -fno-omit-frame-pointer + ) + target_link_options( + ${target_name} PUBLIC -fsanitize=${LIST_OF_SANITIZERS} + ) endif() endif() diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 42ba52b948..850809e856 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -1,12 +1,12 @@ set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${PROJECT_SOURCE_DIR}/cmake/") # determined in SetupCompilers.cmake -if (BOUT_USE_MPI) +if(BOUT_USE_MPI) target_link_libraries(bout++ PUBLIC MPI::MPI_CXX) -endif () +endif() # determined in SetupCompilers.cmake -if (BOUT_USE_OPENMP) +if(BOUT_USE_OPENMP) target_link_libraries(bout++ PUBLIC OpenMP::OpenMP_CXX) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -fopenmp") set(CONFIG_LDFLAGS_SHARED "${CONFIG_LDFLAGS_SHARED} -fopenmp") @@ -14,7 +14,7 @@ if (BOUT_USE_OPENMP) endif() # determined in SetupCompilers.cmake -if (BOUT_HAS_CUDA) +if(BOUT_HAS_CUDA) enable_language(CUDA) message(STATUS "BOUT_HAS_CUDA ${CMAKE_CUDA_COMPILER}") @@ -29,58 +29,65 @@ if (BOUT_HAS_CUDA) set_target_properties(bout++ PROPERTIES CUDA_SEPARABLE_COMPILATION ON) set_target_properties(bout++ PROPERTIES POSITION_INDEPENDENT_CODE ON) set_target_properties(bout++ PROPERTIES LINKER_LANGUAGE CUDA) -endif () +endif() # Caliper option(BOUT_ENABLE_CALIPER "Enable Caliper" OFF) -if (BOUT_ENABLE_CALIPER) +if(BOUT_ENABLE_CALIPER) find_package(caliper REQUIRED) target_include_directories(bout++ PUBLIC ${caliper_INCLUDE_DIR}) target_link_libraries(bout++ PUBLIC caliper) -endif () +endif() set(BOUT_HAS_CALIPER ${BOUT_ENABLE_CALIPER}) # UMPIRE option(BOUT_ENABLE_UMPIRE "Enable UMPIRE memory management" OFF) -if (BOUT_ENABLE_UMPIRE) +if(BOUT_ENABLE_UMPIRE) find_package(UMPIRE REQUIRED) target_include_directories(bout++ PUBLIC ${UMPIRE_INCLUDE_DIRS}/include) target_link_libraries(bout++ PUBLIC umpire) -endif () +endif() set(BOUT_HAS_UMPIRE ${BOUT_ENABLE_UMPIRE}) # RAJA option(BOUT_ENABLE_RAJA "Enable RAJA" OFF) -if (BOUT_ENABLE_RAJA) +if(BOUT_ENABLE_RAJA) find_package(RAJA REQUIRED) - message(STATUS "RAJA_CONFIG:" ${RAJA_CONFIG}) + message(STATUS "RAJA_CONFIG:" ${RAJA_CONFIG}) string(FIND ${RAJA_CONFIG} "raja" loc) math(EXPR value "${loc} + 5" OUTPUT_FORMAT DECIMAL) - string(SUBSTRING ${RAJA_CONFIG} 0 ${value} RAJA_PATH) + string(SUBSTRING ${RAJA_CONFIG} 0 ${value} RAJA_PATH) message(STATUS "RAJA_PATH" ${RAJA_PATH}) target_include_directories(bout++ PUBLIC ${RAJA_PATH}/include) target_link_libraries(bout++ PUBLIC RAJA) -endif () +endif() set(BOUT_HAS_RAJA ${BOUT_ENABLE_RAJA}) # Hypre option(BOUT_USE_HYPRE "Enable support for Hypre solvers" OFF) -if (BOUT_USE_HYPRE) +if(BOUT_USE_HYPRE) enable_language(C) find_package(HYPRE REQUIRED) target_link_libraries(bout++ PUBLIC HYPRE::HYPRE) - if (HYPRE_WITH_CUDA AND BOUT_HAS_CUDA) - target_compile_definitions(bout++ PUBLIC "HYPRE_USING_CUDA;HYPRE_USING_UNIFIED_MEMORY") - target_link_libraries(bout++ PUBLIC CUDA::cusparse CUDA::curand CUDA::culibos CUDA::cublas CUDA::cublasLt) - endif () -endif () + if(HYPRE_WITH_CUDA AND BOUT_HAS_CUDA) + target_compile_definitions( + bout++ PUBLIC "HYPRE_USING_CUDA;HYPRE_USING_UNIFIED_MEMORY" + ) + target_link_libraries( + bout++ PUBLIC CUDA::cusparse CUDA::curand CUDA::culibos CUDA::cublas + CUDA::cublasLt + ) + endif() +endif() message(STATUS "HYPRE support: ${BOUT_USE_HYPRE}") set(BOUT_HAS_HYPRE ${BOUT_USE_HYPRE}) # PETSc -option(BOUT_USE_PETSC "Enable support for PETSc time solvers and inversions" OFF) -if (BOUT_USE_PETSC) - if (NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") +option(BOUT_USE_PETSC "Enable support for PETSc time solvers and inversions" + OFF +) +if(BOUT_USE_PETSC) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") # Cray wrappers sort this out for us find_package(PETSc REQUIRED) target_link_libraries(bout++ PUBLIC PETSc::PETSc) @@ -94,28 +101,44 @@ endif() message(STATUS "PETSc support: ${BOUT_USE_PETSC}") set(BOUT_HAS_PETSC ${BOUT_USE_PETSC}) - -cmake_dependent_option(BOUT_USE_SYSTEM_MPARK_VARIANT "Use external installation of mpark.variant" OFF - "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/mpark.variant/CMakeLists.txt" ON) +cmake_dependent_option( + BOUT_USE_SYSTEM_MPARK_VARIANT + "Use external installation of mpark.variant" + OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/mpark.variant/CMakeLists.txt" + ON +) if(BOUT_USE_SYSTEM_MPARK_VARIANT) message(STATUS "Using external mpark.variant") find_package(mpark_variant REQUIRED) - get_target_property(MPARK_VARIANT_INCLUDE_PATH mpark_variant INTERFACE_INCLUDE_DIRECTORIES) + get_target_property( + MPARK_VARIANT_INCLUDE_PATH mpark_variant INTERFACE_INCLUDE_DIRECTORIES + ) else() message(STATUS "Using mpark.variant submodule") bout_update_submodules() add_subdirectory(externalpackages/mpark.variant) if(NOT TARGET mpark_variant) - message(FATAL_ERROR "mpark_variant not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?") + message( + FATAL_ERROR + "mpark_variant not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?" + ) endif() - set(MPARK_VARIANT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/mpark.variant/include") + set(MPARK_VARIANT_INCLUDE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/mpark.variant/include" + ) set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${MPARK_VARIANT_INCLUDE_PATH}") endif() target_link_libraries(bout++ PUBLIC mpark_variant) -cmake_dependent_option(BOUT_USE_SYSTEM_FMT "Use external installation of fmt" OFF - "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/fmt/CMakeLists.txt" ON) +cmake_dependent_option( + BOUT_USE_SYSTEM_FMT + "Use external installation of fmt" + OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/fmt/CMakeLists.txt" + ON +) if(BOUT_USE_SYSTEM_FMT) message(STATUS "Using external fmt") @@ -125,32 +148,46 @@ else() message(STATUS "Using fmt submodule") bout_update_submodules() # Need to install fmt alongside BOUT++ - set(FMT_INSTALL ON CACHE BOOL "") - set(FMT_DEBUG_POSTFIX "" CACHE STRING "") + set(FMT_INSTALL + ON + CACHE BOOL "" + ) + set(FMT_DEBUG_POSTFIX + "" + CACHE STRING "" + ) add_subdirectory(externalpackages/fmt) if(NOT TARGET fmt::fmt) - message(FATAL_ERROR "fmt not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?") + message( + FATAL_ERROR + "fmt not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?" + ) endif() # Build the library in /lib: this makes updating the path # for bout-config much easier - set_target_properties(fmt PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") - set(FMT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/fmt/include") + set_target_properties( + fmt PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ) + set(FMT_INCLUDE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/fmt/include" + ) set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${FMT_INCLUDE_PATH}") set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -lfmt") endif() target_link_libraries(bout++ PUBLIC fmt::fmt) option(BOUT_USE_PVODE "Enable support for bundled PVODE" ON) -if (BOUT_USE_PVODE) +if(BOUT_USE_PVODE) add_subdirectory(externalpackages/PVODE) target_link_libraries(bout++ PUBLIC pvode pvpre) # Build the libraries in /lib: this makes updating the # path for bout-config much easier - set_target_properties(pvode pvpre PROPERTIES - LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" - ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib") + set_target_properties( + pvode pvpre + PROPERTIES LIBRARY_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ARCHIVE_OUTPUT_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/lib" + ) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -lpvode -lpvpre") endif() message(STATUS "PVODE support: ${BOUT_USE_PVODE}") @@ -158,25 +195,30 @@ set(BOUT_HAS_PVODE ${BOUT_USE_PVODE}) option(BOUT_USE_NETCDF "Enable support for NetCDF output" ON) option(BOUT_DOWNLOAD_NETCDF_CXX4 "Download and build netCDF-cxx4" OFF) -if (BOUT_USE_NETCDF) - if (BOUT_DOWNLOAD_NETCDF_CXX4) +if(BOUT_USE_NETCDF) + if(BOUT_DOWNLOAD_NETCDF_CXX4) message(STATUS "Downloading and configuring NetCDF-cxx4") include(FetchContent) FetchContent_Declare( netcdf-cxx4 GIT_REPOSITORY https://github.com/Unidata/netcdf-cxx4 - GIT_TAG "a43d6d4d415d407712c246faca553bd951730dc1" - ) + GIT_TAG "a43d6d4d415d407712c246faca553bd951730dc1" + ) # Don't build the netcdf tests, they have lots of warnings - set(NCXX_ENABLE_TESTS OFF CACHE BOOL "" FORCE) + set(NCXX_ENABLE_TESTS + OFF + CACHE BOOL "" FORCE + ) # Use our own FindnetCDF module which uses nc-config find_package(netCDF REQUIRED) FetchContent_MakeAvailable(netcdf-cxx4) target_link_libraries(bout++ PUBLIC netCDF::netcdf-cxx4) else() find_package(netCDFCxx) - if (netCDFCxx_FOUND) - set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${netCDF_CXX_LIBRARY} ${netCDF_LIBRARIES}") + if(netCDFCxx_FOUND) + set(CONFIG_LDFLAGS + "${CONFIG_LDFLAGS} ${netCDF_CXX_LIBRARY} ${netCDF_LIBRARIES}" + ) target_link_libraries(bout++ PUBLIC netCDF::netcdf-cxx4) else() find_package(PkgConfig REQUIRED) @@ -186,10 +228,10 @@ if (BOUT_USE_NETCDF) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${NETCDF_LDFLAGS_STRING}") endif() endif() - if (netCDF_DIR) + if(netCDF_DIR) set(netCDF_ROOT "${netCDF_DIR}") endif() - if (netCDFCxx_DIR) + if(netCDFCxx_DIR) set(netCDFCxx_ROOT "${netCDFCxx_DIR}") endif() endif() @@ -198,11 +240,11 @@ set(BOUT_HAS_NETCDF ${BOUT_USE_NETCDF}) option(BOUT_USE_ADIOS2 "Enable support for ADIOS output" OFF) option(BOUT_DOWNLOAD_ADIOS2 "Download and build ADIOS2" OFF) -if (BOUT_USE_ADIOS2) +if(BOUT_USE_ADIOS2) enable_language(C) find_package(MPI REQUIRED COMPONENTS C) - if (BOUT_DOWNLOAD_ADIOS2) + if(BOUT_DOWNLOAD_ADIOS2) message(STATUS "Downloading and configuring ADIOS2") include(FetchContent) FetchContent_Declare( @@ -210,15 +252,33 @@ if (BOUT_USE_ADIOS2) GIT_REPOSITORY https://github.com/ornladios/ADIOS2.git GIT_TAG origin/master GIT_SHALLOW 1 - ) - set(ADIOS2_USE_MPI ON CACHE BOOL "" FORCE) - set(ADIOS2_USE_Fortran OFF CACHE BOOL "" FORCE) - set(ADIOS2_USE_Python OFF CACHE BOOL "" FORCE) - set(ADIOS2_BUILD_EXAMPLES OFF CACHE BOOL "" FORCE) + ) + set(ADIOS2_USE_MPI + ON + CACHE BOOL "" FORCE + ) + set(ADIOS2_USE_Fortran + OFF + CACHE BOOL "" FORCE + ) + set(ADIOS2_USE_Python + OFF + CACHE BOOL "" FORCE + ) + set(ADIOS2_BUILD_EXAMPLES + OFF + CACHE BOOL "" FORCE + ) # Disable testing, or ADIOS will try to find or install GTEST - set(BUILD_TESTING OFF CACHE BOOL "" FORCE) + set(BUILD_TESTING + OFF + CACHE BOOL "" FORCE + ) # Note: SST requires but doesn't check at configure time - set(ADIOS2_USE_SST OFF CACHE BOOL "" FORCE) + set(ADIOS2_USE_SST + OFF + CACHE BOOL "" FORCE + ) FetchContent_MakeAvailable(adios2) message(STATUS "ADIOS2 done configuring") else() @@ -229,9 +289,8 @@ endif() message(STATUS "ADIOS2 support: ${BOUT_USE_ADIOS2}") set(BOUT_HAS_ADIOS2 ${BOUT_USE_ADIOS2}) - option(BOUT_USE_FFTW "Enable support for FFTW" ON) -if (BOUT_USE_FFTW) +if(BOUT_USE_FFTW) find_package(FFTW REQUIRED) target_link_libraries(bout++ PUBLIC FFTW::FFTW) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${FFTW_LIBRARIES}") @@ -244,15 +303,15 @@ option(BOUT_USE_LAPACK "Enable support for LAPACK" AUTO) set_property(CACHE BOUT_USE_LAPACK PROPERTY STRINGS ${ON_OFF_AUTO}) set(LAPACK_FOUND OFF) -if (BOUT_USE_LAPACK) - if (NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") +if(BOUT_USE_LAPACK) + if(NOT CMAKE_SYSTEM_NAME STREQUAL "CrayLinuxEnvironment") # Cray wrappers sort this out for us - if (BOUT_USE_LAPACK STREQUAL ON) + if(BOUT_USE_LAPACK STREQUAL ON) find_package(LAPACK REQUIRED) else() find_package(LAPACK) endif() - if (LAPACK_FOUND) + if(LAPACK_FOUND) target_link_libraries(bout++ PUBLIC "${LAPACK_LIBRARIES}") string(JOIN " " CONFIG_LAPACK_LIBRARIES ${LAPACK_LIBRARIES}) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${CONFIG_LAPACK_LIBRARIES}") @@ -264,7 +323,7 @@ message(STATUS "LAPACK support: ${LAPACK_FOUND}") set(BOUT_HAS_LAPACK ${LAPACK_FOUND}) option(BOUT_USE_SLEPC "Enable support for SLEPc eigen solver" OFF) -if (BOUT_USE_SLEPC) +if(BOUT_USE_SLEPC) find_package(SLEPc REQUIRED) target_link_libraries(bout++ PUBLIC SLEPc::SLEPc) string(JOIN " " CONFIG_SLEPC_LIBRARIES ${SLEPC_LIBRARIES}) @@ -275,44 +334,69 @@ set(BOUT_HAS_SLEPC ${BOUT_USE_SLEPC}) option(BOUT_DOWNLOAD_SUNDIALS "Download and build SUNDIALS" OFF) # Force BOUT_USE_SUNDIALS if we're downloading it! -cmake_dependent_option(BOUT_USE_SUNDIALS "Enable support for SUNDIALS time solvers" OFF - "NOT BOUT_DOWNLOAD_SUNDIALS" ON) -if (BOUT_USE_SUNDIALS) +cmake_dependent_option( + BOUT_USE_SUNDIALS "Enable support for SUNDIALS time solvers" OFF + "NOT BOUT_DOWNLOAD_SUNDIALS" ON +) +if(BOUT_USE_SUNDIALS) enable_language(C) - if (BOUT_DOWNLOAD_SUNDIALS) + if(BOUT_DOWNLOAD_SUNDIALS) message(STATUS "Downloading and configuring SUNDIALS") include(FetchContent) FetchContent_Declare( sundials GIT_REPOSITORY https://github.com/LLNL/sundials - GIT_TAG v7.2.1 - ) + GIT_TAG v7.2.1 + ) # Note: These are settings for building SUNDIALS - set(EXAMPLES_ENABLE_C OFF CACHE BOOL "" FORCE) - set(EXAMPLES_INSTALL OFF CACHE BOOL "" FORCE) - set(ENABLE_MPI ${BOUT_USE_MPI} CACHE BOOL "" FORCE) - set(ENABLE_OPENMP OFF CACHE BOOL "" FORCE) - if (BUILD_SHARED_LIBS) - set(BUILD_STATIC_LIBS OFF CACHE BOOL "" FORCE) + set(EXAMPLES_ENABLE_C + OFF + CACHE BOOL "" FORCE + ) + set(EXAMPLES_INSTALL + OFF + CACHE BOOL "" FORCE + ) + set(ENABLE_MPI + ${BOUT_USE_MPI} + CACHE BOOL "" FORCE + ) + set(ENABLE_OPENMP + OFF + CACHE BOOL "" FORCE + ) + if(BUILD_SHARED_LIBS) + set(BUILD_STATIC_LIBS + OFF + CACHE BOOL "" FORCE + ) else() - set(BUILD_STATIC_LIBS ON CACHE BOOL "" FORCE) + set(BUILD_STATIC_LIBS + ON + CACHE BOOL "" FORCE + ) endif() FetchContent_MakeAvailable(sundials) message(STATUS "SUNDIALS done configuring") else() find_package(SUNDIALS REQUIRED) - if (SUNDIALS_VERSION VERSION_LESS 4.0.0) - message(FATAL_ERROR "SUNDIALS_VERSION 4.0.0 or newer is required. Found version ${SUNDIALS_VERSION}.") + if(SUNDIALS_VERSION VERSION_LESS 4.0.0) + message( + FATAL_ERROR + "SUNDIALS_VERSION 4.0.0 or newer is required. Found version ${SUNDIALS_VERSION}." + ) endif() endif() - if (SUNDIALS_DIR) + if(SUNDIALS_DIR) set(SUNDIALS_ROOT "${SUNDIALS_DIR}") endif() target_link_libraries(bout++ PUBLIC SUNDIALS::nvecparallel) target_link_libraries(bout++ PUBLIC SUNDIALS::cvode) target_link_libraries(bout++ PUBLIC SUNDIALS::ida) target_link_libraries(bout++ PUBLIC SUNDIALS::arkode) - set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${SUNDIALS_cvode_LIBRARY} ${SUNDIALS_ida_LIBRARY} ${SUNDIALS_arkode_LIBRARY} ${SUNDIALS_nvecparallel_LIBRARY}") + set(CONFIG_LDFLAGS + "${CONFIG_LDFLAGS} ${SUNDIALS_cvode_LIBRARY} ${SUNDIALS_ida_LIBRARY} ${SUNDIALS_arkode_LIBRARY} ${SUNDIALS_nvecparallel_LIBRARY}" + ) endif() message(STATUS "SUNDIALS support: ${BOUT_USE_SUNDIALS}") set(BOUT_HAS_SUNDIALS ${BOUT_USE_SUNDIALS}) @@ -321,46 +405,51 @@ set(BOUT_HAS_CVODE ${BOUT_USE_SUNDIALS}) set(BOUT_HAS_IDA ${BOUT_USE_SUNDIALS}) set(ON_OFF_AUTO ON OFF AUTO) -set(BOUT_USE_NLS AUTO CACHE STRING "Enable Native Language Support") +set(BOUT_USE_NLS + AUTO + CACHE STRING "Enable Native Language Support" +) set_property(CACHE BOUT_USE_NLS PROPERTY STRINGS ${ON_OFF_AUTO}) set(BOUT_HAS_GETTEXT OFF) -if (BOUT_USE_NLS) +if(BOUT_USE_NLS) find_package(Gettext) - if (GETTEXT_FOUND) + if(GETTEXT_FOUND) find_package(Intl) - if (Intl_FOUND) - target_link_libraries(bout++ - PUBLIC ${Intl_LIBRARIES}) - target_include_directories(bout++ - PUBLIC ${Intl_INCLUDE_DIRS}) + if(Intl_FOUND) + target_link_libraries(bout++ PUBLIC ${Intl_LIBRARIES}) + target_include_directories(bout++ PUBLIC ${Intl_INCLUDE_DIRS}) set(BOUT_HAS_GETTEXT ON) else() - if (NOT BOUT_USE_NLS STREQUAL "AUTO") - message(FATAL_ERROR "Intl not found but requested!") + if(NOT BOUT_USE_NLS STREQUAL "AUTO") + message(FATAL_ERROR "Intl not found but requested!") endif() endif() else() - if (NOT BOUT_USE_NLS STREQUAL "AUTO") + if(NOT BOUT_USE_NLS STREQUAL "AUTO") message(FATAL_ERROR "GETTEXT not found but requested!") endif() endif() endif() option(BOUT_USE_SCOREP "Enable support for Score-P based instrumentation" OFF) -if (BOUT_USE_SCOREP) - message(STATUS "Score-P support enabled. Please make sure you are calling CMake like so: +if(BOUT_USE_SCOREP) + message( + STATUS + "Score-P support enabled. Please make sure you are calling CMake like so: SCOREP_WRAPPER=off cmake -DCMAKE_C_COMPILER=scorep-mpicc -DCMAKE_CXX_COMPILER=scorep-mpicxx -") +" + ) endif() set(BOUT_HAS_SCOREP ${BOUT_USE_SCOREP}) -option(BOUT_USE_UUID_SYSTEM_GENERATOR "Enable support for using a system UUID generator" ON) -if (BOUT_USE_UUID_SYSTEM_GENERATOR) +option(BOUT_USE_UUID_SYSTEM_GENERATOR + "Enable support for using a system UUID generator" ON +) +if(BOUT_USE_UUID_SYSTEM_GENERATOR) find_package(Libuuid QUIET) - if (Libuuid_FOUND) - target_link_libraries(bout++ - PUBLIC Libuuid::libuuid) + if(Libuuid_FOUND) + target_link_libraries(bout++ PUBLIC Libuuid::libuuid) set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} ${Libuuid_LIBRARIES}") else() message(STATUS "libuuid not found, using fallback UUID generator") @@ -370,24 +459,42 @@ endif() message(STATUS "UUID_SYSTEM_GENERATOR: ${BOUT_USE_UUID_SYSTEM_GENERATOR}") set(BOUT_HAS_UUID_SYSTEM_GENERATOR ${BOUT_USE_UUID_SYSTEM_GENERATOR}) -cmake_dependent_option(BOUT_USE_SYSTEM_CPPTRACE "Use external installation of cpptrace" OFF - "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/cpptrace/CMakeLists.txt" ON) +cmake_dependent_option( + BOUT_USE_SYSTEM_CPPTRACE + "Use external installation of cpptrace" + OFF + "BOUT_UPDATE_GIT_SUBMODULE OR EXISTS ${PROJECT_SOURCE_DIR}/externalpackages/cpptrace/CMakeLists.txt" + ON +) if(BOUT_USE_SYSTEM_CPPTRACE) message(STATUS "Using external cpptrace") find_package(cpptrace REQUIRED) - get_target_property(CPPTRACE_INCLUDE_PATH cpptrace::cpptrace INTERFACE_INCLUDE_DIRECTORIES) + get_target_property( + CPPTRACE_INCLUDE_PATH cpptrace::cpptrace INTERFACE_INCLUDE_DIRECTORIES + ) else() message(STATUS "Using cpptrace submodule") bout_update_submodules() # Need a fork with some fixes for CMake - set(CPPTRACE_LIBDWARF_REPO "https://github.com/ZedThree/libdwarf-lite.git" CACHE STRING "" FORCE) - set(CPPTRACE_LIBDWARF_TAG "ebe10a39afd56b8247de633bfe17666ad50ab95e" CACHE STRING "" FORCE) + set(CPPTRACE_LIBDWARF_REPO + "https://github.com/ZedThree/libdwarf-lite.git" + CACHE STRING "" FORCE + ) + set(CPPTRACE_LIBDWARF_TAG + "ebe10a39afd56b8247de633bfe17666ad50ab95e" + CACHE STRING "" FORCE + ) add_subdirectory(externalpackages/cpptrace) if(NOT TARGET cpptrace::cpptrace) - message(FATAL_ERROR "cpptrace not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?") + message( + FATAL_ERROR + "cpptrace not found! Have you disabled the git submodules (BOUT_UPDATE_GIT_SUBMODULE)?" + ) endif() - set(CPPTRACE_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/cpptrace/include") + set(CPPTRACE_INCLUDE_PATH + "${CMAKE_CURRENT_SOURCE_DIR}/externalpackages/cpptrace/include" + ) endif() set(CONFIG_CFLAGS "${CONFIG_CFLAGS} -I\${CPPTRACE_INCLUDE_PATH}") target_link_libraries(bout++ PUBLIC cpptrace::cpptrace) diff --git a/cmake/SetupCompilers.cmake b/cmake/SetupCompilers.cmake index 647cb20f75..fbe960b0f0 100644 --- a/cmake/SetupCompilers.cmake +++ b/cmake/SetupCompilers.cmake @@ -1,44 +1,58 @@ - # Note: Currently BOUT++ always needs MPI. This option just determines # whether the find_* routines are used option(BOUT_ENABLE_MPI "Enable MPI support" ON) if(BOUT_ENABLE_MPI) - # This might not be entirely sensible, but helps CMake to find the - # correct MPI, workaround for https://gitlab.kitware.com/cmake/cmake/issues/18895 - find_program(MPIEXEC_EXECUTABLE NAMES mpiexec mpirun) - find_package(MPI REQUIRED) -endif () + # This might not be entirely sensible, but helps CMake to find the + # correct MPI, workaround for https://gitlab.kitware.com/cmake/cmake/issues/18895 + find_program(MPIEXEC_EXECUTABLE NAMES mpiexec mpirun) + find_package(MPI REQUIRED) +endif() set(BOUT_USE_MPI ${BOUT_ENABLE_MPI}) option(BOUT_ENABLE_OPENMP "Enable OpenMP support" OFF) -set(BOUT_OPENMP_SCHEDULE static CACHE STRING "Set OpenMP schedule") -set_property(CACHE BOUT_OPENMP_SCHEDULE PROPERTY STRINGS static dynamic guided auto) -if (BOUT_ENABLE_OPENMP) +set(BOUT_OPENMP_SCHEDULE + static + CACHE STRING "Set OpenMP schedule" +) +set_property( + CACHE BOUT_OPENMP_SCHEDULE PROPERTY STRINGS static dynamic guided auto +) +if(BOUT_ENABLE_OPENMP) find_package(OpenMP REQUIRED) set(possible_openmp_schedules static dynamic guided auto) - if (NOT BOUT_OPENMP_SCHEDULE IN_LIST possible_openmp_schedules) - message(FATAL_ERROR "BOUT_OPENMP_SCHEDULE must be one of ${possible_openmp_schedules}; got ${BOUT_OPENMP_SCHEDULE}") + if(NOT BOUT_OPENMP_SCHEDULE IN_LIST possible_openmp_schedules) + message( + FATAL_ERROR + "BOUT_OPENMP_SCHEDULE must be one of ${possible_openmp_schedules}; got ${BOUT_OPENMP_SCHEDULE}" + ) endif() message(STATUS "OpenMP schedule: ${BOUT_OPENMP_SCHEDULE}") -endif () +endif() set(BOUT_USE_OPENMP ${BOUT_ENABLE_OPENMP}) message(STATUS "Enable OpenMP: ${BOUT_ENABLE_OPENMP}") option(BOUT_ENABLE_CUDA "Enable CUDA support" OFF) -set(CUDA_ARCH "compute_70,code=sm_70" CACHE STRING "CUDA architecture") +set(CUDA_ARCH + "compute_70,code=sm_70" + CACHE STRING "CUDA architecture" +) if(BOUT_ENABLE_CUDA) - # Set specific options for CUDA if enabled - enable_language(CUDA) - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -gencode arch=${CUDA_ARCH} -ccbin ${CMAKE_CXX_COMPILER}") - if (BOUT_ENABLE_RAJA) - # RAJA uses lambda expressions - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr") - endif () + # Set specific options for CUDA if enabled + enable_language(CUDA) + set(CMAKE_CUDA_FLAGS + "${CMAKE_CUDA_FLAGS} -gencode arch=${CUDA_ARCH} -ccbin ${CMAKE_CXX_COMPILER}" + ) + if(BOUT_ENABLE_RAJA) + # RAJA uses lambda expressions + set(CMAKE_CUDA_FLAGS + "${CMAKE_CUDA_FLAGS} --expt-extended-lambda --expt-relaxed-constexpr" + ) + endif() -# TODO Ensure openmp flags are not enabled twice! - if (BOUT_ENABLE_OPENMP) - # CMAKE_CUDA_FLAGS does not pass OpenMP_CXX_FLAGS to the host compiler by default - set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler ${OpenMP_CXX_FLAGS}") - endif () + # TODO Ensure openmp flags are not enabled twice! + if(BOUT_ENABLE_OPENMP) + # CMAKE_CUDA_FLAGS does not pass OpenMP_CXX_FLAGS to the host compiler by default + set(CMAKE_CUDA_FLAGS "${CMAKE_CUDA_FLAGS} -Xcompiler ${OpenMP_CXX_FLAGS}") + endif() endif() set(BOUT_HAS_CUDA ${BOUT_ENABLE_CUDA}) diff --git a/examples/6field-simple/CMakeLists.txt b/examples/6field-simple/CMakeLists.txt index 6a51327cda..b6584c49e4 100644 --- a/examples/6field-simple/CMakeLists.txt +++ b/examples/6field-simple/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(6field-simple LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(elm_6f +bout_add_example( + elm_6f SOURCES elm_6f.cxx - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc) + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc +) diff --git a/examples/IMEX/advection-diffusion/CMakeLists.txt b/examples/IMEX/advection-diffusion/CMakeLists.txt index 334d48d767..f2008f3479 100644 --- a/examples/IMEX/advection-diffusion/CMakeLists.txt +++ b/examples/IMEX/advection-diffusion/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(advection-diffusion LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/IMEX/advection-reaction/CMakeLists.txt b/examples/IMEX/advection-reaction/CMakeLists.txt index 03e8686371..d89ff10a78 100644 --- a/examples/IMEX/advection-reaction/CMakeLists.txt +++ b/examples/IMEX/advection-reaction/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(advection-reaction LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(split_operator +bout_add_example( + split_operator SOURCES split_operator.cxx - EXTRA_FILES simple_xz.nc) + EXTRA_FILES simple_xz.nc +) diff --git a/examples/IMEX/diffusion-nl/CMakeLists.txt b/examples/IMEX/diffusion-nl/CMakeLists.txt index 664d16e042..73c7250bb5 100644 --- a/examples/IMEX/diffusion-nl/CMakeLists.txt +++ b/examples/IMEX/diffusion-nl/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(diffusion-nl LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/IMEX/drift-wave-constraint/CMakeLists.txt b/examples/IMEX/drift-wave-constraint/CMakeLists.txt index 5680b5367e..b72396d2f0 100644 --- a/examples/IMEX/drift-wave-constraint/CMakeLists.txt +++ b/examples/IMEX/drift-wave-constraint/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(drift-wave-constraint LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/IMEX/drift-wave/CMakeLists.txt b/examples/IMEX/drift-wave/CMakeLists.txt index e3e2a1b8ee..44fbbd7b0f 100644 --- a/examples/IMEX/drift-wave/CMakeLists.txt +++ b/examples/IMEX/drift-wave/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(drift-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/blob2d-laplacexz/CMakeLists.txt b/examples/blob2d-laplacexz/CMakeLists.txt index 17f9ebe97a..855d86af70 100644 --- a/examples/blob2d-laplacexz/CMakeLists.txt +++ b/examples/blob2d-laplacexz/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(blob2d-laplacexz LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/blob2d-outerloop/CMakeLists.txt b/examples/blob2d-outerloop/CMakeLists.txt index cd7187ee3f..e991b45d51 100644 --- a/examples/blob2d-outerloop/CMakeLists.txt +++ b/examples/blob2d-outerloop/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(blob2d-outerloop LANGUAGES CXX C) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/blob2d/CMakeLists.txt b/examples/blob2d/CMakeLists.txt index a4772874d9..f3f93f0246 100644 --- a/examples/blob2d/CMakeLists.txt +++ b/examples/blob2d/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(blob2d LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(blob2d +bout_add_example( + blob2d SOURCES blob2d.cxx DATA_DIRS delta_0.25 delta_1 delta_10 two_blobs data - EXTRA_FILES blob_velocity.py) + EXTRA_FILES blob_velocity.py +) diff --git a/examples/boundary-conditions/advection/CMakeLists.txt b/examples/boundary-conditions/advection/CMakeLists.txt index a4ab73a24d..edd55d183a 100644 --- a/examples/boundary-conditions/advection/CMakeLists.txt +++ b/examples/boundary-conditions/advection/CMakeLists.txt @@ -2,13 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(advection LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(advection +bout_add_example( + advection SOURCES advection.cxx - DATA_DIRS central-dirichlet - central-free - central-free-o3 - upwind) + DATA_DIRS central-dirichlet central-free central-free-o3 upwind +) diff --git a/examples/boutpp/CMakeLists.txt b/examples/boutpp/CMakeLists.txt index e46a7ae990..1cc5c9619b 100644 --- a/examples/boutpp/CMakeLists.txt +++ b/examples/boutpp/CMakeLists.txt @@ -1,6 +1,6 @@ cmake_minimum_required(VERSION 3.13) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/conducting-wall-mode/CMakeLists.txt b/examples/conducting-wall-mode/CMakeLists.txt index 857a22038e..0e000c1ba8 100644 --- a/examples/conducting-wall-mode/CMakeLists.txt +++ b/examples/conducting-wall-mode/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(conducting-wall-mode LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(conducting-wall-mode +bout_add_example( + conducting-wall-mode SOURCES cwm.cxx - EXTRA_FILES cwm_grid.nc) + EXTRA_FILES cwm_grid.nc +) diff --git a/examples/conduction-snb/CMakeLists.txt b/examples/conduction-snb/CMakeLists.txt index 45072dbe59..3de25718d1 100644 --- a/examples/conduction-snb/CMakeLists.txt +++ b/examples/conduction-snb/CMakeLists.txt @@ -2,11 +2,20 @@ cmake_minimum_required(VERSION 3.13) project(conduction-snb LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(conduction-snb +bout_add_example( + conduction-snb SOURCES conduction-snb.cxx - EXTRA_FILES fit_temperature.py sinusoid.py snb.csv spitzer-harm.csv step.py temperature.csv vfp.csv - DATA_DIRS data step) + EXTRA_FILES + fit_temperature.py + sinusoid.py + snb.csv + spitzer-harm.csv + step.py + temperature.csv + vfp.csv + DATA_DIRS data step +) diff --git a/examples/conduction/CMakeLists.txt b/examples/conduction/CMakeLists.txt index f26b838621..b81164f614 100644 --- a/examples/conduction/CMakeLists.txt +++ b/examples/conduction/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(conduction LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(conduction +bout_add_example( + conduction SOURCES conduction.cxx DATA_DIRS data fromfile - EXTRA_FILES generate.py) + EXTRA_FILES generate.py +) diff --git a/examples/constraints/alfven-wave/CMakeLists.txt b/examples/constraints/alfven-wave/CMakeLists.txt index a95ace4086..375415ab3c 100644 --- a/examples/constraints/alfven-wave/CMakeLists.txt +++ b/examples/constraints/alfven-wave/CMakeLists.txt @@ -2,12 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(constraints-alfven-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(constraints-alfven-wave +bout_add_example( + constraints-alfven-wave SOURCES alfven.cxx DATA_DIRS cbm18 data - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc) - + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc +) diff --git a/examples/constraints/laplace-dae/CMakeLists.txt b/examples/constraints/laplace-dae/CMakeLists.txt index e487bd6a0a..d515fc3515 100644 --- a/examples/constraints/laplace-dae/CMakeLists.txt +++ b/examples/constraints/laplace-dae/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(constraints-laplace-dae LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(constraints-laplace-dae +bout_add_example( + constraints-laplace-dae SOURCES laplace_dae.cxx - EXTRA_FILES simple_xz.nc) + EXTRA_FILES simple_xz.nc +) diff --git a/examples/dalf3/CMakeLists.txt b/examples/dalf3/CMakeLists.txt index 5f5b3d701d..0b6f8461f5 100644 --- a/examples/dalf3/CMakeLists.txt +++ b/examples/dalf3/CMakeLists.txt @@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(dalf3 LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() - -bout_add_example(dalf3 +bout_add_example( + dalf3 SOURCES dalf3.cxx - EXTRA_FILES cbm18_8_y064_x516_090309.nc) + EXTRA_FILES cbm18_8_y064_x516_090309.nc +) diff --git a/examples/eigen-box/CMakeLists.txt b/examples/eigen-box/CMakeLists.txt index 76af4fbaa6..305e330652 100644 --- a/examples/eigen-box/CMakeLists.txt +++ b/examples/eigen-box/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(eigen-box LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(eigen-box +bout_add_example( + eigen-box SOURCES eigen-box.cxx - EXTRA_FILES eigenvals.py) + EXTRA_FILES eigenvals.py +) diff --git a/examples/elm-pb-outerloop/CMakeLists.txt b/examples/elm-pb-outerloop/CMakeLists.txt index 008918a87f..12dc2f7696 100644 --- a/examples/elm-pb-outerloop/CMakeLists.txt +++ b/examples/elm-pb-outerloop/CMakeLists.txt @@ -2,16 +2,16 @@ cmake_minimum_required(VERSION 3.13) project(elm_pb LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(elm_pb_outerloop +bout_add_example( + elm_pb_outerloop SOURCES elm_pb_outerloop.cxx EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc ) if(BOUT_HAS_CUDA) - set_source_files_properties(elm_pb_outerloop.cxx PROPERTIES LANGUAGE CUDA ) + set_source_files_properties(elm_pb_outerloop.cxx PROPERTIES LANGUAGE CUDA) endif() - diff --git a/examples/elm-pb/CMakeLists.txt b/examples/elm-pb/CMakeLists.txt index 8c672822cf..6cdd0f9cd9 100644 --- a/examples/elm-pb/CMakeLists.txt +++ b/examples/elm-pb/CMakeLists.txt @@ -2,14 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(elm_pb LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(elm_pb +bout_add_example( + elm_pb SOURCES elm_pb.cxx - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc - data/BOUT.inp - data-hypre/BOUT.inp - data-nonlinear/BOUT.inp) - + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc data/BOUT.inp data-hypre/BOUT.inp + data-nonlinear/BOUT.inp +) diff --git a/examples/fci-wave/CMakeLists.txt b/examples/fci-wave/CMakeLists.txt index 2680b1310e..ea299edb5a 100644 --- a/examples/fci-wave/CMakeLists.txt +++ b/examples/fci-wave/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(fci-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(fci-wave +bout_add_example( + fci-wave SOURCES fci-wave.cxx DATA_DIRS div div-integrate logn - EXTRA_FILES compare-density.py) + EXTRA_FILES compare-density.py +) diff --git a/examples/finite-volume/diffusion/CMakeLists.txt b/examples/finite-volume/diffusion/CMakeLists.txt index 0dd7d220f6..ccc3d0f7e0 100644 --- a/examples/finite-volume/diffusion/CMakeLists.txt +++ b/examples/finite-volume/diffusion/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(finite-volume-diffusion LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(finite-volume-diffusion +bout_add_example( + finite-volume-diffusion SOURCES diffusion.cxx - EXTRA_FILES mms.py) + EXTRA_FILES mms.py +) diff --git a/examples/finite-volume/fluid/CMakeLists.txt b/examples/finite-volume/fluid/CMakeLists.txt index e9028459ec..e798da77fe 100644 --- a/examples/finite-volume/fluid/CMakeLists.txt +++ b/examples/finite-volume/fluid/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(finite-volume-fluid LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(finite-volume-fluid +bout_add_example( + finite-volume-fluid SOURCES fluid.cxx DATA_DIRS data mms - EXTRA_FILES mms.py) + EXTRA_FILES mms.py +) diff --git a/examples/finite-volume/test/CMakeLists.txt b/examples/finite-volume/test/CMakeLists.txt index 73fe99f960..d09567e193 100644 --- a/examples/finite-volume/test/CMakeLists.txt +++ b/examples/finite-volume/test/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(finite-volume-test LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/gas-compress/CMakeLists.txt b/examples/gas-compress/CMakeLists.txt index 1b4416d32b..75978ba584 100644 --- a/examples/gas-compress/CMakeLists.txt +++ b/examples/gas-compress/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(gas-compress LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(gas-compress +bout_add_example( + gas-compress SOURCES gas_compress.cxx gas_compress.hxx DATA_DIRS rayleigh-taylor sod-shock - EXTRA_FILES rt.grd.nc sod.grd.nc) + EXTRA_FILES rt.grd.nc sod.grd.nc +) diff --git a/examples/gyro-gem/CMakeLists.txt b/examples/gyro-gem/CMakeLists.txt index 7189bb06b8..5d83848688 100644 --- a/examples/gyro-gem/CMakeLists.txt +++ b/examples/gyro-gem/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(gyro-gem LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(gyro-gem +bout_add_example( + gyro-gem SOURCES gem.cxx - EXTRA_FILES cyclone_68x32.nc) + EXTRA_FILES cyclone_68x32.nc +) diff --git a/examples/hasegawa-wakatani-3d/CMakeLists.txt b/examples/hasegawa-wakatani-3d/CMakeLists.txt index 0cdb5207f8..c555d6d7f9 100644 --- a/examples/hasegawa-wakatani-3d/CMakeLists.txt +++ b/examples/hasegawa-wakatani-3d/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(hw3d LANGUAGES CXX C) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/hasegawa-wakatani/CMakeLists.txt b/examples/hasegawa-wakatani/CMakeLists.txt index c9b9401b3a..53f4e5ed4f 100644 --- a/examples/hasegawa-wakatani/CMakeLists.txt +++ b/examples/hasegawa-wakatani/CMakeLists.txt @@ -2,9 +2,8 @@ cmake_minimum_required(VERSION 3.13) project(hasegawa-wakatani LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() bout_add_example(hasegawa-wakatani SOURCES hw.cxx) - diff --git a/examples/invertable_operator/CMakeLists.txt b/examples/invertable_operator/CMakeLists.txt index f054466f23..f18a085ec1 100644 --- a/examples/invertable_operator/CMakeLists.txt +++ b/examples/invertable_operator/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(invertable_operator LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(invertable_operator +bout_add_example( + invertable_operator SOURCES invertable_operator.cxx - REQUIRES BOUT_HAS_PETSC) + REQUIRES BOUT_HAS_PETSC +) diff --git a/examples/laplacexy/alfven-wave/CMakeLists.txt b/examples/laplacexy/alfven-wave/CMakeLists.txt index 2423400519..ebda7703e6 100644 --- a/examples/laplacexy/alfven-wave/CMakeLists.txt +++ b/examples/laplacexy/alfven-wave/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(laplacexy-alfven-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(laplacexy-alfven-wave +bout_add_example( + laplacexy-alfven-wave SOURCES alfven.cxx DATA_DIRS cbm18 data - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc) + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc d3d_119919.nc +) diff --git a/examples/laplacexy/laplace_perp/CMakeLists.txt b/examples/laplacexy/laplace_perp/CMakeLists.txt index 388513b044..d12725c5de 100644 --- a/examples/laplacexy/laplace_perp/CMakeLists.txt +++ b/examples/laplacexy/laplace_perp/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(laplacexy-laplace_perp LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(laplacexy-laplace_perp +bout_add_example( + laplacexy-laplace_perp SOURCES test.cxx - EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc - DATA_DIRS square torus) + EXTRA_FILES cbm18_dens8.grid_nx68ny64.nc + DATA_DIRS square torus +) diff --git a/examples/laplacexy/simple/CMakeLists.txt b/examples/laplacexy/simple/CMakeLists.txt index 7859a08259..e999b9cf58 100644 --- a/examples/laplacexy/simple/CMakeLists.txt +++ b/examples/laplacexy/simple/CMakeLists.txt @@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(laplacexy-simple LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(laplacexy-simple +bout_add_example( + laplacexy-simple SOURCES test-laplacexy.cxx DATA_DIRS data hypre ) diff --git a/examples/monitor-newapi/CMakeLists.txt b/examples/monitor-newapi/CMakeLists.txt index 0ee3ee7f85..5c2022b792 100644 --- a/examples/monitor-newapi/CMakeLists.txt +++ b/examples/monitor-newapi/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(monitor-newapi LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/orszag-tang/CMakeLists.txt b/examples/orszag-tang/CMakeLists.txt index 9ac8fd8d1c..46eae5d0f2 100644 --- a/examples/orszag-tang/CMakeLists.txt +++ b/examples/orszag-tang/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(orszag-tang LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(orszag-tang +bout_add_example( + orszag-tang SOURCES mhd.cxx - EXTRA_FILES data/otv.grd.nc) + EXTRA_FILES data/otv.grd.nc +) diff --git a/examples/preconditioning/wave/CMakeLists.txt b/examples/preconditioning/wave/CMakeLists.txt index 437f39fe3a..05d7548832 100644 --- a/examples/preconditioning/wave/CMakeLists.txt +++ b/examples/preconditioning/wave/CMakeLists.txt @@ -2,7 +2,7 @@ cmake_minimum_required(VERSION 3.13) project(preconditioning-wave LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() diff --git a/examples/staggered_grid/CMakeLists.txt b/examples/staggered_grid/CMakeLists.txt index dd2b3b463e..1950d16c0b 100644 --- a/examples/staggered_grid/CMakeLists.txt +++ b/examples/staggered_grid/CMakeLists.txt @@ -2,11 +2,13 @@ cmake_minimum_required(VERSION 3.13) project(staggered_grid LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(staggered_grid +bout_add_example( + staggered_grid SOURCES test_staggered.cxx EXTRA_FILES generate.py run test-staggered.nc - DATA_DIRS data test) + DATA_DIRS data test +) diff --git a/examples/subsampling/CMakeLists.txt b/examples/subsampling/CMakeLists.txt index 86f71d98f5..c6c487e687 100644 --- a/examples/subsampling/CMakeLists.txt +++ b/examples/subsampling/CMakeLists.txt @@ -2,11 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(subsampling LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(subsampling +bout_add_example( + subsampling SOURCES monitor.cxx - EXTRA_FILES show.py) - + EXTRA_FILES show.py +) diff --git a/examples/wave-slab/CMakeLists.txt b/examples/wave-slab/CMakeLists.txt index b1943c4e1c..8de548b71d 100644 --- a/examples/wave-slab/CMakeLists.txt +++ b/examples/wave-slab/CMakeLists.txt @@ -2,10 +2,12 @@ cmake_minimum_required(VERSION 3.13) project(wave-slab LANGUAGES CXX) -if (NOT TARGET bout++::bout++) +if(NOT TARGET bout++::bout++) find_package(bout++ REQUIRED) endif() -bout_add_example(wave-slab +bout_add_example( + wave-slab SOURCES wave_slab.cxx - EXTRA_FILES generate.py) + EXTRA_FILES generate.py +) diff --git a/externalpackages/PVODE/CMakeLists.txt b/externalpackages/PVODE/CMakeLists.txt index 5e3f8f2f63..51afe8047c 100644 --- a/externalpackages/PVODE/CMakeLists.txt +++ b/externalpackages/PVODE/CMakeLists.txt @@ -6,14 +6,17 @@ else() cmake_policy(VERSION 3.12) endif() -project(PVODE +project( + PVODE DESCRIPTION "ODE Solver" VERSION 0.1 - LANGUAGES CXX) + LANGUAGES CXX +) find_package(MPI REQUIRED) -add_library(pvode +add_library( + pvode source/cvode.cpp source/nvector.cpp source/llnlmath.cpp @@ -33,45 +36,43 @@ add_library(pvode include/pvode/smalldense.h include/pvode/spgmr.h include/pvode/vector.h - ) - -target_include_directories(pvode PUBLIC - $ - $ - $ - ) +) + +target_include_directories( + pvode + PUBLIC $ + $ + $ +) target_link_libraries(pvode PUBLIC MPI::MPI_CXX) -add_library(pvpre - include/pvode/pvbbdpre.h - precon/pvbbdpre.cpp - precon/band.cpp - precon/band.h - ) - +add_library( + pvpre include/pvode/pvbbdpre.h precon/pvbbdpre.cpp precon/band.cpp + precon/band.h +) -set_target_properties(pvode PROPERTIES - SOVERSION 1.0.0) +set_target_properties(pvode PROPERTIES SOVERSION 1.0.0) -target_include_directories(pvpre PUBLIC - $ - $ - $ - ) +target_include_directories( + pvpre + PUBLIC $ + $ + $ +) target_link_libraries(pvpre PUBLIC pvode MPI::MPI_CXX) - -set_target_properties(pvpre PROPERTIES - SOVERSION 1.0.0) +set_target_properties(pvpre PROPERTIES SOVERSION 1.0.0) include(GNUInstallDirs) -install(TARGETS pvode pvpre +install( + TARGETS pvode pvpre EXPORT PVODETargets LIBRARY DESTINATION "${CMAKE_INSTALL_LIBDIR}" ARCHIVE DESTINATION "${CMAKE_INSTALL_LIBDIR}" RUNTIME DESTINATION "${CMAKE_INSTALL_BINDIR}" - INCLUDES DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" - ) + INCLUDES + DESTINATION "${CMAKE_INSTALL_INCLUDEDIR}" +) install(DIRECTORY include/ DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}) include(CMakePackageConfigHelpers) @@ -79,17 +80,19 @@ write_basic_package_version_file( PVODEConfigVersion.cmake VERSION ${PACKAGE_VERSION} COMPATIBILITY SameMajorVersion - ) +) -install(EXPORT PVODETargets +install( + EXPORT PVODETargets FILE PVODEConfig.cmake NAMESPACE PVODE:: DESTINATION "${CMAKE_INSTALL_LIBDIR}/cmake/PVODE" - ) +) -export(EXPORT PVODETargets +export( + EXPORT PVODETargets FILE "${CMAKE_CURRENT_BINARY_DIR}/PVODEConfig.cmake" NAMESPACE PVODE:: - ) +) export(PACKAGE PVODE) diff --git a/manual/CMakeLists.txt b/manual/CMakeLists.txt index af4c528446..87f5101e58 100644 --- a/manual/CMakeLists.txt +++ b/manual/CMakeLists.txt @@ -6,20 +6,26 @@ find_package(Sphinx REQUIRED) set(BOUT_SPHINX_SOURCE ${CMAKE_CURRENT_SOURCE_DIR}/sphinx) set(BOUT_SPHINX_BUILD ${CMAKE_CURRENT_BINARY_DIR}/docs) -set(env_command - ${CMAKE_COMMAND} -E env PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} +set(env_command ${CMAKE_COMMAND} -E env + PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} ) -add_custom_target(sphinx-html - COMMAND ${env_command} ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} - COMMAND ${CMAKE_COMMAND} -E echo "Generated HTML docs in file://${BOUT_SPHINX_BUILD}/index.html" +add_custom_target( + sphinx-html + COMMAND ${env_command} ${SPHINX_EXECUTABLE} -b html ${BOUT_SPHINX_SOURCE} + ${BOUT_SPHINX_BUILD} + COMMAND ${CMAKE_COMMAND} -E echo + "Generated HTML docs in file://${BOUT_SPHINX_BUILD}/index.html" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating HTML documentation with Sphinx in ${BOUT_SPHINX_BUILD}" ) -add_custom_target(sphinx-pdf - COMMAND ${env_command} ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} ${BOUT_SPHINX_BUILD} - COMMAND ${CMAKE_COMMAND} -E echo "Generated PDF docs in file://${BOUT_SPHINX_BUILD}" +add_custom_target( + sphinx-pdf + COMMAND ${env_command} ${SPHINX_EXECUTABLE} -M latexpdf ${BOUT_SPHINX_SOURCE} + ${BOUT_SPHINX_BUILD} + COMMAND ${CMAKE_COMMAND} -E echo + "Generated PDF docs in file://${BOUT_SPHINX_BUILD}" WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Generating PDF documentation with Sphinx in ${BOUT_SPHINX_BUILD}" ) @@ -27,7 +33,8 @@ add_custom_target(sphinx-pdf add_custom_target(docs) add_dependencies(docs sphinx-html) -install(DIRECTORY ${BOUT_SPHINX_BUILD}/ +install( + DIRECTORY ${BOUT_SPHINX_BUILD}/ DESTINATION ${CMAKE_INSTALL_DATAROOTDIR}/doc/bout++/ EXCLUDE_FROM_ALL COMPONENT docs diff --git a/tests/MMS/CMakeLists.txt b/tests/MMS/CMakeLists.txt index 0c42da7074..a6667bcfa5 100644 --- a/tests/MMS/CMakeLists.txt +++ b/tests/MMS/CMakeLists.txt @@ -15,7 +15,7 @@ add_subdirectory(wave-1d-y) ######################################## # The following require boutpp: -if (BOUT_ENABLE_PYTHON) +if(BOUT_ENABLE_PYTHON) add_subdirectory(bracket) add_subdirectory(derivatives3) add_subdirectory(shiftedmetricinterp) diff --git a/tests/MMS/advection/arakawa/CMakeLists.txt b/tests/MMS/advection/arakawa/CMakeLists.txt index 267146ed51..6594cab2c2 100644 --- a/tests/MMS/advection/arakawa/CMakeLists.txt +++ b/tests/MMS/advection/arakawa/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-advection-arakawa - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-advection-arakawa + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES parent.py EXTRA_DEPENDS MMS-advection REQUIRES BOUT_RUN_ALL_TESTS diff --git a/tests/MMS/advection/central/CMakeLists.txt b/tests/MMS/advection/central/CMakeLists.txt index 9b2288856e..a5b5d3795d 100644 --- a/tests/MMS/advection/central/CMakeLists.txt +++ b/tests/MMS/advection/central/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-advection-central - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-advection-central + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES parent.py EXTRA_DEPENDS MMS-advection REQUIRES BOUT_RUN_ALL_TESTS diff --git a/tests/MMS/advection/upwind/CMakeLists.txt b/tests/MMS/advection/upwind/CMakeLists.txt index 3a9db4630a..baec34914e 100644 --- a/tests/MMS/advection/upwind/CMakeLists.txt +++ b/tests/MMS/advection/upwind/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-advection-upwind - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-advection-upwind + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES parent.py EXTRA_DEPENDS MMS-advection REQUIRES BOUT_RUN_ALL_TESTS diff --git a/tests/MMS/advection/weno3/CMakeLists.txt b/tests/MMS/advection/weno3/CMakeLists.txt index 293b6b95c9..5d6c1baefa 100644 --- a/tests/MMS/advection/weno3/CMakeLists.txt +++ b/tests/MMS/advection/weno3/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-advection-weno3 - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-advection-weno3 + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES parent.py EXTRA_DEPENDS MMS-advection REQUIRES BOUT_RUN_ALL_TESTS diff --git a/tests/MMS/bracket/CMakeLists.txt b/tests/MMS/bracket/CMakeLists.txt index fc8103aab0..e4cb7da9f9 100644 --- a/tests/MMS/bracket/CMakeLists.txt +++ b/tests/MMS/bracket/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-bracket - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-bracket + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/MMS/derivatives3/CMakeLists.txt b/tests/MMS/derivatives3/CMakeLists.txt index 5615a46a38..476934ba00 100644 --- a/tests/MMS/derivatives3/CMakeLists.txt +++ b/tests/MMS/derivatives3/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-derivatives3 - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-derivatives3 + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/MMS/diffusion/CMakeLists.txt b/tests/MMS/diffusion/CMakeLists.txt index b91b6a8327..358fdce403 100644 --- a/tests/MMS/diffusion/CMakeLists.txt +++ b/tests/MMS/diffusion/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-diffusion +bout_add_mms_test( + MMS-diffusion SOURCES diffusion.cxx EXECUTABLE_NAME cyto - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/MMS/diffusion2/CMakeLists.txt b/tests/MMS/diffusion2/CMakeLists.txt index 910be46481..0d2e0a2903 100644 --- a/tests/MMS/diffusion2/CMakeLists.txt +++ b/tests/MMS/diffusion2/CMakeLists.txt @@ -1,11 +1,9 @@ -bout_add_mms_test(MMS-diffusion2 +bout_add_mms_test( + MMS-diffusion2 SOURCES diffusion.cxx EXECUTABLE_NAME cyto USE_RUNTEST - EXTRA_FILES - X/BOUT.inp - Y/BOUT.inp - Z/BOUT.inp + EXTRA_FILES X/BOUT.inp Y/BOUT.inp Z/BOUT.inp REQUIRES BOUT_RUN_ALL_TESTS PROCESSORS 2 - ) +) diff --git a/tests/MMS/hw/CMakeLists.txt b/tests/MMS/hw/CMakeLists.txt index 6fda0448be..e7449dfff3 100644 --- a/tests/MMS/hw/CMakeLists.txt +++ b/tests/MMS/hw/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_mms_test(MMS-hw +bout_add_mms_test( + MMS-hw SOURCES hw.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_RUN_ALL_TESTS PROCESSORS 4 ) diff --git a/tests/MMS/laplace/CMakeLists.txt b/tests/MMS/laplace/CMakeLists.txt index 4825fc7fd6..4c2e9fee1f 100644 --- a/tests/MMS/laplace/CMakeLists.txt +++ b/tests/MMS/laplace/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_mms_test(MMS-laplace +bout_add_mms_test( + MMS-laplace SOURCES laplace.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_RUN_ALL_TESTS PROCESSORS 2 -) +) diff --git a/tests/MMS/shiftedmetricinterp/CMakeLists.txt b/tests/MMS/shiftedmetricinterp/CMakeLists.txt index 1693428da6..1e61b9e25d 100644 --- a/tests/MMS/shiftedmetricinterp/CMakeLists.txt +++ b/tests/MMS/shiftedmetricinterp/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-shiftedmetricinterp - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-shiftedmetricinterp + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/MMS/spatial/advection/CMakeLists.txt b/tests/MMS/spatial/advection/CMakeLists.txt index 4adb108612..bdc1c2f615 100644 --- a/tests/MMS/spatial/advection/CMakeLists.txt +++ b/tests/MMS/spatial/advection/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_mms_test(MMS-spatial-advection +bout_add_mms_test( + MMS-spatial-advection SOURCES advection.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_RUN_ALL_TESTS PROCESSORS 4 ) diff --git a/tests/MMS/spatial/d2dx2/CMakeLists.txt b/tests/MMS/spatial/d2dx2/CMakeLists.txt index 0affa9beee..2b6ede3e4a 100644 --- a/tests/MMS/spatial/d2dx2/CMakeLists.txt +++ b/tests/MMS/spatial/d2dx2/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-spatial-d2dx2 +bout_add_mms_test( + MMS-spatial-d2dx2 SOURCES test_d2dx2.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_RUN_ALL_TESTS ) diff --git a/tests/MMS/spatial/d2dz2/CMakeLists.txt b/tests/MMS/spatial/d2dz2/CMakeLists.txt index 01b61eaa5d..d60e4028fc 100644 --- a/tests/MMS/spatial/d2dz2/CMakeLists.txt +++ b/tests/MMS/spatial/d2dz2/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-spatial-d2dz2 +bout_add_mms_test( + MMS-spatial-d2dz2 SOURCES test_d2dz2.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_RUN_ALL_TESTS ) diff --git a/tests/MMS/spatial/diffusion/CMakeLists.txt b/tests/MMS/spatial/diffusion/CMakeLists.txt index 67a27aafa4..0e7b99ab84 100644 --- a/tests/MMS/spatial/diffusion/CMakeLists.txt +++ b/tests/MMS/spatial/diffusion/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_mms_test(MMS-spatial-diffusion +bout_add_mms_test( + MMS-spatial-diffusion SOURCES diffusion.cxx USE_RUNTEST EXTRA_FILES X/BOUT.inp diff --git a/tests/MMS/spatial/fci/CMakeLists.txt b/tests/MMS/spatial/fci/CMakeLists.txt index 94b9682c9c..dcdb183fdd 100644 --- a/tests/MMS/spatial/fci/CMakeLists.txt +++ b/tests/MMS/spatial/fci/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_mms_test(MMS-spatial-fci +bout_add_mms_test( + MMS-spatial-fci SOURCES fci_mms.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES zoidberg_FOUND PROCESSORS 2 ) diff --git a/tests/MMS/time-petsc/CMakeLists.txt b/tests/MMS/time-petsc/CMakeLists.txt index 3cbbe6f717..554602ccfe 100644 --- a/tests/MMS/time-petsc/CMakeLists.txt +++ b/tests/MMS/time-petsc/CMakeLists.txt @@ -1,7 +1,6 @@ -bout_add_mms_test(MMS-time-petsc +bout_add_mms_test( + MMS-time-petsc USE_RUNTEST - REQUIRES - BOUT_HAS_PETSC - BOUT_RUN_ALL_TESTS + REQUIRES BOUT_HAS_PETSC BOUT_RUN_ALL_TESTS EXTRA_DEPENDS MMS-time ) diff --git a/tests/MMS/time/CMakeLists.txt b/tests/MMS/time/CMakeLists.txt index 9c3480629e..5924356122 100644 --- a/tests/MMS/time/CMakeLists.txt +++ b/tests/MMS/time/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-time +bout_add_mms_test( + MMS-time SOURCES time.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_RUN_ALL_TESTS ) diff --git a/tests/MMS/upwinding3/CMakeLists.txt b/tests/MMS/upwinding3/CMakeLists.txt index 5daf58b29b..a1664c03e9 100644 --- a/tests/MMS/upwinding3/CMakeLists.txt +++ b/tests/MMS/upwinding3/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_mms_test(MMS-upwinding3 - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_mms_test( + MMS-upwinding3 + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/MMS/wave-1d-y/CMakeLists.txt b/tests/MMS/wave-1d-y/CMakeLists.txt index cc5cddfff4..ce7b1b7c0e 100644 --- a/tests/MMS/wave-1d-y/CMakeLists.txt +++ b/tests/MMS/wave-1d-y/CMakeLists.txt @@ -1,5 +1,5 @@ -bout_add_mms_test(MMS-wave-1d-y +bout_add_mms_test( + MMS-wave-1d-y SOURCES wave.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/MMS/wave-1d/CMakeLists.txt b/tests/MMS/wave-1d/CMakeLists.txt index a9ae3d748c..568cf11beb 100644 --- a/tests/MMS/wave-1d/CMakeLists.txt +++ b/tests/MMS/wave-1d/CMakeLists.txt @@ -1,5 +1,5 @@ -bout_add_mms_test(MMS-wave-1d +bout_add_mms_test( + MMS-wave-1d SOURCES wave.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP ) diff --git a/tests/integrated/test-backtrace/CMakeLists.txt b/tests/integrated/test-backtrace/CMakeLists.txt index 579f4863ac..420558bc04 100644 --- a/tests/integrated/test-backtrace/CMakeLists.txt +++ b/tests/integrated/test-backtrace/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_integrated_test(test-backtrace +bout_add_integrated_test( + test-backtrace SOURCES boutexcept.cxx USE_RUNTEST ) diff --git a/tests/integrated/test-beuler/CMakeLists.txt b/tests/integrated/test-beuler/CMakeLists.txt index c67b2dce44..495cd670d0 100644 --- a/tests/integrated/test-beuler/CMakeLists.txt +++ b/tests/integrated/test-beuler/CMakeLists.txt @@ -1,2 +1,5 @@ -bout_add_integrated_test(test_beuler SOURCES test_beuler.cxx - REQUIRES BOUT_HAS_PETSC) +bout_add_integrated_test( + test_beuler + SOURCES test_beuler.cxx + REQUIRES BOUT_HAS_PETSC +) diff --git a/tests/integrated/test-bout-override-default-option/CMakeLists.txt b/tests/integrated/test-bout-override-default-option/CMakeLists.txt index 7c3d6390b0..0c8b643e98 100644 --- a/tests/integrated/test-bout-override-default-option/CMakeLists.txt +++ b/tests/integrated/test-bout-override-default-option/CMakeLists.txt @@ -1,3 +1,4 @@ -bout_add_integrated_test(test-bout-override-default-option +bout_add_integrated_test( + test-bout-override-default-option SOURCES test-bout-override-default-option.cxx - ) +) diff --git a/tests/integrated/test-boutpp/CMakeLists.txt b/tests/integrated/test-boutpp/CMakeLists.txt index 0a16bba43e..8aa0d55869 100644 --- a/tests/integrated/test-boutpp/CMakeLists.txt +++ b/tests/integrated/test-boutpp/CMakeLists.txt @@ -1,4 +1,4 @@ -if (BOUT_ENABLE_PYTHON) +if(BOUT_ENABLE_PYTHON) add_subdirectory(collect) add_subdirectory(collect-staggered) add_subdirectory(legacy-model) diff --git a/tests/integrated/test-boutpp/collect-staggered/CMakeLists.txt b/tests/integrated/test-boutpp/collect-staggered/CMakeLists.txt index 7a41adc15d..c893384389 100644 --- a/tests/integrated/test-boutpp/collect-staggered/CMakeLists.txt +++ b/tests/integrated/test-boutpp/collect-staggered/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-boutpp-collect-staggered - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_integrated_test( + test-boutpp-collect-staggered + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/integrated/test-boutpp/collect/CMakeLists.txt b/tests/integrated/test-boutpp/collect/CMakeLists.txt index beb78086c6..5a8e7579f1 100644 --- a/tests/integrated/test-boutpp/collect/CMakeLists.txt +++ b/tests/integrated/test-boutpp/collect/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_integrated_test(test-boutpp-collect +bout_add_integrated_test( + test-boutpp-collect USE_RUNTEST EXTRA_FILES input/BOUT.inp REQUIRES BOUT_ENABLE_PYTHON diff --git a/tests/integrated/test-boutpp/legacy-model/CMakeLists.txt b/tests/integrated/test-boutpp/legacy-model/CMakeLists.txt index a32b1f4f4b..eddeae92fc 100644 --- a/tests/integrated/test-boutpp/legacy-model/CMakeLists.txt +++ b/tests/integrated/test-boutpp/legacy-model/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-boutpp-legacy-model - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_integrated_test( + test-boutpp-legacy-model + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/integrated/test-boutpp/mms-ddz/CMakeLists.txt b/tests/integrated/test-boutpp/mms-ddz/CMakeLists.txt index dfac88662a..3cfafb6a6a 100644 --- a/tests/integrated/test-boutpp/mms-ddz/CMakeLists.txt +++ b/tests/integrated/test-boutpp/mms-ddz/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-boutpp-mms-ddz - USE_RUNTEST - USE_DATA_BOUT_INP +bout_add_integrated_test( + test-boutpp-mms-ddz + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_ENABLE_PYTHON ) diff --git a/tests/integrated/test-boutpp/print/CMakeLists.txt b/tests/integrated/test-boutpp/print/CMakeLists.txt index 2e61b29257..84b005df89 100644 --- a/tests/integrated/test-boutpp/print/CMakeLists.txt +++ b/tests/integrated/test-boutpp/print/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_integrated_test(test-boutpp-print +bout_add_integrated_test( + test-boutpp-print USE_RUNTEST EXTRA_FILES test/BOUT.inp test.py REQUIRES BOUT_ENABLE_PYTHON diff --git a/tests/integrated/test-boutpp/simple-model/CMakeLists.txt b/tests/integrated/test-boutpp/simple-model/CMakeLists.txt index 891a2ce018..a17bb6291a 100644 --- a/tests/integrated/test-boutpp/simple-model/CMakeLists.txt +++ b/tests/integrated/test-boutpp/simple-model/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_integrated_test(test-boutpp-simple-model +bout_add_integrated_test( + test-boutpp-simple-model USE_RUNTEST EXTRA_FILES mini/BOUT.inp REQUIRES BOUT_ENABLE_PYTHON diff --git a/tests/integrated/test-boutpp/slicing/CMakeLists.txt b/tests/integrated/test-boutpp/slicing/CMakeLists.txt index 4f314a40cc..57f74aa125 100644 --- a/tests/integrated/test-boutpp/slicing/CMakeLists.txt +++ b/tests/integrated/test-boutpp/slicing/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_integrated_test(test-boutpp-slicing +bout_add_integrated_test( + test-boutpp-slicing USE_RUNTEST EXTRA_FILES test/BOUT.inp test.py REQUIRES BOUT_ENABLE_PYTHON diff --git a/tests/integrated/test-collect/CMakeLists.txt b/tests/integrated/test-collect/CMakeLists.txt index e5fa43dde8..a3487d6443 100644 --- a/tests/integrated/test-collect/CMakeLists.txt +++ b/tests/integrated/test-collect/CMakeLists.txt @@ -1,5 +1,5 @@ -bout_add_integrated_test(test-collect +bout_add_integrated_test( + test-collect SOURCES test-collect.cxx - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/integrated/test-command-args/CMakeLists.txt b/tests/integrated/test-command-args/CMakeLists.txt index 1a2cf88a5e..331994ac11 100644 --- a/tests/integrated/test-command-args/CMakeLists.txt +++ b/tests/integrated/test-command-args/CMakeLists.txt @@ -1,5 +1,6 @@ -bout_add_integrated_test(test-command-args +bout_add_integrated_test( + test-command-args SOURCES command-args.cxx USE_RUNTEST EXTRA_FILES BOUT.inp - ) +) diff --git a/tests/integrated/test-communications/CMakeLists.txt b/tests/integrated/test-communications/CMakeLists.txt index de6197ce3b..84c940d2f6 100644 --- a/tests/integrated/test-communications/CMakeLists.txt +++ b/tests/integrated/test-communications/CMakeLists.txt @@ -1,9 +1,8 @@ -bout_add_integrated_test(test-communications +bout_add_integrated_test( + test-communications SOURCES test-communications.cxx - USE_RUNTEST - USE_DATA_BOUT_INP - EXTRA_FILES - data_limiter/BOUT.inp + USE_RUNTEST USE_DATA_BOUT_INP + EXTRA_FILES data_limiter/BOUT.inp REQUIRES BOUT_HAS_NETCDF PROCESSORS 18 - ) +) diff --git a/tests/integrated/test-coordinates-initialization/CMakeLists.txt b/tests/integrated/test-coordinates-initialization/CMakeLists.txt index ad47be272c..f3f50ff17a 100644 --- a/tests/integrated/test-coordinates-initialization/CMakeLists.txt +++ b/tests/integrated/test-coordinates-initialization/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-coordinates-initialization +bout_add_integrated_test( + test-coordinates-initialization SOURCES test-coordinates-initialization.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 3 - ) +) diff --git a/tests/integrated/test-cyclic/CMakeLists.txt b/tests/integrated/test-cyclic/CMakeLists.txt index a6296858e2..0a98e29450 100644 --- a/tests/integrated/test-cyclic/CMakeLists.txt +++ b/tests/integrated/test-cyclic/CMakeLists.txt @@ -1,8 +1,8 @@ -bout_add_integrated_test(test-cyclic +bout_add_integrated_test( + test-cyclic SOURCES test_cyclic.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES test_io.grd.nc REQUIRES BOUT_HAS_NETCDF PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-datafilefacade/CMakeLists.txt b/tests/integrated/test-datafilefacade/CMakeLists.txt index a6d1876e16..3032ece263 100644 --- a/tests/integrated/test-datafilefacade/CMakeLists.txt +++ b/tests/integrated/test-datafilefacade/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-datafile-facade +bout_add_integrated_test( + test-datafile-facade SOURCES test-datafile-facade.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_NETCDF PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-delp2/CMakeLists.txt b/tests/integrated/test-delp2/CMakeLists.txt index c1de3ed940..bab68fbfaf 100644 --- a/tests/integrated/test-delp2/CMakeLists.txt +++ b/tests/integrated/test-delp2/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-delp2 +bout_add_integrated_test( + test-delp2 SOURCES test_delp2.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-drift-instability-staggered/CMakeLists.txt b/tests/integrated/test-drift-instability-staggered/CMakeLists.txt index 18383286cd..6bebf454a0 100644 --- a/tests/integrated/test-drift-instability-staggered/CMakeLists.txt +++ b/tests/integrated/test-drift-instability-staggered/CMakeLists.txt @@ -1,10 +1,10 @@ -bout_add_integrated_test(test-drift-instability-staggered +bout_add_integrated_test( + test-drift-instability-staggered SOURCES 2fluid.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES uedge.grd_std.cdl REQUIRES BOUT_HAS_NETCDF REQUIRES BOUT_HAS_FFTW PROCESSORS 2 - ) +) diff --git a/tests/integrated/test-drift-instability/CMakeLists.txt b/tests/integrated/test-drift-instability/CMakeLists.txt index b0b73eca01..55b08726f9 100644 --- a/tests/integrated/test-drift-instability/CMakeLists.txt +++ b/tests/integrated/test-drift-instability/CMakeLists.txt @@ -1,10 +1,10 @@ -bout_add_integrated_test(test-drift-instability +bout_add_integrated_test( + test-drift-instability SOURCES 2fluid.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES uedge.grd_std.cdl REQUIRES BOUT_HAS_NETCDF REQUIRES BOUT_HAS_FFTW PROCESSORS 2 - ) +) diff --git a/tests/integrated/test-fci-boundary/CMakeLists.txt b/tests/integrated/test-fci-boundary/CMakeLists.txt index bf25cd7c57..b9164ce000 100644 --- a/tests/integrated/test-fci-boundary/CMakeLists.txt +++ b/tests/integrated/test-fci-boundary/CMakeLists.txt @@ -1,22 +1,25 @@ -bout_add_mms_test(test-fci-boundary +bout_add_mms_test( + test-fci-boundary SOURCES get_par_bndry.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES zoidberg_FOUND PROCESSORS 1 - ) +) -if (zoidberg_FOUND) +if(zoidberg_FOUND) set(gridfile ${CMAKE_CURRENT_BINARY_DIR}/grid.fci.nc) - add_custom_command(OUTPUT ${gridfile} - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/grid.py ${gridfile} + add_custom_command( + OUTPUT ${gridfile} + COMMAND + ${CMAKE_COMMAND} -E env PYTHONPATH=${BOUT_PYTHONPATH}:$ENV{PYTHONPATH} + ${Python3_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/grid.py ${gridfile} WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/../../../tools/pylib/boutconfig/__init__.py + DEPENDS + ${CMAKE_CURRENT_BINARY_DIR}/../../../tools/pylib/boutconfig/__init__.py DEPENDS grid.py - IMPLICIT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR} + IMPLICIT_DEPENDS ${CMAKE_CURRENT_BINARY_DIR} COMMENT "Creating test-fci-boundary grid file" ) add_custom_target(test-fci-boundary-grid DEPENDS ${gridfile}) - add_dependencies(test-fci-boundary - test-fci-boundary-grid) + add_dependencies(test-fci-boundary test-fci-boundary-grid) endif() diff --git a/tests/integrated/test-fci-mpi/CMakeLists.txt b/tests/integrated/test-fci-mpi/CMakeLists.txt index aea0421c0a..0abbbce099 100644 --- a/tests/integrated/test-fci-mpi/CMakeLists.txt +++ b/tests/integrated/test-fci-mpi/CMakeLists.txt @@ -1,9 +1,10 @@ -bout_add_mms_test(test-fci-mpi +bout_add_mms_test( + test-fci-mpi SOURCES fci_mpi.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 6 - DOWNLOAD https://zenodo.org/records/7614499/files/W7X-conf4-36x8x128.fci.nc?download=1 + DOWNLOAD + https://zenodo.org/records/7614499/files/W7X-conf4-36x8x128.fci.nc?download=1 DOWNLOAD_NAME grid.fci.nc REQUIRES BOUT_HAS_PETSC ) diff --git a/tests/integrated/test-fieldgroupComm/CMakeLists.txt b/tests/integrated/test-fieldgroupComm/CMakeLists.txt index 17a699c187..d5d59eedf5 100644 --- a/tests/integrated/test-fieldgroupComm/CMakeLists.txt +++ b/tests/integrated/test-fieldgroupComm/CMakeLists.txt @@ -1,9 +1,9 @@ -bout_add_integrated_test(test-fieldgroupComm +bout_add_integrated_test( + test-fieldgroupComm SOURCES test_fieldgroupcomm.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES cyclone_68x32.nc REQUIRES BOUT_HAS_NETCDF PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-griddata-yboundary-guards/CMakeLists.txt b/tests/integrated/test-griddata-yboundary-guards/CMakeLists.txt index b01afa5f32..4766549cbb 100644 --- a/tests/integrated/test-griddata-yboundary-guards/CMakeLists.txt +++ b/tests/integrated/test-griddata-yboundary-guards/CMakeLists.txt @@ -1,13 +1,11 @@ -bout_add_integrated_test(test-griddata-yboundary-guards +bout_add_integrated_test( + test-griddata-yboundary-guards SOURCES test_griddata.cxx USE_RUNTEST EXTRA_FILES - data-doublenull-0/BOUT.inp - data-doublenull-1/BOUT.inp - data-doublenull-2/BOUT.inp - data-singlenull-0/BOUT.inp - data-singlenull-1/BOUT.inp - data-singlenull-2/BOUT.inp + data-doublenull-0/BOUT.inp data-doublenull-1/BOUT.inp + data-doublenull-2/BOUT.inp data-singlenull-0/BOUT.inp + data-singlenull-1/BOUT.inp data-singlenull-2/BOUT.inp REQUIRES BOUT_HAS_NETCDF PROCESSORS 6 - ) +) diff --git a/tests/integrated/test-griddata/CMakeLists.txt b/tests/integrated/test-griddata/CMakeLists.txt index 8e03b5de0d..3ea14a4d49 100644 --- a/tests/integrated/test-griddata/CMakeLists.txt +++ b/tests/integrated/test-griddata/CMakeLists.txt @@ -1,5 +1,6 @@ -bout_add_integrated_test(test-griddata +bout_add_integrated_test( + test-griddata SOURCES test_griddata.cxx USE_RUNTEST EXTRA_FILES screw/BOUT.inp - ) +) diff --git a/tests/integrated/test-gyro/CMakeLists.txt b/tests/integrated/test-gyro/CMakeLists.txt index 00be4d29a0..339625f048 100644 --- a/tests/integrated/test-gyro/CMakeLists.txt +++ b/tests/integrated/test-gyro/CMakeLists.txt @@ -1,10 +1,10 @@ -bout_add_integrated_test(test-gyro +bout_add_integrated_test( + test-gyro SOURCES test_gyro.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES cyclone_68x32.nc data/benchmark.0.nc REQUIRES BOUT_HAS_NETCDF REQUIRES BOUT_HAS_FFTW PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-initial/CMakeLists.txt b/tests/integrated/test-initial/CMakeLists.txt index 33a03d89ca..0c2ec14c1c 100644 --- a/tests/integrated/test-initial/CMakeLists.txt +++ b/tests/integrated/test-initial/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-initial +bout_add_integrated_test( + test-initial SOURCES test_initial.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-interchange-instability/CMakeLists.txt b/tests/integrated/test-interchange-instability/CMakeLists.txt index 380c93d029..a4c8108bbb 100644 --- a/tests/integrated/test-interchange-instability/CMakeLists.txt +++ b/tests/integrated/test-interchange-instability/CMakeLists.txt @@ -1,4 +1,5 @@ -bout_add_integrated_test(test-interchange-instability +bout_add_integrated_test( + test-interchange-instability SOURCES 2fluid.cxx CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST @@ -6,4 +7,4 @@ bout_add_integrated_test(test-interchange-instability REQUIRES BOUT_HAS_NETCDF REQUIRES BOUT_HAS_FFTW PROCESSORS 2 - ) +) diff --git a/tests/integrated/test-interpolate-z/CMakeLists.txt b/tests/integrated/test-interpolate-z/CMakeLists.txt index 399edcd8ff..7328807cd3 100644 --- a/tests/integrated/test-interpolate-z/CMakeLists.txt +++ b/tests/integrated/test-interpolate-z/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-interpolate-z +bout_add_integrated_test( + test-interpolate-z SOURCES test_interpolate.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW - ) +) diff --git a/tests/integrated/test-interpolate/CMakeLists.txt b/tests/integrated/test-interpolate/CMakeLists.txt index de4bd14f8a..3a12855a65 100644 --- a/tests/integrated/test-interpolate/CMakeLists.txt +++ b/tests/integrated/test-interpolate/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-interpolate +bout_add_integrated_test( + test-interpolate SOURCES test_interpolate.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW - ) +) diff --git a/tests/integrated/test-invertable-operator/CMakeLists.txt b/tests/integrated/test-invertable-operator/CMakeLists.txt index de45de14a2..eed109c450 100644 --- a/tests/integrated/test-invertable-operator/CMakeLists.txt +++ b/tests/integrated/test-invertable-operator/CMakeLists.txt @@ -1,8 +1,8 @@ -bout_add_integrated_test(test-invertable-operator +bout_add_integrated_test( + test-invertable-operator SOURCES invertable_operator.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_PETSC PROCESSORS 2 - ) +) diff --git a/tests/integrated/test-invpar/CMakeLists.txt b/tests/integrated/test-invpar/CMakeLists.txt index 8e143a1188..8df5b477ac 100644 --- a/tests/integrated/test-invpar/CMakeLists.txt +++ b/tests/integrated/test-invpar/CMakeLists.txt @@ -1,8 +1,8 @@ -bout_add_integrated_test(test-invpar +bout_add_integrated_test( + test-invpar SOURCES test_invpar.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-laplace-hypre3d/CMakeLists.txt b/tests/integrated/test-laplace-hypre3d/CMakeLists.txt index 2645c18c67..df68e9e5af 100644 --- a/tests/integrated/test-laplace-hypre3d/CMakeLists.txt +++ b/tests/integrated/test-laplace-hypre3d/CMakeLists.txt @@ -1,10 +1,8 @@ -bout_add_integrated_test(test-laplace-hypre3d +bout_add_integrated_test( + test-laplace-hypre3d SOURCES test-laplace3d.cxx - EXTRA_FILES - data_circular_core/BOUT.inp - data_circular_core-sol/BOUT.inp - data_slab_core/BOUT.inp - data_slab_sol/BOUT.inp + EXTRA_FILES data_circular_core/BOUT.inp data_circular_core-sol/BOUT.inp + data_slab_core/BOUT.inp data_slab_sol/BOUT.inp USE_RUNTEST REQUIRES BOUT_HAS_HYPRE - ) +) diff --git a/tests/integrated/test-laplace-petsc3d/CMakeLists.txt b/tests/integrated/test-laplace-petsc3d/CMakeLists.txt index 93bf4f7efa..0f0e4a2cac 100644 --- a/tests/integrated/test-laplace-petsc3d/CMakeLists.txt +++ b/tests/integrated/test-laplace-petsc3d/CMakeLists.txt @@ -1,10 +1,8 @@ -bout_add_integrated_test(test-laplace-petsc3d +bout_add_integrated_test( + test-laplace-petsc3d SOURCES test-laplace3d.cxx - EXTRA_FILES - data_circular_core/BOUT.inp - data_circular_core-sol/BOUT.inp - data_slab_core/BOUT.inp - data_slab_sol/BOUT.inp + EXTRA_FILES data_circular_core/BOUT.inp data_circular_core-sol/BOUT.inp + data_slab_core/BOUT.inp data_slab_sol/BOUT.inp USE_RUNTEST REQUIRES BOUT_HAS_PETSC - ) +) diff --git a/tests/integrated/test-laplace/CMakeLists.txt b/tests/integrated/test-laplace/CMakeLists.txt index f3e95f3fa5..455e9cf769 100644 --- a/tests/integrated/test-laplace/CMakeLists.txt +++ b/tests/integrated/test-laplace/CMakeLists.txt @@ -1,10 +1,10 @@ -bout_add_integrated_test(test-laplace +bout_add_integrated_test( + test-laplace SOURCES test_laplace.cxx CONFLICTS BOUT_USE_METRIC_3D EXTRA_FILES test_laplace.grd.nc data/benchmark.0.nc - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_NETCDF REQUIRES BOUT_HAS_FFTW PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-laplacexy-fv/CMakeLists.txt b/tests/integrated/test-laplacexy-fv/CMakeLists.txt index 57e98edd38..14887b6ff6 100644 --- a/tests/integrated/test-laplacexy-fv/CMakeLists.txt +++ b/tests/integrated/test-laplacexy-fv/CMakeLists.txt @@ -1,8 +1,9 @@ -bout_add_integrated_test(test-laplacexy-fv +bout_add_integrated_test( + test-laplacexy-fv SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_PETSC - CONFLICTS BOUT_USE_METRIC_3D # Test requires Laplace_perpXY operator, which is not implemented for 3d metrics - USE_RUNTEST - USE_DATA_BOUT_INP + CONFLICTS + BOUT_USE_METRIC_3D # Test requires Laplace_perpXY operator, which is not implemented for 3d metrics + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 8 - ) +) diff --git a/tests/integrated/test-laplacexy-short/CMakeLists.txt b/tests/integrated/test-laplacexy-short/CMakeLists.txt index c5576b58ac..58b8e49962 100644 --- a/tests/integrated/test-laplacexy-short/CMakeLists.txt +++ b/tests/integrated/test-laplacexy-short/CMakeLists.txt @@ -1,8 +1,9 @@ -bout_add_integrated_test(test-laplacexy-short +bout_add_integrated_test( + test-laplacexy-short SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_PETSC - CONFLICTS BOUT_USE_METRIC_3D # Test uses cyclic Laplace solver as a preconditioner, which is not available with 3d metrics - USE_RUNTEST - USE_DATA_BOUT_INP + CONFLICTS + BOUT_USE_METRIC_3D # Test uses cyclic Laplace solver as a preconditioner, which is not available with 3d metrics + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 8 - ) +) diff --git a/tests/integrated/test-laplacexy/CMakeLists.txt b/tests/integrated/test-laplacexy/CMakeLists.txt index 338bb46763..43c75b1cd8 100644 --- a/tests/integrated/test-laplacexy/CMakeLists.txt +++ b/tests/integrated/test-laplacexy/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-laplacexy +bout_add_integrated_test( + test-laplacexy SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_PETSC - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 8 - ) +) diff --git a/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt b/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt index a787752ebc..e56829b243 100644 --- a/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt +++ b/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-laplacexy2-hypre +bout_add_integrated_test( + test-laplacexy2-hypre SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_HYPRE - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/integrated/test-laplacexz/CMakeLists.txt b/tests/integrated/test-laplacexz/CMakeLists.txt index b19aa986ad..e0569d5ea6 100644 --- a/tests/integrated/test-laplacexz/CMakeLists.txt +++ b/tests/integrated/test-laplacexz/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-laplacexz +bout_add_integrated_test( + test-laplacexz SOURCES test-laplacexz.cxx REQUIRES BOUT_HAS_PETSC CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/integrated/test-multigrid_laplace/CMakeLists.txt b/tests/integrated/test-multigrid_laplace/CMakeLists.txt index 67cf38b510..b6bb16b8a8 100644 --- a/tests/integrated/test-multigrid_laplace/CMakeLists.txt +++ b/tests/integrated/test-multigrid_laplace/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-multigrid-laplace +bout_add_integrated_test( + test-multigrid-laplace SOURCES test_multigrid_laplace.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 3 - ) +) diff --git a/tests/integrated/test-naulin-laplace/CMakeLists.txt b/tests/integrated/test-naulin-laplace/CMakeLists.txt index 9e63477a53..8ec20b3875 100644 --- a/tests/integrated/test-naulin-laplace/CMakeLists.txt +++ b/tests/integrated/test-naulin-laplace/CMakeLists.txt @@ -1,8 +1,8 @@ -bout_add_integrated_test(test-naulin-laplace +bout_add_integrated_test( + test-naulin-laplace SOURCES test_naulin_laplace.cxx CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW PROCESSORS 3 - ) +) diff --git a/tests/integrated/test-options-netcdf/CMakeLists.txt b/tests/integrated/test-options-netcdf/CMakeLists.txt index f2d115d768..e956e9a16f 100644 --- a/tests/integrated/test-options-netcdf/CMakeLists.txt +++ b/tests/integrated/test-options-netcdf/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-options-netcdf +bout_add_integrated_test( + test-options-netcdf SOURCES test-options-netcdf.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_NETCDF CONFLICTS BOUT_HAS_LEGACY_NETCDF - ) +) diff --git a/tests/integrated/test-petsc_laplace/CMakeLists.txt b/tests/integrated/test-petsc_laplace/CMakeLists.txt index 9492b9f34f..1d2e7896f0 100644 --- a/tests/integrated/test-petsc_laplace/CMakeLists.txt +++ b/tests/integrated/test-petsc_laplace/CMakeLists.txt @@ -1,8 +1,9 @@ -bout_add_integrated_test(test-petsc-laplace +bout_add_integrated_test( + test-petsc-laplace SOURCES test_petsc_laplace.cxx REQUIRES BOUT_HAS_PETSC - CONFLICTS BOUT_USE_METRIC_3D # default preconditioner uses 'cyclic' Laplace solver which is not available with 3d metrics - USE_RUNTEST - USE_DATA_BOUT_INP + CONFLICTS + BOUT_USE_METRIC_3D # default preconditioner uses 'cyclic' Laplace solver which is not available with 3d metrics + USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt b/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt index 53e4acbedb..34042223a0 100644 --- a/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt +++ b/tests/integrated/test-petsc_laplace_MAST-grid/CMakeLists.txt @@ -1,14 +1,12 @@ -bout_add_integrated_test(test-petsc-laplace-MAST-grid +bout_add_integrated_test( + test-petsc-laplace-MAST-grid SOURCES test_petsc_laplace_MAST_grid.cxx REQUIRES BOUT_HAS_PETSC CONFLICTS BOUT_USE_METRIC_3D - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES - grids/grid_MAST_SOL_jyis2.nc - grids/grid_MAST_SOL_jyis34.nc - grids/grid_MAST_SOL_jyis65.nc - grids/grid_MAST_SOL_jyis81.nc + grids/grid_MAST_SOL_jyis2.nc grids/grid_MAST_SOL_jyis34.nc + grids/grid_MAST_SOL_jyis65.nc grids/grid_MAST_SOL_jyis81.nc grids/grid_MAST_SOL_jyis113.nc PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-restart-io/CMakeLists.txt b/tests/integrated/test-restart-io/CMakeLists.txt index c3de2ad0e1..0a98019248 100644 --- a/tests/integrated/test-restart-io/CMakeLists.txt +++ b/tests/integrated/test-restart-io/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-restart-io +bout_add_integrated_test( + test-restart-io SOURCES test-restart-io.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_NETCDF PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-restarting/CMakeLists.txt b/tests/integrated/test-restarting/CMakeLists.txt index 83cb9c808f..8174743459 100644 --- a/tests/integrated/test-restarting/CMakeLists.txt +++ b/tests/integrated/test-restarting/CMakeLists.txt @@ -1,5 +1,5 @@ -bout_add_integrated_test(test-restarting +bout_add_integrated_test( + test-restarting SOURCES test_restarting.cxx - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/integrated/test-slepc-solver/CMakeLists.txt b/tests/integrated/test-slepc-solver/CMakeLists.txt index bc4c178de9..cdfc7018c6 100644 --- a/tests/integrated/test-slepc-solver/CMakeLists.txt +++ b/tests/integrated/test-slepc-solver/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-slepc-solver +bout_add_integrated_test( + test-slepc-solver SOURCES test-slepc-solver.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_SLEPC - ) +) diff --git a/tests/integrated/test-smooth/CMakeLists.txt b/tests/integrated/test-smooth/CMakeLists.txt index 6b6f0c2001..728ea8e3b4 100644 --- a/tests/integrated/test-smooth/CMakeLists.txt +++ b/tests/integrated/test-smooth/CMakeLists.txt @@ -1,8 +1,8 @@ -bout_add_integrated_test(test-smooth +bout_add_integrated_test( + test-smooth SOURCES test_smooth.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP EXTRA_FILES test_smooth.nc data/benchmark.0.nc REQUIRES BOUT_HAS_NETCDF PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-snb/CMakeLists.txt b/tests/integrated/test-snb/CMakeLists.txt index 1a81c392a4..5308b63e53 100644 --- a/tests/integrated/test-snb/CMakeLists.txt +++ b/tests/integrated/test-snb/CMakeLists.txt @@ -1,6 +1,7 @@ -bout_add_integrated_test(test-snb +bout_add_integrated_test( + test-snb SOURCES test_snb.cxx CONFLICTS BOUT_USE_METRIC_3D USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW - ) +) diff --git a/tests/integrated/test-solver/CMakeLists.txt b/tests/integrated/test-solver/CMakeLists.txt index f20e11f1eb..de3c602621 100644 --- a/tests/integrated/test-solver/CMakeLists.txt +++ b/tests/integrated/test-solver/CMakeLists.txt @@ -1,3 +1 @@ -bout_add_integrated_test(test-solver - SOURCES test_solver.cxx - ) +bout_add_integrated_test(test-solver SOURCES test_solver.cxx) diff --git a/tests/integrated/test-squash/CMakeLists.txt b/tests/integrated/test-squash/CMakeLists.txt index 2c7e8d734d..f422331831 100644 --- a/tests/integrated/test-squash/CMakeLists.txt +++ b/tests/integrated/test-squash/CMakeLists.txt @@ -1,8 +1,8 @@ -bout_add_integrated_test(test-squash +bout_add_integrated_test( + test-squash SOURCES squash.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_NETCDF TESTARGS "${CMAKE_CURRENT_LIST_DIR}/../../../bin" PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-stopCheck-file/CMakeLists.txt b/tests/integrated/test-stopCheck-file/CMakeLists.txt index 60b9b23489..914142d85a 100644 --- a/tests/integrated/test-stopCheck-file/CMakeLists.txt +++ b/tests/integrated/test-stopCheck-file/CMakeLists.txt @@ -1,9 +1,6 @@ -bout_add_integrated_test(test-stopCheck-file +bout_add_integrated_test( + test-stopCheck-file SOURCES test_stopCheck.cxx - USE_RUNTEST - USE_DATA_BOUT_INP - EXTRA_FILES - data/BOUT.stop - dataSecond/BOUT.inp - dataSecond/otherStop.check - ) + USE_RUNTEST USE_DATA_BOUT_INP + EXTRA_FILES data/BOUT.stop dataSecond/BOUT.inp dataSecond/otherStop.check +) diff --git a/tests/integrated/test-stopCheck/CMakeLists.txt b/tests/integrated/test-stopCheck/CMakeLists.txt index 93cf5fb67b..09f4063818 100644 --- a/tests/integrated/test-stopCheck/CMakeLists.txt +++ b/tests/integrated/test-stopCheck/CMakeLists.txt @@ -1,5 +1,5 @@ -bout_add_integrated_test(test-stopCheck +bout_add_integrated_test( + test-stopCheck SOURCES test_stopCheck.cxx - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/integrated/test-twistshift-staggered/CMakeLists.txt b/tests/integrated/test-twistshift-staggered/CMakeLists.txt index 747e64dda1..7f741c5173 100644 --- a/tests/integrated/test-twistshift-staggered/CMakeLists.txt +++ b/tests/integrated/test-twistshift-staggered/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-twistshift-staggered +bout_add_integrated_test( + test-twistshift-staggered SOURCES test-twistshift.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW - ) +) diff --git a/tests/integrated/test-twistshift/CMakeLists.txt b/tests/integrated/test-twistshift/CMakeLists.txt index e8712e9844..9e876f0b12 100644 --- a/tests/integrated/test-twistshift/CMakeLists.txt +++ b/tests/integrated/test-twistshift/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-twistshift +bout_add_integrated_test( + test-twistshift SOURCES test-twistshift.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW - ) +) diff --git a/tests/integrated/test-vec/CMakeLists.txt b/tests/integrated/test-vec/CMakeLists.txt index 111840c71a..921650d06a 100644 --- a/tests/integrated/test-vec/CMakeLists.txt +++ b/tests/integrated/test-vec/CMakeLists.txt @@ -1,7 +1,7 @@ -bout_add_integrated_test(test-vec +bout_add_integrated_test( + test-vec SOURCES testVec.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW PROCESSORS 4 - ) +) diff --git a/tests/integrated/test-yupdown-weights/CMakeLists.txt b/tests/integrated/test-yupdown-weights/CMakeLists.txt index 5b6d825c87..eb14dcb074 100644 --- a/tests/integrated/test-yupdown-weights/CMakeLists.txt +++ b/tests/integrated/test-yupdown-weights/CMakeLists.txt @@ -1,5 +1,5 @@ -bout_add_integrated_test(test-yupdown-weights +bout_add_integrated_test( + test-yupdown-weights SOURCES test_yupdown_weights.cxx - USE_RUNTEST - USE_DATA_BOUT_INP - ) + USE_RUNTEST USE_DATA_BOUT_INP +) diff --git a/tests/integrated/test-yupdown/CMakeLists.txt b/tests/integrated/test-yupdown/CMakeLists.txt index 3c6d500b4e..0e0636e9b2 100644 --- a/tests/integrated/test-yupdown/CMakeLists.txt +++ b/tests/integrated/test-yupdown/CMakeLists.txt @@ -1,6 +1,6 @@ -bout_add_integrated_test(test-yupdown +bout_add_integrated_test( + test-yupdown SOURCES test_yupdown.cxx - USE_RUNTEST - USE_DATA_BOUT_INP + USE_RUNTEST USE_DATA_BOUT_INP REQUIRES BOUT_HAS_FFTW - ) +) diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 40aa207dea..7cf0bb0af7 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -1,122 +1,148 @@ bout_update_submodules() -if (NOT EXISTS "${PROJECT_SOURCE_DIR}/externalpackages/googletest/CMakeLists.txt") - set(BOUT_ENABLE_UNIT_TESTS OFF PARENT_SCOPE) - message(WARNING "googletest not found! Have you disabled the git submodules (GIT_SUBMODULE)?") +if(NOT EXISTS + "${PROJECT_SOURCE_DIR}/externalpackages/googletest/CMakeLists.txt" +) + set(BOUT_ENABLE_UNIT_TESTS + OFF + PARENT_SCOPE + ) + message( + WARNING + "googletest not found! Have you disabled the git submodules (GIT_SUBMODULE)?" + ) return() endif() -set(BOUT_ENABLE_UNIT_TESTS ON PARENT_SCOPE) +set(BOUT_ENABLE_UNIT_TESTS + ON + PARENT_SCOPE +) # disable gtest pthreads for proper execution of Death tests (on some platforms) add_definitions(-DGTEST_HAS_PTHREAD=0) -set(gtest_disable_pthreads ON CACHE BOOL "" FORCE) +set(gtest_disable_pthreads + ON + CACHE BOOL "" FORCE +) -add_subdirectory("${PROJECT_SOURCE_DIR}/externalpackages/googletest" - "externalpackages/googletest") +add_subdirectory( + "${PROJECT_SOURCE_DIR}/externalpackages/googletest" + "externalpackages/googletest" +) if(NOT TARGET gtest) - message(FATAL_ERROR "googletest not found! Have you disabled the git submodules (GIT_SUBMODULE)?") + message( + FATAL_ERROR + "googletest not found! Have you disabled the git submodules (GIT_SUBMODULE)?" + ) endif() # Some unit tests require GMOCK, so make sure we build it set(BUILD_GMOCK ON) mark_as_advanced( - BUILD_GMOCK BUILD_GTEST BUILD_SHARED_LIBS - gmock_build_tests gtest_build_samples gtest_build_tests - gtest_disable_pthreads gtest_force_shared_crt gtest_hide_internal_symbols - ) + BUILD_GMOCK + BUILD_GTEST + BUILD_SHARED_LIBS + gmock_build_tests + gtest_build_samples + gtest_build_tests + gtest_disable_pthreads + gtest_force_shared_crt + gtest_hide_internal_symbols +) set(serial_tests_source - ./bout_test_main.cxx - ./field/test_field.cxx - ./field/test_field2d.cxx - ./field/test_field3d.cxx - ./field/test_field_factory.cxx - ./field/test_fieldgroup.cxx - ./field/test_fieldperp.cxx - ./field/test_initialprofiles.cxx - ./field/test_vector2d.cxx - ./field/test_vector3d.cxx - ./field/test_where.cxx - ./include/bout/test_array.cxx - ./include/bout/test_assert.cxx - ./include/bout/test_bout_enum_class.cxx - ./include/bout/test_deriv_store.cxx - ./include/bout/test_generic_factory.cxx - ./include/bout/test_macro_for_each.cxx - ./include/bout/test_monitor.cxx - ./include/bout/test_petsc_indexer.cxx - ./include/bout/test_petsc_matrix.cxx - ./include/bout/test_petsc_setters.cxx - ./include/bout/test_petsc_vector.cxx - ./include/bout/test_region.cxx - ./include/bout/test_single_index_ops.cxx - ./include/bout/test_stencil.cxx - ./include/bout/test_template_combinations.cxx - ./include/bout/test_traits.cxx - ./include/test_cyclic_reduction.cxx - ./include/test_derivs.cxx - ./include/test_mask.cxx - ./invert/test_fft.cxx - ./invert/laplace/test_laplace_petsc3damg.cxx - ./invert/laplace/test_laplace_cyclic.cxx - ./mesh/data/test_gridfromoptions.cxx - ./mesh/parallel/test_shiftedmetric.cxx - ./mesh/test_boundary_factory.cxx - ./mesh/test_boutmesh.cxx - ./mesh/test_coordinates.cxx - ./mesh/test_coordinates_accessor.cxx - ./mesh/test_interpolation.cxx - ./mesh/test_invert3x3.cxx - ./mesh/test_mesh.cxx - ./mesh/test_paralleltransform.cxx - ./solver/test_fakesolver.cxx - ./solver/test_fakesolver.hxx - ./solver/test_solver.cxx - ./solver/test_solverfactory.cxx - ./sys/test_boutexception.cxx - ./sys/test_expressionparser.cxx - ./sys/test_msg_stack.cxx - ./sys/test_options.cxx - ./sys/test_options_adios2.cxx - ./sys/test_options_fields.cxx - ./sys/test_options_netcdf.cxx - ./sys/test_optionsreader.cxx - ./sys/test_output.cxx - ./sys/test_range.cxx - ./sys/test_timer.cxx - ./sys/test_type_name.cxx - ./sys/test_utils.cxx - ./sys/test_variant.cxx - ./sys/test_raja.cxx - ./test_extras.cxx - ./test_extras.hxx - ./fake_mesh.hxx - ./fake_mesh_fixture.hxx - ./src/test_bout++.cxx) + ./bout_test_main.cxx + ./field/test_field.cxx + ./field/test_field2d.cxx + ./field/test_field3d.cxx + ./field/test_field_factory.cxx + ./field/test_fieldgroup.cxx + ./field/test_fieldperp.cxx + ./field/test_initialprofiles.cxx + ./field/test_vector2d.cxx + ./field/test_vector3d.cxx + ./field/test_where.cxx + ./include/bout/test_array.cxx + ./include/bout/test_assert.cxx + ./include/bout/test_bout_enum_class.cxx + ./include/bout/test_deriv_store.cxx + ./include/bout/test_generic_factory.cxx + ./include/bout/test_macro_for_each.cxx + ./include/bout/test_monitor.cxx + ./include/bout/test_petsc_indexer.cxx + ./include/bout/test_petsc_matrix.cxx + ./include/bout/test_petsc_setters.cxx + ./include/bout/test_petsc_vector.cxx + ./include/bout/test_region.cxx + ./include/bout/test_single_index_ops.cxx + ./include/bout/test_stencil.cxx + ./include/bout/test_template_combinations.cxx + ./include/bout/test_traits.cxx + ./include/test_cyclic_reduction.cxx + ./include/test_derivs.cxx + ./include/test_mask.cxx + ./invert/test_fft.cxx + ./invert/laplace/test_laplace_petsc3damg.cxx + ./invert/laplace/test_laplace_cyclic.cxx + ./mesh/data/test_gridfromoptions.cxx + ./mesh/parallel/test_shiftedmetric.cxx + ./mesh/test_boundary_factory.cxx + ./mesh/test_boutmesh.cxx + ./mesh/test_coordinates.cxx + ./mesh/test_coordinates_accessor.cxx + ./mesh/test_interpolation.cxx + ./mesh/test_invert3x3.cxx + ./mesh/test_mesh.cxx + ./mesh/test_paralleltransform.cxx + ./solver/test_fakesolver.cxx + ./solver/test_fakesolver.hxx + ./solver/test_solver.cxx + ./solver/test_solverfactory.cxx + ./sys/test_boutexception.cxx + ./sys/test_expressionparser.cxx + ./sys/test_msg_stack.cxx + ./sys/test_options.cxx + ./sys/test_options_adios2.cxx + ./sys/test_options_fields.cxx + ./sys/test_options_netcdf.cxx + ./sys/test_optionsreader.cxx + ./sys/test_output.cxx + ./sys/test_range.cxx + ./sys/test_timer.cxx + ./sys/test_type_name.cxx + ./sys/test_utils.cxx + ./sys/test_variant.cxx + ./sys/test_raja.cxx + ./test_extras.cxx + ./test_extras.hxx + ./fake_mesh.hxx + ./fake_mesh_fixture.hxx + ./src/test_bout++.cxx +) if(BOUT_HAS_HYPRE) - list(APPEND serial_tests_source ./include/bout/test_hypre_interface.cxx) - list(APPEND serial_tests_source ./invert/laplace/test_laplace_hypre3d.cxx) -endif () + list(APPEND serial_tests_source ./include/bout/test_hypre_interface.cxx) + list(APPEND serial_tests_source ./invert/laplace/test_laplace_hypre3d.cxx) +endif() add_executable(serial_tests ${serial_tests_source}) -target_include_directories(serial_tests PUBLIC bout++ - $ - $ - ${CMAKE_INSTALL_PREFIX}/include - ) +target_include_directories( + serial_tests + PUBLIC bout++ $ + $ + ${CMAKE_INSTALL_PREFIX}/include +) set_target_properties(serial_tests PROPERTIES LINKER_LANGUAGE CXX) target_link_libraries(serial_tests gtest gmock bout++::bout++) add_test(NAME serial_tests COMMAND serial_tests --gtest_brief=1) set_target_properties(serial_tests PROPERTIES FOLDER tests/unit) add_dependencies(build-check-unit-tests serial_tests) -if (BOUT_HAS_CUDA) - set_source_files_properties(${serial_tests_source} PROPERTIES LANGUAGE CUDA ) - set_target_properties(serial_tests PROPERTIES CUDA_STANDARD 14) -endif () - +if(BOUT_HAS_CUDA) + set_source_files_properties(${serial_tests_source} PROPERTIES LANGUAGE CUDA) + set_target_properties(serial_tests PROPERTIES CUDA_STANDARD 14) +endif() diff --git a/tools/pylib/_boutpp_build/CMakeLists.txt b/tools/pylib/_boutpp_build/CMakeLists.txt index 3be2a5d2aa..bf5ba35261 100644 --- a/tools/pylib/_boutpp_build/CMakeLists.txt +++ b/tools/pylib/_boutpp_build/CMakeLists.txt @@ -1,19 +1,21 @@ # Error if Python API was explicitly requested, otherwise just a # warning and don't build Python API macro(bout_python_maybe_error VAR NAME) - if (NOT ${VAR}) + if(NOT ${VAR}) set(_error_msg "${NAME} is required for the Python interface") - if (NOT "${BOUT_ENABLE_PYTHON}" STREQUAL "AUTO") + if(NOT "${BOUT_ENABLE_PYTHON}" STREQUAL "AUTO") message(FATAL_ERROR ${_error_msg}) else() message(WARNING ${_error_msg}) - set(BOUT_ENABLE_PYTHON OFF PARENT_SCOPE) + set(BOUT_ENABLE_PYTHON + OFF + PARENT_SCOPE + ) return() endif() endif() endmacro() - bout_python_maybe_error(BUILD_SHARED_LIBS "BOUT++ shared library") find_package(Numpy) @@ -25,35 +27,41 @@ bout_python_maybe_error(${Cython_FOUND} Cython) find_package(Bash) bout_python_maybe_error(${Bash_FOUND} Bash) -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import jinja2" - RESULT_VARIABLE jinja2_FOUND) -if (jinja2_FOUND EQUAL 0) +execute_process( + COMMAND ${Python3_EXECUTABLE} -c "import jinja2" RESULT_VARIABLE jinja2_FOUND +) +if(jinja2_FOUND EQUAL 0) # We have jinja2 - all good else() bout_python_maybe_error(OFF jinja2) endif() -execute_process(COMMAND ${Python3_EXECUTABLE} -c "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX')[:-3])" +execute_process( + COMMAND ${Python3_EXECUTABLE} -c + "import sysconfig; print(sysconfig.get_config_var('EXT_SUFFIX')[:-3])" RESULT_VARIABLE PYTHON_WORKING OUTPUT_VARIABLE PYTHON_EXT_SUFFIX OUTPUT_STRIP_TRAILING_WHITESPACE - ) -if (NOT ${PYTHON_WORKING} EQUAL 0) +) +if(NOT ${PYTHON_WORKING} EQUAL 0) set(MSG "Failed to get the extension name from python!") - if ("${BOUT_ENABLE_PYTHON}" STREQUAL "ON") + if("${BOUT_ENABLE_PYTHON}" STREQUAL "ON") message(FATAL_ERROR ${MSG}) else() message(WARNING ${MSG}) - set(BOUT_ENABLE_PYTHON OFF ) + set(BOUT_ENABLE_PYTHON OFF) endif() endif() # No errors? We can build the interface! -if ("${BOUT_ENABLE_PYTHON}" STREQUAL "AUTO") - set(BOUT_ENABLE_PYTHON ON PARENT_SCOPE) +if("${BOUT_ENABLE_PYTHON}" STREQUAL "AUTO") + set(BOUT_ENABLE_PYTHON + ON + PARENT_SCOPE + ) endif() -if (NOT BOUT_ENABLE_PYTHON) +if(NOT BOUT_ENABLE_PYTHON) message(WARNING "Python interface will not be built, see warnings above") return() endif() @@ -71,54 +79,70 @@ foreach(file IN LISTS files) set(gen ${tar}/${file}) list(APPEND generated ${gen}) #message(FATAL_ERROR "${gen} ${src}/${file}.jinja") - add_custom_command(OUTPUT ${gen} - COMMAND ${CMAKE_COMMAND} -E make_directory ${tar} - COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${tar}/..:\${PYTHONPATH} ${Python3_EXECUTABLE} generate.py ${file}.jinja ${gen} - DEPENDS ${src}/${file}.jinja - DEPENDS ${src}/helper.py - DEPENDS ${src}/resolve_enum_inv.pyx.jinja - DEPENDS ${src}/generate.py - DEPENDS bout++ - WORKING_DIRECTORY ${src}/ - COMMENT "Generating ${file}") + add_custom_command( + OUTPUT ${gen} + COMMAND ${CMAKE_COMMAND} -E make_directory ${tar} + COMMAND ${CMAKE_COMMAND} -E env PYTHONPATH=${tar}/..:\${PYTHONPATH} + ${Python3_EXECUTABLE} generate.py ${file}.jinja ${gen} + DEPENDS ${src}/${file}.jinja + DEPENDS ${src}/helper.py + DEPENDS ${src}/resolve_enum_inv.pyx.jinja + DEPENDS ${src}/generate.py + DEPENDS bout++ + WORKING_DIRECTORY ${src}/ + COMMENT "Generating ${file}" + ) endforeach() set(boutpp_depends ${generated}) -set(files "boutexception_helper.hxx" "boutexception_helper.cxx" "boutpp_openmpi_compat.hxx" "bout_options.pxd" "setup.py") +set(files "boutexception_helper.hxx" "boutexception_helper.cxx" + "boutpp_openmpi_compat.hxx" "bout_options.pxd" "setup.py" +) foreach(file IN LISTS files) list(APPEND ${boutpp_depends} "${CMAKE_CURRENT_BINARY_DIR}/${file}") bout_copy_file("${file}") endforeach() -add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libboutpp.cpp +add_custom_command( + OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/libboutpp.cpp COMMAND ${CMAKE_COMMAND} -E copy boutpp.pyx libboutpp.pyx - COMMAND ${Python3_EXECUTABLE} -m cython libboutpp.pyx --cplus -3 -X binding=True -X embedsignature=True + COMMAND ${Python3_EXECUTABLE} -m cython libboutpp.pyx --cplus -3 -X + binding=True -X embedsignature=True WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} DEPENDS ${boutpp_depends} - ) +) -add_library(boutpp${PYTHON_EXT_SUFFIX} SHARED - ${tar}/libboutpp.cpp - ${tar}/helper.cxx - ${tar}/boutexception_helper.cxx - ) +add_library( + boutpp${PYTHON_EXT_SUFFIX} SHARED ${tar}/libboutpp.cpp ${tar}/helper.cxx + ${tar}/boutexception_helper.cxx +) -add_custom_target(boutpp ALL - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_BINARY_DIR}/libboutpp${PYTHON_EXT_SUFFIX}.so ${CMAKE_CURRENT_BINARY_DIR}/../boutpp/libboutpp${PYTHON_EXT_SUFFIX}.so - COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/boutpp.py ${CMAKE_CURRENT_BINARY_DIR}/../boutpp/__init__.py +add_custom_target( + boutpp ALL + COMMAND + ${CMAKE_COMMAND} -E copy + ${CMAKE_CURRENT_BINARY_DIR}/libboutpp${PYTHON_EXT_SUFFIX}.so + ${CMAKE_CURRENT_BINARY_DIR}/../boutpp/libboutpp${PYTHON_EXT_SUFFIX}.so + COMMAND ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/boutpp.py + ${CMAKE_CURRENT_BINARY_DIR}/../boutpp/__init__.py DEPENDS boutpp${PYTHON_EXT_SUFFIX} COMMENT "Building python interface" ) -install(TARGETS boutpp${PYTHON_EXT_SUFFIX} - DESTINATION ${CMAKE_INSTALL_PYTHON_SITEARCH}/boutpp/ +install(TARGETS boutpp${PYTHON_EXT_SUFFIX} + DESTINATION ${CMAKE_INSTALL_PYTHON_SITEARCH}/boutpp/ ) -install(FILES ${CMAKE_CURRENT_SOURCE_DIR}/boutpp.py +install( + FILES ${CMAKE_CURRENT_SOURCE_DIR}/boutpp.py DESTINATION ${CMAKE_INSTALL_PYTHON_SITEARCH}/boutpp/ RENAME __init__.py - ) +) target_link_libraries(boutpp${PYTHON_EXT_SUFFIX} bout++) -target_include_directories(boutpp${PYTHON_EXT_SUFFIX} PRIVATE $ ${Numpy_INCLUDE_DIRS} ${Python3_INCLUDE_DIRS}) +target_include_directories( + boutpp${PYTHON_EXT_SUFFIX} + PRIVATE $ ${Numpy_INCLUDE_DIRS} + ${Python3_INCLUDE_DIRS} +) From 523ee8a9e0cea2469e503cefa8ad1bf8f73cac8a Mon Sep 17 00:00:00 2001 From: Owen Parry Date: Fri, 20 Feb 2026 12:03:38 +0000 Subject: [PATCH 235/461] Ignore initial CMake formatting commit. --- .git-blame-ignore-revs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 93c768a148..010100588c 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -1,6 +1,8 @@ # Clang-format whole repo d8f14fdddb5ca0fbb32d8e2bf5ac2960d6ac5ce6 ed2117e6d6826a98b6988e2f18c0c34e408563b6 +# CMake formatting +17ac13c28aa3b34a0e46dbe87bb3874f6b25e706 # Added by the bot 4b010b7634aee1045743be80c268d4644522cd29 a71cad2dd6ace5741a754e2ca7daacd4bb094e0e From 7dbac074f0f6d0a7b2155faedef1f71259be2590 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 20 Feb 2026 15:44:19 +0000 Subject: [PATCH 236/461] Add some very basic FieldPerp Laplacian solver tests Also fix PCR and PCRThomas which were always using the global `nsys` array size, instead of the local array size --- src/invert/laplace/impls/pcr/pcr.cxx | 25 +++++--- .../laplace/impls/pcr_thomas/pcr_thomas.cxx | 20 ++++++- tests/integrated/test-laplace/runtest | 59 +++++++++++-------- .../integrated/test-laplace/test_laplace.cxx | 8 ++- 4 files changed, 77 insertions(+), 35 deletions(-) diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index a33bd7eef2..61e493d3ed 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -37,22 +37,21 @@ **************************************************************************/ #include "pcr.hxx" -#include "bout/globals.hxx" +#include +#include #include #include +#include #include +#include #include #include #include -#include -#include -#include - -#include "bout/boutcomm.hxx" #include - #include +#include +#include #include #include @@ -556,6 +555,8 @@ void LaplacePCR ::cr_pcr_solver(Matrix& a_mpi, Matrix& b_mpi const int xend = localmesh->xend; const int nx = xend - xstart + 1; // number of interior points + const int nsys = std::get<0>(a_mpi.shape()); + // Handle boundary points so that the PCR algorithm works with arrays of // the same size on each rank. // Note that this modifies the coefficients of b and r in the first and last @@ -622,6 +623,8 @@ void LaplacePCR ::cr_pcr_solver(Matrix& a_mpi, Matrix& b_mpi void LaplacePCR ::eliminate_boundary_rows(Matrix& a, Matrix& b, Matrix& c, Matrix& r) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { for (int kz = 0; kz < nsys; kz++) { // Do forward elimination on *all* boundary rows up to xstart @@ -657,6 +660,8 @@ void LaplacePCR ::apply_boundary_conditions(const Matrix& a, const Matrix& r, Matrix& x) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { for (int kz = 0; kz < nsys; kz++) { for (int ix = localmesh->xstart - 1; ix >= 0; ix--) { @@ -682,6 +687,8 @@ void LaplacePCR ::apply_boundary_conditions(const Matrix& a, void LaplacePCR ::cr_forward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); Array alpha(nsys); Array gamma(nsys); @@ -755,6 +762,8 @@ void LaplacePCR ::cr_forward_multiple_row(Matrix& a, Matrix& void LaplacePCR ::cr_backward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); MPI_Status status; @@ -806,6 +815,8 @@ void LaplacePCR ::pcr_forward_single_row(Matrix& a, Matrix& Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + Array alpha(nsys); Array gamma(nsys); Array sbuf(4 * nsys); diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index d471775990..604a630feb 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -37,9 +37,11 @@ **************************************************************************/ #include "pcr_thomas.hxx" -#include "bout/globals.hxx" +#include "bout/array.hxx" #include "bout/boutcomm.hxx" +#include "bout/dcomplex.hxx" +#include "bout/globals.hxx" #include #include #include @@ -556,6 +558,8 @@ void LaplacePCR_THOMAS ::pcr_thomas_solver(Matrix& a_mpi, const int xend = localmesh->xend; const int nx = xend - xstart + 1; // number of interior points + const int nsys = std::get<0>(a_mpi.shape()); + // Handle boundary points so that the PCR algorithm works with arrays of // the same size on each rank. // Note that this modifies the coefficients of b and r in the first and last @@ -622,6 +626,8 @@ void LaplacePCR_THOMAS ::eliminate_boundary_rows(const Matrix& a, const Matrix& c, Matrix& r) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { // x index is first interior row const int xstart = localmesh->xstart; @@ -656,6 +662,8 @@ void LaplacePCR_THOMAS ::apply_boundary_conditions(const Matrix& a, const Matrix& r, Matrix& x) { + const int nsys = std::get<0>(a.shape()); + if (localmesh->firstX()) { for (int kz = 0; kz < nsys; kz++) { for (int ix = localmesh->xstart - 1; ix >= 0; ix--) { @@ -681,6 +689,8 @@ void LaplacePCR_THOMAS ::apply_boundary_conditions(const Matrix& a, void LaplacePCR_THOMAS ::cr_forward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); Array alpha(nsys); Array gamma(nsys); @@ -756,6 +766,8 @@ void LaplacePCR_THOMAS ::cr_backward_multiple_row(Matrix& a, Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + MPI_Comm comm = BoutComm::get(); MPI_Status status; @@ -807,6 +819,8 @@ void LaplacePCR_THOMAS ::pcr_forward_single_row(Matrix& a, Matrix& c, Matrix& r, Matrix& x) const { + const int nsys = std::get<0>(a.shape()); + Array alpha(nsys); Array gamma(nsys); Array sbuf(4 * nsys); @@ -984,6 +998,8 @@ void LaplacePCR_THOMAS ::pThomas_forward_multiple_row(Matrix& a, Matrix& b, Matrix& c, Matrix& r) const { + const int nsys = std::get<0>(a.shape()); + for (int kz = 0; kz < nsys; kz++) { for (int i = 3; i <= n_mpi; i++) { const dcomplex alpha = -a(kz, i) / b(kz, i - 1); @@ -1015,6 +1031,8 @@ void LaplacePCR_THOMAS ::pcr_double_row_substitution(Matrix& a, Matrix& c, Matrix& r, Matrix& x) { + const int nsys = std::get<0>(a.shape()); + Array alpha(nsys); Array gamma(nsys); Array sbuf(4 * nsys); diff --git a/tests/integrated/test-laplace/runtest b/tests/integrated/test-laplace/runtest index d1e9c3d1c9..8704447d66 100755 --- a/tests/integrated/test-laplace/runtest +++ b/tests/integrated/test-laplace/runtest @@ -8,12 +8,11 @@ # Requires: netcdf # Cores: 4 -from __future__ import print_function +from boututils.run_wrapper import build_and_log, shell, launch_safe +from boutdata.collect import collect, create_cache +import numpy.testing as npt +from sys import exit -try: - from builtins import str -except: - pass # Variables to compare vars = [ @@ -36,18 +35,11 @@ vars = [ ] tol = 1e-6 # Absolute tolerance -from boututils.run_wrapper import build_and_log, shell, launch_safe -from boutdata.collect import collect -import numpy as np -from sys import stdout, exit - build_and_log("Laplacian inversion test") # Read benchmark values print("Reading benchmark data") -bmk = {} -for v in vars: - bmk[v] = collect(v, path="data", prefix="benchmark", info=False) +bmk = {v: collect(v, path="data", prefix="benchmark", info=False) for v in vars} print("Running Laplacian inversion test") success = True @@ -58,29 +50,44 @@ for solver in ["cyclic", "pcr", "pcr_thomas"]: if nproc > 2: nxpe = 2 - cmd = "./test_laplace NXPE=" + str(nxpe) + " laplace:type=" + solver + cmd = f"./test_laplace NXPE={nxpe} laplace:type={solver}" shell("rm data/BOUT.dmp.*.nc") - print(" %s solver with %d processors (nxpe = %d)...." % (solver, nproc, nxpe)) + print(f" {solver} solver with {nproc} processors ({nxpe=})....") s, out = launch_safe(cmd, nproc=nproc, mthread=1, pipe=True) - with open("run.log." + str(nproc), "w") as f: + with open(f"run.log.{nproc}", "w") as f: f.write(out) + cache = create_cache(path="data", prefix="BOUT.dmp") + # Collect output data for v in vars: - stdout.write(" Checking variable " + v + " ... ") - result = collect(v, path="data", info=False) + print(f" Checking variable {v} ...", end="") + result = collect(v, path="data", info=False, datafile_cache=cache) # Compare benchmark and output - if np.shape(bmk[v]) != np.shape(result): - print("Fail, wrong shape") - success = False - diff = np.max(np.abs(bmk[v] - result)) - if diff > tol: - print("Fail, maximum difference = " + str(diff)) - success = False - else: + try: + npt.assert_allclose(result, bmk[v], atol=tol, rtol=tol) print("Pass") + except AssertionError as e: + print(f"Fail: {e}") + success = False + + # Only check FieldPerps on one processor because reading them in is + # quite annoying on mutliple cores due to mismatched global y indices + if nproc == 1: + for v in ["flag0_perp", "flag3_perp"]: + print(f" Checking variable {v} ...", end="") + result = collect(v, path="data", info=False, datafile_cache=cache) + # Compare benchmark and output + try: + npt.assert_allclose( + result, bmk[v.replace("_perp", "")][:, 0, :], atol=tol, rtol=tol + ) + print("Pass") + except AssertionError as e: + print(f"Fail: {e}") + success = False if success: print(" => All Laplacian inversion tests passed") diff --git a/tests/integrated/test-laplace/test_laplace.cxx b/tests/integrated/test-laplace/test_laplace.cxx index 94887c718e..abdf796dc6 100644 --- a/tests/integrated/test-laplace/test_laplace.cxx +++ b/tests/integrated/test-laplace/test_laplace.cxx @@ -3,6 +3,8 @@ * */ +#include "bout/field2d.hxx" +#include "bout/globals.hxx" #include #include #include @@ -12,6 +14,8 @@ int main(int argc, char** argv) { // Initialise BOUT++, setting up mesh BoutInitialise(argc, argv); + using bout::globals::mesh; + FieldFactory f{bout::globals::mesh}; Options dump; @@ -24,14 +28,16 @@ int main(int argc, char** argv) { dump["c"] = c; dump["d"] = d; - auto lap = std::unique_ptr{Laplacian::create()}; + auto lap = Laplacian::create(); lap->setCoefA(0.0); lap->setCoefC(1.0); lap->setCoefD(1.0); dump["flag0"] = lap->solve(input); + dump["flag0_perp"] = lap->solve(sliceXZ(input, mesh->ystart)); lap->setInnerBoundaryFlags(INVERT_DC_GRAD + INVERT_AC_GRAD); dump["flag3"] = lap->solve(input); + dump["flag3_perp"] = lap->solve(sliceXZ(input, mesh->ystart)); lap->setCoefA(a); lap->setInnerBoundaryFlags(0); From 83d6660357a767a214c8af344d9403df28367c69 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 30 Jan 2026 13:32:15 +0000 Subject: [PATCH 237/461] Pull out common classes for FFT/DST transforms in some Laplacians The Laplacian inversion solvers `LaplaceCyclic`, `LaplacePCR`, `LaplacePCR_THOMAS` all use identical code for transforming using FFTs/DSTs. This commit pulls out that common code to make it easier to maintain. Also, make the transforms parallel-Z-ready by using the Z guard cells as offsets --- CMakeLists.txt | 2 + include/bout/invert_laplace.hxx | 18 +- src/invert/laplace/common_transform.cxx | 381 +++++++++++++++ src/invert/laplace/common_transform.hxx | 132 ++++++ .../laplace/impls/cyclic/cyclic_laplace.cxx | 435 ++++-------------- src/invert/laplace/impls/pcr/pcr.cxx | 342 ++------------ .../laplace/impls/pcr_thomas/pcr_thomas.cxx | 349 ++------------ src/invert/laplace/invert_laplace.cxx | 27 +- 8 files changed, 689 insertions(+), 997 deletions(-) create mode 100644 src/invert/laplace/common_transform.cxx create mode 100644 src/invert/laplace/common_transform.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index c05bad29db..871079763e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -231,6 +231,8 @@ set(BOUT_SOURCES ./src/field/where.cxx ./src/invert/fft_fftw.cxx ./src/invert/lapack_routines.cxx + ./src/invert/laplace/common_transform.cxx + ./src/invert/laplace/common_transform.hxx ./src/invert/laplace/impls/cyclic/cyclic_laplace.cxx ./src/invert/laplace/impls/cyclic/cyclic_laplace.hxx ./src/invert/laplace/impls/iterative_parallel_tri/iterative_parallel_tri.cxx diff --git a/include/bout/invert_laplace.hxx b/include/bout/invert_laplace.hxx index 187056d115..258b4d2e80 100644 --- a/include/bout/invert_laplace.hxx +++ b/include/bout/invert_laplace.hxx @@ -40,6 +40,7 @@ class Laplacian; #define PVEC_REAL_MPI_TYPE MPI_DOUBLE #endif +#include "bout/bout_types.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" #include "bout/fieldperp.hxx" @@ -50,6 +51,8 @@ class Laplacian; #include "bout/dcomplex.hxx" +class DSTTransform; +class FFTTransform; class Solver; constexpr auto LAPLACE_SPT = "spt"; @@ -258,7 +261,7 @@ public: /// Coefficients in tridiagonal inversion void tridagCoefs(int jx, int jy, int jz, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* ccoef = nullptr, const Field2D* d = nullptr, - CELL_LOC loc = CELL_DEFAULT); + CELL_LOC loc = CELL_DEFAULT) const; /*! * Create a new Laplacian solver @@ -301,6 +304,10 @@ public: void savePerformance(Solver& solver, const std::string& name); protected: + // Give access for tridagMatrix + friend class DSTTransform; + friend class FFTTransform; + bool async_send; ///< If true, use asyncronous send in parallel algorithms int maxmode; ///< The maximum Z mode to solve for @@ -332,23 +339,24 @@ protected: void tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* ccoef = nullptr, const Field2D* d = nullptr, - CELL_LOC loc = CELL_DEFAULT) { + CELL_LOC loc = CELL_DEFAULT) const { tridagCoefs(jx, jy, kwave, a, b, c, ccoef, ccoef, d, loc); } void tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* c1coef, const Field2D* c2coef, const Field2D* d, - CELL_LOC loc = CELL_DEFAULT); + CELL_LOC loc = CELL_DEFAULT) const; void tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dcomplex* bk, int jy, int kz, BoutReal kwave, const Field2D* a, const Field2D* ccoef, - const Field2D* d, bool includeguards = true, bool zperiodic = true) { + const Field2D* d, bool includeguards = true, + bool zperiodic = true) const { tridagMatrix(avec, bvec, cvec, bk, jy, kz, kwave, a, ccoef, ccoef, d, includeguards, zperiodic); } void tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dcomplex* bk, int jy, int kz, BoutReal kwave, const Field2D* a, const Field2D* c1coef, const Field2D* c2coef, const Field2D* d, bool includeguards = true, - bool zperiodic = true); + bool zperiodic = true) const; CELL_LOC location; ///< staggered grid location of this solver Mesh* localmesh; ///< Mesh object for this solver Coordinates* coords; ///< Coordinates object, so we only have to call diff --git a/src/invert/laplace/common_transform.cxx b/src/invert/laplace/common_transform.cxx new file mode 100644 index 0000000000..98571624a1 --- /dev/null +++ b/src/invert/laplace/common_transform.cxx @@ -0,0 +1,381 @@ +#include "common_transform.hxx" + +#include "bout/array.hxx" +#include "bout/bout_types.hxx" +#include "bout/constants.hxx" +#include "bout/dcomplex.hxx" +#include "bout/fft.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/fieldperp.hxx" +#include "bout/invert_laplace.hxx" +#include "bout/mesh.hxx" +#include "bout/openmpwrap.hxx" +#include "bout/utils.hxx" + +#include + +FFTTransform::FFTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, + int zs, int ze, int inbndry, int outbndry, + bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC) + : zlength(getUniform(mesh.getCoordinatesConst()->zlength())), nmode(nmode), xs(xs), + xe(xe), ys(ys), ye(ye), zs(zs), ze(ze), nx(xe - xs + 1), ny(ye - ys + 1), + nz(ze - zs + 1), nxny(nx * ny), nsys(nmode * ny), inbndry(inbndry), + outbndry(outbndry), inner_boundary_set_on_first_x(inner_boundary_set_on_first_x), + outer_boundary_set_on_last_x(outer_boundary_set_on_last_x), zero_DC(zero_DC), + localmesh(&mesh) {} + +auto FFTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, + const Field3D& x0, const Field2D& Acoef, const Field2D& C1coef, + const Field2D& C2coef, const Field2D& Dcoef) const + -> Matrices { + + Matrices result(nsys, nx); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + // Loop over X and Y indices, including boundaries but not guard cells + // (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ind = 0; ind < nxny; ++ind) { + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + // Take FFT in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + rfft(&(x0(ix, iy, zs)), nz, std::begin(k1d)); + } else { + rfft(&(rhs(ix, iy, zs)), nz, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(((iy - ys) * nmode) + kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nsys; ind++) { + const int iy = ys + (ind / nmode); + const int kz = ind % nmode; + + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(ind, 0), &result.b(ind, 0), &result.c(ind, 0), + &result.bcmplx(ind, 0), iy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto FFTTransform::backward(const Field3D& rhs, const Matrix& xcmplx3D) const + -> Field3D { + Field3D x{emptyFrom(rhs)}; + + // FFT back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx3D(((iy - ys) * nmode) + kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + irfft(std::begin(k1d), nz, &(x(ix, iy, zs))); + } + } + + return x; +} + +auto FFTTransform::forward(const Laplacian& laplacian, const FieldPerp& rhs, + const FieldPerp& x0, const Field2D& Acoef, + const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { + + Matrices result(nmode, nx); + const int jy = rhs.getIndex(); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + // Loop over X indices, including boundaries but not guard + // cells (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ix = xs; ix <= xe; ix++) { + // Take FFT in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + rfft(&(x0(ix, zs)), nz, std::begin(k1d)); + } else { + rfft(&(rhs(ix, zs)), nz, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int kz = 0; kz < nmode; kz++) { + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(kz, 0), &result.b(kz, 0), &result.c(kz, 0), + &result.bcmplx(kz, 0), jy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto FFTTransform::backward(const FieldPerp& rhs, const Matrix& xcmplx) const + -> FieldPerp { + FieldPerp x{emptyFrom(rhs)}; + + // FFT back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZFFT routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ix = xs; ix <= xe; ++ix) { + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx(kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + irfft(std::begin(k1d), nz, &(x(ix, zs))); + } + } + + checkData(x); + + return x; +} + +DSTTransform::DSTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, + int zs, int ze, int inbndry, int outbndry, + bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC) + : zlength(getUniform(mesh.getCoordinatesConst()->zlength())), nmode(nmode), xs(xs), + xe(xe), ys(ys), ye(ye), zs(zs), ze(ze), nx(xe - xs + 1), ny(ye - ys + 1), + nz(ze - zs + 1), nxny(nx * ny), nsys(nmode * ny), inbndry(inbndry), + outbndry(outbndry), inner_boundary_set_on_first_x(inner_boundary_set_on_first_x), + outer_boundary_set_on_last_x(outer_boundary_set_on_last_x), zero_DC(zero_DC), + localmesh(&mesh) {} + +auto DSTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, + const Field3D& x0, const Field2D& Acoef, const Field2D& C1coef, + const Field2D& C2coef, const Field2D& Dcoef) const + -> Matrices { + + Matrices result(nsys, nx); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array(nz); + + // Loop over X and Y indices, including boundaries but not guard cells + // (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ind = 0; ind < nxny; ++ind) { + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + // Take DST in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + bout::fft::DST(&(x0(ix, iy, zs + 1)), nz - 2, std::begin(k1d)); + } else { + bout::fft::DST(&(rhs(ix, iy, zs + 1)), nz - 2, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(((iy - ys) * nmode) + kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nsys; ind++) { + const int iy = ys + (ind / nmode); + const int kz = ind % nmode; + + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(ind, 0), &result.b(ind, 0), &result.c(ind, 0), + &result.bcmplx(ind, 0), iy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto DSTTransform::backward(const Field3D& rhs, const Matrix& xcmplx3D) const + -> Field3D { + Field3D x{emptyFrom(rhs)}; + + // DST back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y + const int ix = xs + (ind / ny); + const int iy = ys + (ind % ny); + + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx3D(((iy - ys) * nmode) + kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + bout::fft::DST_rev(std::begin(k1d), nz - 2, &(x(ix, iy, zs + 1))); + + x(ix, iy, zs) = -x(ix, iy, zs + 2); + x(ix, iy, ze) = -x(ix, iy, ze - 2); + } + } + + return x; +} + +auto DSTTransform::forward(const Laplacian& laplacian, const FieldPerp& rhs, + const FieldPerp& x0, const Field2D& Acoef, + const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { + + Matrices result(nmode, nx); + const int jy = rhs.getIndex(); + + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + // Loop over X indices, including boundaries but not guard + // cells (unless periodic in x) + BOUT_OMP_PERF(for) + for (int ix = xs; ix <= xe; ix++) { + // Take DST in Z direction, apply shift, and put result in k1d + + if (((ix < inbndry) and inner_boundary_set_on_first_x) + || ((localmesh->LocalNx - ix - 1 < outbndry) + and outer_boundary_set_on_last_x)) { + // Use the values in x0 in the boundary + rfft(&(x0(ix, zs)), nz, std::begin(k1d)); + } else { + rfft(&(rhs(ix, zs)), nz, std::begin(k1d)); + } + + // Copy into array, transposing so kz is first index + for (int kz = 0; kz < nmode; kz++) { + result.bcmplx(kz, ix - xs) = k1d[kz]; + } + } + + // Get elements of the tridiagonal matrix + // including boundary conditions + BOUT_OMP_PERF(for nowait) + for (int kz = 0; kz < nmode; kz++) { + const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] + laplacian.tridagMatrix(&result.a(kz, 0), &result.b(kz, 0), &result.c(kz, 0), + &result.bcmplx(kz, 0), jy, kz, kwave, &Acoef, &C1coef, + &C2coef, &Dcoef, false); + } + } + return result; +} + +auto DSTTransform::backward(const FieldPerp& rhs, const Matrix& xcmplx) const + -> FieldPerp { + FieldPerp x{emptyFrom(rhs)}; + + // DST back to real space + BOUT_OMP_PERF(parallel) + { + /// Create a local thread-scope working array + // ZDST routine expects input of this length + auto k1d = Array((nz / 2) + 1); + + BOUT_OMP_PERF(for nowait) + for (int ix = xs; ix < xe; ++ix) { + if (zero_DC) { + k1d[0] = 0.; + } + + for (int kz = static_cast(zero_DC); kz < nmode; kz++) { + k1d[kz] = xcmplx(kz, ix - xs); + } + + for (int kz = nmode; kz < (nz / 2) + 1; kz++) { + k1d[kz] = 0.0; // Filtering out all higher harmonics + } + + irfft(std::begin(k1d), nz, &(x(ix, zs))); + } + } + + checkData(x); + + return x; +} diff --git a/src/invert/laplace/common_transform.hxx b/src/invert/laplace/common_transform.hxx new file mode 100644 index 0000000000..a05ca2feb3 --- /dev/null +++ b/src/invert/laplace/common_transform.hxx @@ -0,0 +1,132 @@ +#pragma once + +#include "bout/bout_types.hxx" +#include "bout/dcomplex.hxx" +#include "bout/utils.hxx" + +class Laplacian; +class Field2D; +class Field3D; +class FieldPerp; +class Mesh; + +/// Helper class to do FFTs for `LaplaceCyclic`, `LaplacePCR`, `LaplacePCR_THOMAS` +class FFTTransform { + /// Physical length of the Z domain + BoutReal zlength; + /// Number of unfiltered Fourier modes + int nmode; + + int xs; + int xe; + int ys; + int ye; + int zs; + int ze; + + int nx; + int ny; + /// Number of (interior) points in Z + int nz; + int nxny; + /// Number of systems to solve = number of unfiltered Fourier modes times number of y + /// points + int nsys; + /// Number of inner boundary cells + int inbndry; + /// Number of outer boundary cells + int outbndry; + + bool inner_boundary_set_on_first_x; + bool outer_boundary_set_on_last_x; + + bool zero_DC; + + const Mesh* localmesh; + +public: + FFTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, int zs, + int ze, int inbndry, int outbndry, bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC); + + struct Matrices { + Matrix a; + Matrix b; + Matrix c; + Matrix bcmplx; + + Matrices(int nsys, int nx) + : a(nsys, nx), b(nsys, nx), c(nsys, nx), bcmplx(nsys, nx) {} + }; + + auto forward(const Laplacian& laplacian, const Field3D& rhs, const Field3D& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const Field3D& rhs, const Matrix& xcmplx3D) const -> Field3D; + + auto forward(const Laplacian& laplacian, const FieldPerp& rhs, const FieldPerp& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const FieldPerp& rhs, const Matrix& xcmplx) const -> FieldPerp; +}; + +/// Helper class to do discrete sin transforms (DSTs) for `LaplaceCyclic`, +/// `LaplacePCR`, `LaplacePCR_THOMAS` +class DSTTransform { + /// Physical length of the Z domain + BoutReal zlength; + /// Number of unfiltered Fourier modes + int nmode; + + int xs; + int xe; + int ys; + int ye; + int zs; + int ze; + + int nx; + int ny; + /// Number of (interior) points in Z + int nz; + int nxny; + /// Number of systems to solve = number of unfiltered Fourier modes times number of y + /// points + int nsys; + /// Number of inner boundary cells + int inbndry; + /// Number of outer boundary cells + int outbndry; + + bool inner_boundary_set_on_first_x; + bool outer_boundary_set_on_last_x; + + bool zero_DC; + + const Mesh* localmesh; + +public: + DSTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, int ye, int zs, + int ze, int inbndry, int outbndry, bool inner_boundary_set_on_first_x, + bool outer_boundary_set_on_last_x, bool zero_DC); + + struct Matrices { + Matrix a; + Matrix b; + Matrix c; + Matrix bcmplx; + + Matrices(int nsys, int nx) + : a(nsys, nx), b(nsys, nx), c(nsys, nx), bcmplx(nsys, nx) {} + }; + + auto forward(const Laplacian& laplacian, const Field3D& rhs, const Field3D& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const Field3D& rhs, const Matrix& xcmplx3D) const -> Field3D; + + auto forward(const Laplacian& laplacian, const FieldPerp& rhs, const FieldPerp& x0, + const Field2D& Acoef, const Field2D& C1coef, const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices; + auto backward(const FieldPerp& rhs, const Matrix& xcmplx) const -> FieldPerp; +}; diff --git a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx index 80b22e73f7..0ee6fbe0cd 100644 --- a/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx +++ b/src/invert/laplace/impls/cyclic/cyclic_laplace.cxx @@ -39,6 +39,10 @@ #include "cyclic_laplace.hxx" +#include "../../common_transform.hxx" + +#include "bout/array.hxx" +#include "bout/dcomplex.hxx" #include #include #include @@ -51,6 +55,7 @@ #include #include +#include #include LaplaceCyclic::LaplaceCyclic(Options* opt, const CELL_LOC loc, Mesh* mesh_in, @@ -117,15 +122,11 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); - FieldPerp x{emptyFrom(rhs)}; // Result - - int jy = rhs.getIndex(); // Get the Y index - x.setIndex(jy); - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set - int inbndry = localmesh->xstart, outbndry = localmesh->xstart; + int inbndry = localmesh->xstart; + int outbndry = localmesh->xstart; if (isGlobalFlagSet(INVERT_BOTH_BNDRY_ONE) || (localmesh->xstart < 2)) { inbndry = outbndry = 1; } @@ -137,172 +138,50 @@ FieldPerp LaplaceCyclic::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells. (unless periodic - // in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - // wave number is 1/[rad]; DST has extra 2. - BoutReal kwave = kz * 2.0 * PI / (2. * zlen); - - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false, // Don't include guard cells in arrays - false); // Z domain not periodic - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems cr->setCoefs(a, b, c); cr->solve(bcmplx, xcmplx); - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - - // ZFFT routine expects input of this length - auto k1d = Array(localmesh->LocalNz); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz); kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, x[ix] + 1); - - x(ix, 0) = -x(ix, 2); - x(ix, localmesh->LocalNz - 1) = -x(ix, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array((localmesh->LocalNz) / 2 + 1); - - // Loop over X indices, including boundaries but not guard - // cells (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0[ix], localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs[ix], localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - cr->setCoefs(a, b, c); - cr->solve(bcmplx, xcmplx); + return transform.backward(rhs, xcmplx); + } - if (localmesh->periodicX) { - // Subtract X average of kz=0 mode - BoutReal local[2] = { - 0.0, // index 0 = sum of coefficients - static_cast(xe - xs + 1) // number of grid cells - }; - for (int ix = xs; ix <= xe; ix++) { - local[0] += xcmplx(0, ix - xs).real(); - } - BoutReal global[2]; - MPI_Allreduce(local, global, 2, MPI_DOUBLE, MPI_SUM, localmesh->getXcomm()); - BoutReal avg = global[0] / global[1]; - for (int ix = xs; ix <= xe; ix++) { - xcmplx(0, ix - xs) -= avg; - } + const FFTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); + + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); + + // Solve tridiagonal systems + cr->setCoefs(matrices.a, matrices.b, matrices.c); + cr->solve(matrices.bcmplx, xcmplx); + + if (localmesh->periodicX) { + // Subtract X average of kz=0 mode + std::array local = { + 0.0, // index 0 = sum of coefficients + static_cast(xe - xs + 1) // number of grid cells + }; + for (int ix = xs; ix <= xe; ix++) { + local[0] += xcmplx(0, ix - xs).real(); } - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array((localmesh->LocalNz) / 2 + 1); - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz) / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - irfft(std::begin(k1d), localmesh->LocalNz, x[ix]); - } + std::array global{}; + MPI_Allreduce(local.data(), global.data(), 2, MPI_DOUBLE, MPI_SUM, + localmesh->getXcomm()); + const BoutReal avg = global[0] / global[1]; + for (int ix = xs; ix <= xe; ix++) { + xcmplx(0, ix - xs) -= avg; } } - checkData(x); - - return x; + return transform.backward(rhs, xcmplx); } Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { @@ -318,7 +197,8 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set - int inbndry = localmesh->xstart, outbndry = localmesh->xstart; + int inbndry = localmesh->xstart; + int outbndry = localmesh->xstart; if (isGlobalFlagSet(INVERT_BOTH_BNDRY_ONE) || (localmesh->xstart < 2)) { inbndry = outbndry = 1; } @@ -332,7 +212,8 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { int nx = xe - xs + 1; // Number of X points on this processor // Get range of Y indices - int ys = localmesh->ystart, ye = localmesh->yend; + int ys = localmesh->ystart; + int ye = localmesh->yend; if (localmesh->hasBndryLowerY()) { if (include_yguards) { @@ -351,215 +232,61 @@ Field3D LaplaceCyclic::solve(const Field3D& rhs, const Field3D& x0) { const int ny = (ye - ys + 1); // Number of Y points const int nsys = nmode * ny; // Number of systems of equations to solve - const int nxny = nx * ny; // Number of points in X-Y // This is just to silence static analysis ASSERT0(ny > 0); - auto a3D = Matrix(nsys, nx); - auto b3D = Matrix(nsys, nx); - auto c3D = Matrix(nsys, nx); - auto xcmplx3D = Matrix(nsys, nx); - auto bcmplx3D = Matrix(nsys, nx); if (dst) { - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array(localmesh->LocalNz); - - // Loop over X and Y indices, including boundaries but not guard cells. - // (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - // wave number is 1/[rad]; DST has extra 2. - BoutReal kwave = kz * 2.0 * PI / (2. * zlen); - - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false, // Don't include guard cells in arrays - false); // Z domain not periodic - } - } - - // Solve tridiagonal systems - cr->setCoefs(a3D, b3D, c3D); - cr->solve(bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of length LocalNz - auto k1d = Array(localmesh->LocalNz); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, &x(ix, iy, 1)); - - x(ix, iy, 0) = -x(ix, iy, 2); - x(ix, iy, localmesh->LocalNz - 1) = -x(ix, iy, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - // ZFFT routine expects input of this length - auto k1d = Array(localmesh->LocalNz / 2 + 1); - - // Loop over X and Y indices, including boundaries but not guard cells - // (unless periodic in x) - - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - cr->setCoefs(a3D, b3D, c3D); - cr->solve(bcmplx3D, xcmplx3D); - - if (localmesh->periodicX) { - // Subtract X average of kz=0 mode - std::vector local(ny + 1, 0.0); - for (int y = 0; y < ny; y++) { - for (int ix = xs; ix <= xe; ix++) { - local[y] += xcmplx3D(y * nmode, ix - xs).real(); - } - } - local[ny] = static_cast(xe - xs + 1); - - // Global reduce - std::vector global(ny + 1, 0.0); - MPI_Allreduce(local.data(), global.data(), ny + 1, MPI_DOUBLE, MPI_SUM, - localmesh->getXcomm()); - // Subtract average from kz=0 modes - for (int y = 0; y < ny; y++) { - BoutReal avg = global[y] / global[ny]; - for (int ix = xs; ix <= xe; ix++) { - xcmplx3D(y * nmode, ix - xs) -= avg; - } - } - } - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length + cr->setCoefs(matrices.a, matrices.b, matrices.c); + cr->solve(matrices.bcmplx, xcmplx3D); - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - if (zero_DC) { - k1d[0] = 0.; - } + return transform.backward(rhs, xcmplx3D); + } + const FFTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - for (int kz = nmode; kz < localmesh->LocalNz / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + // Solve tridiagonal systems + cr->setCoefs(matrices.a, matrices.b, matrices.c); + cr->solve(matrices.bcmplx, xcmplx3D); - irfft(std::begin(k1d), localmesh->LocalNz, x(ix, iy)); + if (localmesh->periodicX) { + // Subtract X average of kz=0 mode + std::vector local(ny + 1, 0.0); + for (int y = 0; y < ny; y++) { + for (int ix = xs; ix <= xe; ix++) { + local[y] += xcmplx3D(y * nmode, ix - xs).real(); + } + } + local[ny] = static_cast(xe - xs + 1); + + // Global reduce + std::vector global(ny + 1, 0.0); + MPI_Allreduce(local.data(), global.data(), ny + 1, MPI_DOUBLE, MPI_SUM, + localmesh->getXcomm()); + // Subtract average from kz=0 modes + for (int y = 0; y < ny; y++) { + BoutReal avg = global[y] / global[ny]; + for (int ix = xs; ix <= xe; ix++) { + xcmplx3D(y * nmode, ix - xs) -= avg; } } } - checkData(x); - - return x; + return transform.backward(rhs, xcmplx3D); } void LaplaceCyclic ::verify_solution(const Matrix& a_ver, diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index 61e493d3ed..db9aaf814a 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -38,6 +38,8 @@ #include "pcr.hxx" +#include "../../common_transform.hxx" + #include #include #include @@ -161,151 +163,29 @@ FieldPerp LaplacePCR::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { - const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells. (unless periodic - // in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = - kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. - - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) - - // Solve tridiagonal systems - cr_pcr_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz); kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, x[ix] + 1); - - x(ix, 0) = -x(ix, 2); - x(ix, localmesh->LocalNz - 1) = -x(ix, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells (unless periodic in - // x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0[ix], localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs[ix], localmesh->LocalNz, std::begin(k1d)); - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - cr_pcr_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - if (zero_DC) { - k1d[0] = 0.; - } + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz) / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - irfft(std::begin(k1d), localmesh->LocalNz, x[ix]); - } - } + return transform.backward(rhs, xcmplx); } + const FFTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - checkData(x); + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - return x; + // Solve tridiagonal systems + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); + + return transform.backward(rhs, xcmplx); } Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { @@ -316,8 +196,6 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { Timer timer("invert"); - Field3D x{emptyFrom(rhs)}; // Result - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set @@ -356,185 +234,33 @@ Field3D LaplacePCR::solve(const Field3D& rhs, const Field3D& x0) { const int ny = (ye - ys + 1); // Number of Y points nsys = nmode * ny; // Number of systems of equations to solve - const int nxny = nx * ny; // Number of points in X-Y - - auto a3D = Matrix(nsys, nx); - auto b3D = Matrix(nsys, nx); - auto c3D = Matrix(nsys, nx); auto xcmplx3D = Matrix(nsys, nx); - auto bcmplx3D = Matrix(nsys, nx); if (dst) { - const BoutReal zlen = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells. - // (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - BoutReal kwave = - kz * 2.0 * PI / (2. * zlen); // wave number is 1/[rad]; DST has extra 2. - - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) - - // Solve tridiagonal systems - cr_pcr_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, &x(ix, iy, 1)); - - x(ix, iy, 0) = -x(ix, iy, 2); - x(ix, iy, localmesh->LocalNz - 1) = -x(ix, iy, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array(localmesh->LocalNz / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells - // (unless periodic in x) - - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } // BOUT_OMP_PERF(parallel) + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - cr_pcr_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - irfft(std::begin(k1d), localmesh->LocalNz, x(ix, iy)); - } - } + return transform.backward(rhs, xcmplx3D); } + const FFTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); + + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - checkData(x); + // Solve tridiagonal systems + cr_pcr_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - return x; + return transform.backward(rhs, xcmplx3D); } /** diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index 604a630feb..99d1a4e30a 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -38,6 +38,8 @@ #include "pcr_thomas.hxx" +#include "../../common_transform.hxx" + #include "bout/array.hxx" #include "bout/boutcomm.hxx" #include "bout/dcomplex.hxx" @@ -139,11 +141,6 @@ FieldPerp LaplacePCR_THOMAS::solve(const FieldPerp& rhs, const FieldPerp& x0) { ASSERT1(rhs.getLocation() == location); ASSERT1(x0.getLocation() == location); - FieldPerp x{emptyFrom(rhs)}; // Result - - int jy = rhs.getIndex(); // Get the Y index - x.setIndex(jy); - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set @@ -160,151 +157,30 @@ FieldPerp LaplacePCR_THOMAS::solve(const FieldPerp& rhs, const FieldPerp& x0) { } if (dst) { - const BoutReal zlength = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells. (unless periodic - // in x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs[ix] + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - // wave number is 1/[rad]; DST has extra 2. - const BoutReal kwave = kz * 2.0 * PI / (2. * zlength); - - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - pcr_thomas_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } - - for (int kz = nmode; kz < (localmesh->LocalNz); kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, x[ix] + 1); - - x(ix, 0) = -x(ix, 2); - x(ix, localmesh->LocalNz - 1) = -x(ix, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X indices, including boundaries but not guard cells (unless periodic in - // x) - BOUT_OMP_PERF(for) - for (int ix = xs; ix <= xe; ix++) { - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0[ix], localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs[ix], localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx(kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int kz = 0; kz < nmode; kz++) { - const BoutReal kwave = kz * 2.0 * PI / zlength; // wave number is 1/[rad] - tridagMatrix(&a(kz, 0), &b(kz, 0), &c(kz, 0), &bcmplx(kz, 0), jy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - pcr_thomas_solver(a, b, c, bcmplx, xcmplx); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); + return transform.backward(rhs, xcmplx); + } - BOUT_OMP_PERF(for nowait) - for (int ix = xs; ix <= xe; ix++) { - if (zero_DC) { - k1d[0] = 0.; - } + const FFTTransform transform( + *localmesh, nmode, xs, xe, 0, 0, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx(kz, ix - xs); - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - for (int kz = nmode; kz < (localmesh->LocalNz) / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + // Solve tridiagonal systems + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx); - irfft(std::begin(k1d), localmesh->LocalNz, x[ix]); - } - } - } - - checkData(x); - - return x; + return transform.backward(rhs, xcmplx); } Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { @@ -315,8 +191,6 @@ Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { Timer timer("invert"); - Field3D x{emptyFrom(rhs)}; // Result - // Get the width of the boundary // If the flags to assign that only one guard cell should be used is set @@ -332,7 +206,7 @@ Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { outbndry = 1; } - int nx = xe - xs + 1; // Number of X points on this processor + const int nx = xe - xs + 1; // Number of X points on this processor // Get range of Y indices int ys = localmesh->ystart; @@ -355,186 +229,33 @@ Field3D LaplacePCR_THOMAS::solve(const Field3D& rhs, const Field3D& x0) { const int ny = (ye - ys + 1); // Number of Y points nsys = nmode * ny; // Number of systems of equations to solve - const int nxny = nx * ny; // Number of points in X-Y - - auto a3D = Matrix(nsys, nx); - auto b3D = Matrix(nsys, nx); - auto c3D = Matrix(nsys, nx); - auto xcmplx3D = Matrix(nsys, nx); - auto bcmplx3D = Matrix(nsys, nx); if (dst) { - const BoutReal zlength = getUniform(coords->dz) * (localmesh->LocalNz - 3); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells. - // (unless periodic in x) - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take DST in Z direction and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - DST(x0(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } else { - DST(rhs(ix, iy) + 1, localmesh->LocalNz - 2, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } - - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - // wave number is 1/[rad]; DST has extra 2. - BoutReal kwave = kz * 2.0 * PI / (2. * zlength); - - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // wave number index - kwave, // kwave (inverse wave length) - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } - - // Solve tridiagonal systems - pcr_thomas_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array( - localmesh->LocalNz); // ZFFT routine expects input of this length - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - for (int kz = 0; kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } - - for (int kz = nmode; kz < localmesh->LocalNz; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } - - DST_rev(std::begin(k1d), localmesh->LocalNz - 2, &x(ix, iy, 1)); - - x(ix, iy, 0) = -x(ix, iy, 2); - x(ix, iy, localmesh->LocalNz - 1) = -x(ix, iy, localmesh->LocalNz - 3); - } - } - } else { - const BoutReal zlength = getUniform(coords->zlength()); - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array(localmesh->LocalNz / 2 - + 1); // ZFFT routine expects input of this length - - // Loop over X and Y indices, including boundaries but not guard cells - // (unless periodic in x) - - BOUT_OMP_PERF(for) - for (int ind = 0; ind < nxny; ++ind) { - // ind = (ix - xs)*(ye - ys + 1) + (iy - ys) - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - // Take FFT in Z direction, apply shift, and put result in k1d - - if (((ix < inbndry) && isInnerBoundaryFlagSetOnFirstX(INVERT_SET)) - || ((localmesh->LocalNx - ix - 1 < outbndry) - && isOuterBoundaryFlagSetOnLastX(INVERT_SET))) { - // Use the values in x0 in the boundary - rfft(x0(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } else { - rfft(rhs(ix, iy), localmesh->LocalNz, std::begin(k1d)); - } - - // Copy into array, transposing so kz is first index - for (int kz = 0; kz < nmode; kz++) { - bcmplx3D((iy - ys) * nmode + kz, ix - xs) = k1d[kz]; - } - } + const DSTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - // Get elements of the tridiagonal matrix - // including boundary conditions - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nsys; ind++) { - // ind = (iy - ys) * nmode + kz - int iy = ys + ind / nmode; - int kz = ind % nmode; - - // wave number is 1/[rad] - BoutReal kwave = kz * 2.0 * PI / zlength; - tridagMatrix(&a3D(ind, 0), &b3D(ind, 0), &c3D(ind, 0), &bcmplx3D(ind, 0), iy, - kz, // True for the component constant (DC) in Z - kwave, // Z wave number - &Acoef, &C1coef, &C2coef, &Dcoef, - false); // Don't include guard cells in arrays - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); // Solve tridiagonal systems - pcr_thomas_solver(a3D, b3D, c3D, bcmplx3D, xcmplx3D); - - // FFT back to real space - BOUT_OMP_PERF(parallel) - { - /// Create a local thread-scope working array - auto k1d = Array((localmesh->LocalNz) / 2 - + 1); // ZFFT routine expects input of this length + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - const bool zero_DC = isGlobalFlagSet(INVERT_ZERO_DC); - - BOUT_OMP_PERF(for nowait) - for (int ind = 0; ind < nxny; ++ind) { // Loop over X and Y - int ix = xs + ind / ny; - int iy = ys + ind % ny; - - if (zero_DC) { - k1d[0] = 0.; - } - - for (int kz = static_cast(zero_DC); kz < nmode; kz++) { - k1d[kz] = xcmplx3D((iy - ys) * nmode + kz, ix - xs); - } + return transform.backward(rhs, xcmplx3D); + } - for (int kz = nmode; kz < localmesh->LocalNz / 2 + 1; kz++) { - k1d[kz] = 0.0; // Filtering out all higher harmonics - } + const FFTTransform transform( + *localmesh, nmode, xs, xe, ys, ye, localmesh->zstart, localmesh->zend, inbndry, + outbndry, isInnerBoundaryFlagSetOnFirstX(INVERT_SET), + isOuterBoundaryFlagSetOnLastX(INVERT_SET), isGlobalFlagSet(INVERT_ZERO_DC)); - irfft(std::begin(k1d), localmesh->LocalNz, x(ix, iy)); - } - } - } + auto matrices = transform.forward(*this, rhs, x0, Acoef, C1coef, C2coef, Dcoef); - checkData(x); + // Solve tridiagonal systems + pcr_thomas_solver(matrices.a, matrices.b, matrices.c, matrices.bcmplx, xcmplx3D); - return x; + return transform.backward(rhs, xcmplx3D); } /** diff --git a/src/invert/laplace/invert_laplace.cxx b/src/invert/laplace/invert_laplace.cxx index e96248ef7e..e9182042de 100644 --- a/src/invert/laplace/invert_laplace.cxx +++ b/src/invert/laplace/invert_laplace.cxx @@ -44,6 +44,7 @@ #include #include #include +#include #include // Implementations: @@ -90,19 +91,12 @@ Laplacian::Laplacian(Options* options, const CELL_LOC loc, Mesh* mesh_in, const BoutReal filter = (*options)["filter"] .doc("Fraction of Z modes to filter out. Between 0 and 1") .withDefault(0.0); - const int ncz = localmesh->LocalNz; + const int ncz = localmesh->zend - localmesh->zstart + 1; // convert filtering into an integer number of modes maxmode = (*options)["maxmode"] .doc("The maximum Z mode to solve for") - .withDefault(ROUND((1.0 - filter) * static_cast(ncz / 2))); - - // Clamp maxmode between 0 and nz/2 - if (maxmode < 0) { - maxmode = 0; - } - if (maxmode > ncz / 2) { - maxmode = ncz / 2; - } + .withDefault(ROUND((1.0 - filter) * static_cast(ncz) / 2.0)); + maxmode = std::clamp(maxmode, 0, ncz / 2); low_mem = (*options)["low_mem"] .doc("If true, reduce the amount of memory used") @@ -259,7 +253,7 @@ Field2D Laplacian::solve(const Field2D& b, const Field2D& x0) { **********************************************************************************/ void Laplacian::tridagCoefs(int jx, int jy, int jz, dcomplex& a, dcomplex& b, dcomplex& c, - const Field2D* ccoef, const Field2D* d, CELL_LOC loc) { + const Field2D* ccoef, const Field2D* d, CELL_LOC loc) const { if (loc == CELL_DEFAULT) { loc = location; @@ -276,13 +270,13 @@ void Laplacian::tridagCoefs(int jx, int jy, int jz, dcomplex& a, dcomplex& b, dc void Laplacian::tridagCoefs(int /* jx */, int /* jy */, BoutReal /* kwave */, dcomplex& /* a */, dcomplex& /* b */, dcomplex& /* c */, const Field2D* /* c1coef */, const Field2D* /* c2coef */, - const Field2D* /* d */, CELL_LOC /* loc */) { + const Field2D* /* d */, CELL_LOC /* loc */) const { throw BoutException("Laplacian::tridagCoefs() does not support 3d metrics."); } #else void Laplacian::tridagCoefs(int jx, int jy, BoutReal kwave, dcomplex& a, dcomplex& b, dcomplex& c, const Field2D* c1coef, const Field2D* c2coef, - const Field2D* d, CELL_LOC loc) { + const Field2D* d, CELL_LOC loc) const { /* Function: Laplacian::tridagCoef * Purpose: - Set the matrix components of A in Ax=b, solving * @@ -424,14 +418,14 @@ void Laplacian::tridagMatrix(dcomplex* /*avec*/, dcomplex* /*bvec*/, dcomplex* / dcomplex* /*bk*/, int /*jy*/, int /*kz*/, BoutReal /*kwave*/, const Field2D* /*a*/, const Field2D* /*c1coef*/, const Field2D* /*c2coef*/, const Field2D* /*d*/, - bool /*includeguards*/, bool /*zperiodic*/) { + bool /*includeguards*/, bool /*zperiodic*/) const { throw BoutException("Error: tridagMatrix does not yet work with 3D metric."); } #else void Laplacian::tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dcomplex* bk, int jy, int kz, BoutReal kwave, const Field2D* a, const Field2D* c1coef, const Field2D* c2coef, - const Field2D* d, bool includeguards, bool zperiodic) { + const Field2D* d, bool includeguards, bool zperiodic) const { ASSERT1(a->getLocation() == location); ASSERT1(c1coef->getLocation() == location); ASSERT1(c2coef->getLocation() == location); @@ -460,7 +454,8 @@ void Laplacian::tridagMatrix(dcomplex* avec, dcomplex* bvec, dcomplex* cvec, dco // Setting the width of the boundary. // NOTE: The default is a width of (localmesh->xstart) guard cells - int inbndry = localmesh->xstart, outbndry = localmesh->xstart; + int inbndry = localmesh->xstart; + int outbndry = localmesh->xstart; // If the flags to assign that only one guard cell should be used is set if (isGlobalFlagSet(INVERT_BOTH_BNDRY_ONE) || (localmesh->xstart < 2)) { From c8f38049359170a34c915e209276238ea66b9a1e Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:31:44 +0000 Subject: [PATCH 238/461] [bot] Apply format changes --- src/invert/laplace/common_transform.cxx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/invert/laplace/common_transform.cxx b/src/invert/laplace/common_transform.cxx index 98571624a1..847add4fea 100644 --- a/src/invert/laplace/common_transform.cxx +++ b/src/invert/laplace/common_transform.cxx @@ -28,8 +28,8 @@ FFTTransform::FFTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, auto FFTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, const Field3D& x0, const Field2D& Acoef, const Field2D& C1coef, - const Field2D& C2coef, const Field2D& Dcoef) const - -> Matrices { + const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { Matrices result(nsys, nx); @@ -79,8 +79,8 @@ auto FFTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, return result; } -auto FFTTransform::backward(const Field3D& rhs, const Matrix& xcmplx3D) const - -> Field3D { +auto FFTTransform::backward(const Field3D& rhs, + const Matrix& xcmplx3D) const -> Field3D { Field3D x{emptyFrom(rhs)}; // FFT back to real space @@ -162,8 +162,8 @@ auto FFTTransform::forward(const Laplacian& laplacian, const FieldPerp& rhs, return result; } -auto FFTTransform::backward(const FieldPerp& rhs, const Matrix& xcmplx) const - -> FieldPerp { +auto FFTTransform::backward(const FieldPerp& rhs, + const Matrix& xcmplx) const -> FieldPerp { FieldPerp x{emptyFrom(rhs)}; // FFT back to real space @@ -209,8 +209,8 @@ DSTTransform::DSTTransform(const Mesh& mesh, int nmode, int xs, int xe, int ys, auto DSTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, const Field3D& x0, const Field2D& Acoef, const Field2D& C1coef, - const Field2D& C2coef, const Field2D& Dcoef) const - -> Matrices { + const Field2D& C2coef, + const Field2D& Dcoef) const -> Matrices { Matrices result(nsys, nx); @@ -260,8 +260,8 @@ auto DSTTransform::forward(const Laplacian& laplacian, const Field3D& rhs, return result; } -auto DSTTransform::backward(const Field3D& rhs, const Matrix& xcmplx3D) const - -> Field3D { +auto DSTTransform::backward(const Field3D& rhs, + const Matrix& xcmplx3D) const -> Field3D { Field3D x{emptyFrom(rhs)}; // DST back to real space @@ -346,8 +346,8 @@ auto DSTTransform::forward(const Laplacian& laplacian, const FieldPerp& rhs, return result; } -auto DSTTransform::backward(const FieldPerp& rhs, const Matrix& xcmplx) const - -> FieldPerp { +auto DSTTransform::backward(const FieldPerp& rhs, + const Matrix& xcmplx) const -> FieldPerp { FieldPerp x{emptyFrom(rhs)}; // DST back to real space From fee25979c8bfd9c8d427df262b3be91b75f49b5c Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:31:45 +0000 Subject: [PATCH 239/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 010100588c..d04b9f1259 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -8,3 +8,4 @@ ed2117e6d6826a98b6988e2f18c0c34e408563b6 a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 2c2402ed59c91164eaff46dee0f79386b7347e9e 05b7c571544c3bcb153fce67d12b9ac48947fc2d +c8f38049359170a34c915e209276238ea66b9a1e From 353e709d6f9958faeda4e381efe6d52097bcb571 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 23 Feb 2026 17:22:15 +0000 Subject: [PATCH 240/461] Disable LaplaceXYHypre and test if using 3D metrics --- src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx | 2 +- src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx | 6 ++++++ tests/integrated/test-laplacexy2-hypre/CMakeLists.txt | 1 + 3 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx index 9323513f21..439d07a4e5 100644 --- a/src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx +++ b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.cxx @@ -1,6 +1,6 @@ #include "bout/build_defines.hxx" -#if BOUT_HAS_HYPRE +#if BOUT_HAS_HYPRE and not BOUT_USE_METRIC_3D #include "laplacexy-hypre.hxx" diff --git a/src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx index f415f9ebf4..f9b1d90fb3 100644 --- a/src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx +++ b/src/invert/laplacexy/impls/hypre/laplacexy-hypre.hxx @@ -43,6 +43,12 @@ RegisterUnavailableLaplaceXY registerlaplacexyhypre("hypre", "BOUT++ was not configured with HYPRE"); } +#elif BOUT_USE_METRIC_3D +namespace { +RegisterUnavailableLaplaceXY + registerlaplacexyhypre("hypre", "BOUT++ was configured with 3D metrics"); +} + #else // BOUT_HAS_HYPRE class Mesh; diff --git a/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt b/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt index e56829b243..4f16f78713 100644 --- a/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt +++ b/tests/integrated/test-laplacexy2-hypre/CMakeLists.txt @@ -2,5 +2,6 @@ bout_add_integrated_test( test-laplacexy2-hypre SOURCES test-laplacexy.cxx REQUIRES BOUT_HAS_HYPRE + CONFLICTS BOUT_USE_METRIC_3D USE_RUNTEST USE_DATA_BOUT_INP ) From f887aed94e50bcef5b7cf9bc4568f62435b581c0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 23 Feb 2026 17:24:51 +0000 Subject: [PATCH 241/461] CI: remove (mostly) redundant test This test has significant overlap with other tests, it doesn't really give us any useful information --- .github/workflows/tests.yml | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f44ebb87ec..0abc01ef5f 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -121,22 +121,6 @@ jobs: omp_num_threads: 2 on_cron: false - - name: "CMake, new PETSc" - os: ubuntu-latest - cmake_options: "-DBUILD_SHARED_LIBS=ON - -DBOUT_ENABLE_METRIC_3D=ON - -DBOUT_ENABLE_OPENMP=ON - -DBOUT_USE_PETSC=ON - -DBOUT_USE_SLEPC=ON - -DBOUT_USE_SUNDIALS=ON - -DBOUT_USE_HYPRE=OFF - -DBOUT_ENABLE_PYTHON=ON - -DSUNDIALS_ROOT=/home/runner/local - -DPETSC_DIR=/home/runner/local/petsc - -DSLEPC_DIR=/home/runner/local/slepc" - build_petsc: -petsc - on_cron: false - exclude: - is_cron: true config: From 4c96e9b9639e236def795f1efd17289c63e52828 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 23 Feb 2026 17:25:41 +0000 Subject: [PATCH 242/461] CI: rename build to be more accurate --- .github/workflows/tests.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 0abc01ef5f..e45e082232 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -40,7 +40,7 @@ jobs: is_cron: - ${{ github.event_name == 'cron' }} config: - - name: "CMake, PETSc unreleased, ADIOS2" + - name: "PETSc unreleased, ADIOS2, 3D metrics" os: ubuntu-24.04 cmake_options: "-DBUILD_SHARED_LIBS=ON -DBOUT_ENABLE_METRIC_3D=ON From 18ed91d855ebd7ac7d230b59c5ad44aeb818d426 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 23 Feb 2026 18:45:05 +0000 Subject: [PATCH 243/461] Mark unavailable Laplacian solvers that don't work with 3D metrics --- .../laplace/impls/naulin/naulin_laplace.cxx | 6 ++++++ .../laplace/impls/naulin/naulin_laplace.hxx | 15 ++++++++++++++- src/invert/laplace/impls/pcr/pcr.cxx | 6 ++++++ src/invert/laplace/impls/pcr/pcr.hxx | 17 +++++++++++++++-- .../laplace/impls/pcr_thomas/pcr_thomas.cxx | 6 ++++++ .../laplace/impls/pcr_thomas/pcr_thomas.hxx | 15 ++++++++++++++- .../laplace/impls/serial_tri/serial_tri.cxx | 6 ++++++ .../laplace/impls/serial_tri/serial_tri.hxx | 15 ++++++++++++++- src/invert/laplace/impls/spt/spt.cxx | 6 ++++++ src/invert/laplace/impls/spt/spt.hxx | 16 ++++++++++++++-- 10 files changed, 101 insertions(+), 7 deletions(-) diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.cxx b/src/invert/laplace/impls/naulin/naulin_laplace.cxx index f61dc93908..1c90976ce6 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.cxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.cxx @@ -138,6 +138,10 @@ */ // clang-format on +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include @@ -426,3 +430,5 @@ void LaplaceNaulin::outputVars(Options& output_options, output_options[fmt::format("{}_mean_underrelax_counts", getPerformanceName())] .assignRepeat(naulinsolver_mean_underrelax_counts, time_dimension); } + +#endif diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.hxx b/src/invert/laplace/impls/naulin/naulin_laplace.hxx index 1998a046d7..c4e7d41d9e 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.hxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.hxx @@ -28,11 +28,22 @@ class LaplaceNaulin; #ifndef BOUT_LAP_NAULIN_H #define BOUT_LAP_NAULIN_H +#include #include + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacenaulin(LAPLACE_NAULIN, "BOUT++ was configured with 3D metrics"); +} + +#else + #include namespace { -RegisterLaplace registerlaplacenaulin(LAPLACE_NAULIN); +const RegisterLaplace registerlaplacenaulin(LAPLACE_NAULIN); } /// Solves the 2D Laplacian equation @@ -198,4 +209,6 @@ private: void copy_x_boundaries(Field3D& x, const Field3D& x0, Mesh* mesh); }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_LAP_NAULIN_H diff --git a/src/invert/laplace/impls/pcr/pcr.cxx b/src/invert/laplace/impls/pcr/pcr.cxx index a33bd7eef2..591edb5080 100644 --- a/src/invert/laplace/impls/pcr/pcr.cxx +++ b/src/invert/laplace/impls/pcr/pcr.cxx @@ -36,6 +36,10 @@ * **************************************************************************/ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include "pcr.hxx" #include "bout/globals.hxx" @@ -1108,3 +1112,5 @@ void LaplacePCR ::verify_solution(const Matrix& a_ver, output.write("max abs error {}\n", max_error); output.write("max abs error location {} {}\n", max_loc_x, max_loc_z); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/pcr/pcr.hxx b/src/invert/laplace/impls/pcr/pcr.hxx index ec4637f56c..2b90e17466 100644 --- a/src/invert/laplace/impls/pcr/pcr.hxx +++ b/src/invert/laplace/impls/pcr/pcr.hxx @@ -29,13 +29,24 @@ class LaplacePCR; #ifndef BOUT_PCR_H #define BOUT_PCR_H -#include +#include #include + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacepcr(LAPLACE_PCR, "BOUT++ was configured with 3D metrics"); +} + +#else + +#include #include #include namespace { -RegisterLaplace registerlaplacepcr(LAPLACE_PCR); +const RegisterLaplace registerlaplacepcr(LAPLACE_PCR); } class LaplacePCR : public Laplacian { @@ -175,4 +186,6 @@ private: bool dst{false}; }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_PCR_H diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx index d471775990..1b8c66dfdf 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.cxx @@ -36,6 +36,10 @@ * **************************************************************************/ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include "pcr_thomas.hxx" #include "bout/globals.hxx" @@ -1190,3 +1194,5 @@ void LaplacePCR_THOMAS ::verify_solution(const Matrix& a_ver, } output.write("max abs error {}\n", max_error); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx index e12a647789..1b5c6b97b9 100644 --- a/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx +++ b/src/invert/laplace/impls/pcr_thomas/pcr_thomas.hxx @@ -29,8 +29,19 @@ class LaplacePCR_THOMAS; #ifndef BOUT_PCR_THOMAS_H #define BOUT_PCR_THOMAS_H +#include "bout/build_defines.hxx" +#include "bout/invert_laplace.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacepcrthomas(LAPLACE_PCR_THOMAS, "BOUT++ was configured with 3D metrics"); +} + +#else + #include -#include #include #include @@ -178,4 +189,6 @@ private: bool dst{false}; }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_PCR_THOMAS_H diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.cxx b/src/invert/laplace/impls/serial_tri/serial_tri.cxx index a14e0e4a26..d051ce0c1e 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.cxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.cxx @@ -24,6 +24,10 @@ * **************************************************************************/ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include "serial_tri.hxx" #include "bout/globals.hxx" @@ -247,3 +251,5 @@ FieldPerp LaplaceSerialTri::solve(const FieldPerp& b, const FieldPerp& x0) { return x; // Result of the inversion } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/serial_tri/serial_tri.hxx b/src/invert/laplace/impls/serial_tri/serial_tri.hxx index 5b0419fa27..4aed777b7c 100644 --- a/src/invert/laplace/impls/serial_tri/serial_tri.hxx +++ b/src/invert/laplace/impls/serial_tri/serial_tri.hxx @@ -29,8 +29,19 @@ class LaplaceSerialTri; #ifndef BOUT_SERIAL_TRI_H #define BOUT_SERIAL_TRI_H +#include "bout/build_defines.hxx" +#include "bout/invert_laplace.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacetri(LAPLACE_TRI, "BOUT++ was configured with 3D metrics"); +} + +#else + #include -#include #include namespace { @@ -80,4 +91,6 @@ private: Field2D A, C, D; }; +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_SERIAL_TRI_H diff --git a/src/invert/laplace/impls/spt/spt.cxx b/src/invert/laplace/impls/spt/spt.cxx index cd24ee1acf..34a1a64cae 100644 --- a/src/invert/laplace/impls/spt/spt.cxx +++ b/src/invert/laplace/impls/spt/spt.cxx @@ -31,6 +31,10 @@ * */ +#include "bout/build_defines.hxx" + +#if not BOUT_USE_METRIC_3D + #include #include #include @@ -553,3 +557,5 @@ void LaplaceSPT::SPT_data::allocate(int mm, int nx) { buffer.reallocate(4 * mm); } + +#endif // BOUT_USE_METRIC_3D diff --git a/src/invert/laplace/impls/spt/spt.hxx b/src/invert/laplace/impls/spt/spt.hxx index a9d5b2583f..a3b1acb7c3 100644 --- a/src/invert/laplace/impls/spt/spt.hxx +++ b/src/invert/laplace/impls/spt/spt.hxx @@ -39,10 +39,20 @@ class LaplaceSPT; #ifndef BOUT_SPT_H +#include "bout/build_defines.hxx" +#include "bout/invert_laplace.hxx" + +#if BOUT_USE_METRIC_3D + +namespace { +const RegisterUnavailableLaplace + registerlaplacespt(LAPLACE_SPT, "BOUT++ was configured with 3D metrics"); +} + +#else #define BOUT_SPT_H #include -#include #include #include #include @@ -153,7 +163,9 @@ private: namespace { // Note: After class definition so compiler knows that // registered class is derived from Laplacian -RegisterLaplace registerlaplacespt(LAPLACE_SPT); +const RegisterLaplace registerlaplacespt(LAPLACE_SPT); } // namespace +#endif // BOUT_USE_METRIC_3D + #endif // BOUT_SPT_H From daf63738b55d315bb221bf20bc4d8e28b7a7c10c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 23 Feb 2026 18:48:08 +0000 Subject: [PATCH 244/461] Get PETSc Laplacian test working with 3D metrics - Remove comparison to default solver - The default is cyclic which doesn't work with 3D metrics - Also we don't actually do any comparison, so it doesn't help us test anything - Use one of PETSc's preconditioners instead of one of our solvers - Prefer MUMPS but fall back to LU, which should always be available --- .../test-petsc_laplace/CMakeLists.txt | 2 - .../test-petsc_laplace/data/BOUT.inp | 9 --- tests/integrated/test-petsc_laplace/runtest | 2 - .../test-petsc_laplace/test_petsc_laplace.cxx | 80 +++++++++++-------- 4 files changed, 46 insertions(+), 47 deletions(-) diff --git a/tests/integrated/test-petsc_laplace/CMakeLists.txt b/tests/integrated/test-petsc_laplace/CMakeLists.txt index 1d2e7896f0..977b33afe2 100644 --- a/tests/integrated/test-petsc_laplace/CMakeLists.txt +++ b/tests/integrated/test-petsc_laplace/CMakeLists.txt @@ -2,8 +2,6 @@ bout_add_integrated_test( test-petsc-laplace SOURCES test_petsc_laplace.cxx REQUIRES BOUT_HAS_PETSC - CONFLICTS - BOUT_USE_METRIC_3D # default preconditioner uses 'cyclic' Laplace solver which is not available with 3d metrics USE_RUNTEST USE_DATA_BOUT_INP PROCESSORS 4 ) diff --git a/tests/integrated/test-petsc_laplace/data/BOUT.inp b/tests/integrated/test-petsc_laplace/data/BOUT.inp index e7c285b54c..15e54ff753 100644 --- a/tests/integrated/test-petsc_laplace/data/BOUT.inp +++ b/tests/integrated/test-petsc_laplace/data/BOUT.inp @@ -67,15 +67,6 @@ outer_boundary_flags = 32 # Identity in boundary ############################################# -[SPT] -#type=spt -all_terms = true -nonuniform = true -#flags=15 -include_yguards = false - -#maxits=10000 - [laplace] all_terms = true nonuniform = true diff --git a/tests/integrated/test-petsc_laplace/runtest b/tests/integrated/test-petsc_laplace/runtest index 83e1006338..5496987128 100755 --- a/tests/integrated/test-petsc_laplace/runtest +++ b/tests/integrated/test-petsc_laplace/runtest @@ -16,11 +16,9 @@ vars = [ ("max_error1", 2.0e-4), ("max_error2", 2.0e-4), ("max_error3", 2.0e-4), - ("max_error4", 1.0e-5), ("max_error5", 2.0e-4), ("max_error6", 2.0e-5), ("max_error7", 2.0e-4), - ("max_error8", 2.0e-5), ] # tol = 1e-4 # Absolute (?) tolerance diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index c42c55d8d6..e869cd1b1d 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -26,6 +26,7 @@ #include "bout/bout.hxx" // NOLINT #include "bout/bout_types.hxx" #include "bout/boutexception.hxx" +#include "bout/build_config.hxx" #include "bout/constants.hxx" #include "bout/difops.hxx" #include "bout/field2d.hxx" @@ -39,6 +40,7 @@ #include "fmt/core.h" #include +#include #include #include @@ -112,10 +114,37 @@ int main(int argc, char** argv) { BoutInitialise(argc, argv); { - Options* options = Options::getRoot()->getSection("petsc2nd"); - auto invert = Laplacian::create(options); - options = Options::getRoot()->getSection("petsc4th"); - auto invert_4th = Laplacian::create(options); + // Not be used for 3D metrics + Options::root()["laplace"].setConditionallyUsed(); + + // For 3D metrics, we need to use one of PETSc's preconditioners + // and mumps seems to be the best? Or at least HYPRE doesn't work + // on this test with more than one processor. +#ifdef PETSC_HAVE_MUMPS + constexpr auto petsc_has_mumps = true; +#else + constexpr auto petsc_has_mumps = false; +#endif + + // Preconditioner to use for 3D metrics + constexpr auto petsc_pc = petsc_has_mumps ? "mumps" : "lu"; + + auto& options_2nd = Options::root()["petsc2nd"]; + if constexpr (bout::build::use_metric_3d) { + options_2nd["pctype"] = petsc_pc; + options_2nd["rightprec"].setConditionallyUsed(); + options_2nd["precon"].setConditionallyUsed(); + } + + auto invert = Laplacian::create(&options_2nd); + + auto& options_4th = Options::root()["petsc4th"]; + if constexpr (bout::build::use_metric_3d) { + options_4th["pctype"] = petsc_pc; + options_4th["rightprec"].setConditionallyUsed(); + options_4th["precon"].setConditionallyUsed(); + } + auto invert_4th = Laplacian::create(&options_4th); Options dump; @@ -136,35 +165,27 @@ int main(int argc, char** argv) { const Field3D b_1 = forward_laplace(f_1, a_1, c_1, d_1); - int test_num = 0; - check_laplace(++test_num, "PETSc 2nd order", *invert, INVERT_AC_GRAD, INVERT_AC_GRAD, - a_1, c_1, d_1, b_1, f_1, mesh->ystart, dump); + check_laplace(1, "PETSc 2nd order", *invert, INVERT_AC_GRAD, INVERT_AC_GRAD, a_1, c_1, + d_1, b_1, f_1, mesh->ystart, dump); ///////////////////////////////////////////////// // Test 2: Gaussian x-profiles, 4th order Krylov - check_laplace(++test_num, "PETSc 4th order", *invert_4th, INVERT_AC_GRAD, - INVERT_AC_GRAD, a_1, c_1, d_1, b_1, f_1, mesh->ystart, dump); + check_laplace(2, "PETSc 4th order", *invert_4th, INVERT_AC_GRAD, INVERT_AC_GRAD, a_1, + c_1, d_1, b_1, f_1, mesh->ystart, dump); //////////////////////////////////////////////////////////////////////////////////////// - // Test 3+4: Gaussian x-profiles, z-independent coefficients and compare with SPT method + // Test 3+4: Gaussian x-profiles, z-independent coefficients const Field2D a_3 = DC(a_1); const Field2D c_3 = DC(c_1); const Field2D d_3 = DC(d_1); const Field3D b_3 = forward_laplace(f_1, a_3, c_3, d_3); - check_laplace(++test_num, "with coefficients constant in z, PETSc 2nd order", *invert, + check_laplace(3, "with coefficients constant in z, PETSc 2nd order", *invert, INVERT_AC_GRAD, INVERT_AC_GRAD, a_3, c_3, d_3, b_3, f_1, mesh->ystart, dump); - Options* SPT_options = Options::getRoot()->getSection("SPT"); - auto invert_SPT = Laplacian::create(SPT_options); - - check_laplace(++test_num, "with coefficients constant in z, default solver", - *invert_SPT, INVERT_AC_GRAD, INVERT_AC_GRAD | INVERT_DC_GRAD, a_3, c_3, - d_3, b_3, f_1, mesh->ystart, dump); - ////////////////////////////////////////////// // Test 5: Cosine x-profiles, 2nd order Krylov Field3D f_5 = generate_f5(*mesh); @@ -176,16 +197,14 @@ int main(int argc, char** argv) { const Field3D b_5 = forward_laplace(f_5, a_5, c_5, d_5); - check_laplace(++test_num, "different profiles, PETSc 2nd order", *invert, - INVERT_AC_GRAD, INVERT_AC_GRAD, a_5, c_5, d_5, b_5, f_5, mesh->ystart, - dump); + check_laplace(5, "different profiles, PETSc 2nd order", *invert, INVERT_AC_GRAD, + INVERT_AC_GRAD, a_5, c_5, d_5, b_5, f_5, mesh->ystart, dump); ////////////////////////////////////////////// // Test 6: Cosine x-profiles, 4th order Krylov - check_laplace(++test_num, "different profiles, PETSc 4th order", *invert_4th, - INVERT_AC_GRAD, INVERT_AC_GRAD, a_5, c_5, d_5, b_5, f_5, mesh->ystart, - dump); + check_laplace(6, "different profiles, PETSc 4th order", *invert_4th, INVERT_AC_GRAD, + INVERT_AC_GRAD, a_5, c_5, d_5, b_5, f_5, mesh->ystart, dump); ////////////////////////////////////////////////////////////////////////////////////// // Test 7+8: Cosine x-profiles, z-independent coefficients and compare with SPT method @@ -195,16 +214,11 @@ int main(int argc, char** argv) { const Field2D d_7 = DC(d_5); const Field3D b_7 = forward_laplace(f_5, a_7, c_7, d_7); - check_laplace(++test_num, + check_laplace(7, "different profiles, with coefficients constant in z, PETSc 2nd order", *invert, INVERT_AC_GRAD, INVERT_AC_GRAD, a_7, c_7, d_7, b_7, f_5, mesh->ystart, dump); - check_laplace(++test_num, - "different profiles, with coefficients constant in z, default solver", - *invert_SPT, INVERT_AC_GRAD, INVERT_AC_GRAD | INVERT_DC_GRAD, a_7, c_7, - d_7, b_7, f_5, mesh->ystart, dump); - // Write and close the output file bout::writeDefaultOutputFile(dump); @@ -219,13 +233,11 @@ int main(int argc, char** argv) { BoutReal max_error_at_ystart(const Field3D& error) { const auto* mesh = error.getMesh(); - BoutReal local_max_error = error(mesh->xstart, mesh->ystart, 0); + BoutReal local_max_error = 0.0; for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { - if (local_max_error < error(jx, mesh->ystart, jz)) { - local_max_error = error(jx, mesh->ystart, jz); - } + local_max_error = std::max(local_max_error, error(jx, mesh->ystart, jz)); } } From ec6a97f8ebd6a0909939091b663825c97d396380 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 12:07:38 +0000 Subject: [PATCH 245/461] tests: Refactor petsc-laplace test - use field factory, rather than explicit loops --- tests/integrated/test-petsc_laplace/runtest | 52 +- .../test-petsc_laplace/test_petsc_laplace.cxx | 490 ++++-------------- 2 files changed, 135 insertions(+), 407 deletions(-) diff --git a/tests/integrated/test-petsc_laplace/runtest b/tests/integrated/test-petsc_laplace/runtest index 5496987128..b4379f7754 100755 --- a/tests/integrated/test-petsc_laplace/runtest +++ b/tests/integrated/test-petsc_laplace/runtest @@ -8,25 +8,21 @@ # requires: all_tests # cores: 4 -# Variables to compare -from __future__ import print_function -from builtins import str - -vars = [ - ("max_error1", 2.0e-4), - ("max_error2", 2.0e-4), - ("max_error3", 2.0e-4), - ("max_error5", 2.0e-4), - ("max_error6", 2.0e-5), - ("max_error7", 2.0e-4), -] -# tol = 1e-4 # Absolute (?) tolerance - from boututils.run_wrapper import build_and_log, shell, launch_safe -from boutdata.collect import collect - -# import numpy as np -from sys import stdout, exit +from boutdata.collect import collect, create_cache + +import pathlib +from sys import exit + +errors = [ + "max_error1", + "max_error2", + "max_error3", + "max_error5", + "max_error6", + "max_error7", +] +tol = 2e-4 # Absolute (?) tolerance build_and_log("PETSc Laplacian inversion test") @@ -34,29 +30,25 @@ print("Running PETSc Laplacian inversion test") success = True for nproc in [1, 2, 4]: - # nxpe = 1 - # if nproc > 2: - # nxpe = 2 - cmd = "./test_petsc_laplace" shell("rm data/BOUT.dmp.*.nc") - print(" %d processors...." % nproc) + print(f" {nproc} processors....") s, out = launch_safe(cmd, nproc=nproc, pipe=True, verbose=True) - f = open("run.log." + str(nproc), "w") - f.write(out) - f.close() + + pathlib.Path(f"run.log.{nproc}").write_text(out) + cache = create_cache(path="data", prefix="BOUT.dmp") # Collect output data - for varname, tol in vars: - stdout.write(" Checking " + varname + " ... ") - error = collect(varname, path="data", info=False) + for varname in errors: + print(f" Checking {varname} ... ", end="") + error = collect(varname, path="data", info=False, datafile_cache=cache) if error <= 0: print("Convergence error") success = False elif error > tol: - print("Fail, maximum error is = " + str(error)) + print(f"Fail, maximum error is = {error:e}") success = False else: print("Pass") diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index e869cd1b1d..96e7994637 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -31,19 +31,22 @@ #include "bout/difops.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" +#include "bout/field_factory.hxx" #include "bout/invert_laplace.hxx" #include "bout/options.hxx" #include "bout/options_io.hxx" #include "bout/output.hxx" #include "bout/traits.hxx" +#include "bout/vecops.hxx" -#include "fmt/core.h" +#include "fmt/format.h" #include #include #include #include +namespace { BoutReal max_error_at_ystart(const Field3D& error); void apply_flat_boundary(Field3D& bcoef); @@ -101,14 +104,69 @@ Field3D forward_laplace(const Field3D& field, const T& acoef, const T& ccoef, } Field3D generate_f1(const Mesh& mesh); -Field3D generate_a1(const Mesh& mesh); -Field3D generate_c1(const Mesh& mesh); -Field3D generate_d1(const Mesh& mesh); -Field3D generate_f5(const Mesh& mesh); -Field3D generate_a5(const Mesh& mesh); -Field3D generate_c5(const Mesh& mesh); -Field3D generate_d5(const Mesh& mesh); +Field3D generate_d1(Mesh& mesh) { + Options options{{"p_d1", 0.512547}, {"q_d1", 0.30908712}}; + + return FieldFactory::get()->create3D( + "1. + (0.2 * exp(-50. * (x - p_d1)^2 / 4.) * sin((z - 2. * pi * q_d1) * 3.))", + &options, &mesh); +} + +Field3D generate_c1(Mesh& mesh) { + Options options{{"p_c1", 0.18439023}, {"q_c1", 0.401089473}}; + + return FieldFactory::get()->create3D( + "1. + (0.15 * exp(-50. * (x - p_c1)^2 * 2.) * sin((z - 2. * pi * q_c1) * 2.))", + &options, &mesh); +} + +Field3D generate_a1(Mesh& mesh) { + Options options{{"p_a1", 0.612547}, {"q_a1", 0.30908712}}; + + return FieldFactory::get()->create3D( + "-1. + (0.1 * exp(-50. * (x - p_a1)^2 * 2.5) * sin((z - 2. * pi * q_a1) * 7.))", + &options, &mesh); +} + +Field3D generate_f5(Mesh& mesh) { + Options options{{"p_f5", 0.623901}, {"q_f5", 0.01209489}}; + + auto result = FieldFactory::get()->create3D( + "exp(-((50. * (x - p_f5)^2) + 1. - cos((z - 2. * pi * q_f5))))" + "- 50 * (2. * p_f5 * exp(-50. * (-p_f5)^2) * x" + " + (-p_f5 * exp(-50. * (-p_f5)^2) - (1 - p_f5) * exp(-50. * (1 - " + "p_f5)^2)) * x^2)" + " * exp(-(1. - cos(z - 2. * pi * q_f5)))", + &options, &mesh); + result.applyBoundary("neumann"); + checkData(result); + return result; +} + +Field3D generate_d5(Mesh& mesh) { + Options options{{"p_d5", 0.63298589}, {"q_d5", 0.889237890}}; + + return FieldFactory::get()->create3D( + "1. + (p_d5 * cos(2. * pi * x) * sin((z - 2 * pi * q_d5) * 3.))", &options, &mesh); +} + +Field3D generate_c5(Mesh& mesh) { + Options options{{"p_c5", 0.160983834}, {"q_c5", 0.73050121087}}; + + return FieldFactory::get()->create3D( + "1. + (p_c5 * cos(2. * pi * x * 5) * sin((z - 2 * pi * q_c5) * 2.))", &options, + &mesh); +} + +Field3D generate_a5(Mesh& mesh) { + Options options{{"p_a5", 0.5378950}, {"q_a5", 0.2805870}}; + + return FieldFactory::get()->create3D( + "-1. + (p_a5 * cos(2. * pi * x * 2.) * sin((z - 2. * pi * q_a5) * 7.))", &options, + &mesh); +} +} // namespace int main(int argc, char** argv) { @@ -151,6 +209,25 @@ int main(int argc, char** argv) { // Solving equations of the form d*Delp2(f) + 1/c*Grad_perp(c).Grad_perp(f) + a*f = b for various f, a, c, d using bout::globals::mesh; + Field3D loop_x; + Field3D loop_z; + loop_x.allocate(); + loop_z.allocate(); + for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { + const BoutReal x = mesh->GlobalX(jx); + for (int jy = 0; jy < mesh->LocalNy; jy++) { + for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { + const BoutReal z = mesh->GlobalZ(jz); + loop_x(jx, jy, jz) = x; + loop_z(jx, jy, jz) = z; + } + } + } + dump["loop_x"] = loop_x; + dump["loop_z"] = loop_z; + dump["exp_x"] = FieldFactory::get()->create3D("x"); + dump["exp_z"] = FieldFactory::get()->create3D("z"); + // Only Neumann x-boundary conditions are implemented so far, so test functions should be Neumann in x and periodic in z. // Use Field3D's, but solver only works on FieldPerp slices, so only use 1 y-point @@ -231,6 +308,7 @@ int main(int argc, char** argv) { return 0; } +namespace { BoutReal max_error_at_ystart(const Field3D& error) { const auto* mesh = error.getMesh(); BoutReal local_max_error = 0.0; @@ -271,234 +349,23 @@ void apply_flat_boundary(Field3D& bcoef) { } Field3D generate_f1(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - constexpr BoutReal p = 0.39503274; // NOLINT constexpr BoutReal q = 0.20974396; // NOLINT Field3D result; result.allocate(); for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - //make the gradients zero at both x-boundaries - result(jx, jy, jz) = 0. - + exp(-(100. * pow(x - p, 2) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-100. * pow(-p, 2)) * x - + (-p * exp(-100. * pow(-p, 2)) - - (1 - p) * exp(-100. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - //make the gradients zero at both x-boundaries - result(jx, jy, jz) = 0. - + exp(-(60. * pow(x - p, 2) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-60. * pow(-p, 2)) * x - + (-p * exp(-60. * pow(-p, 2)) - - (1 - p) * exp(-60. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - //make the gradients zero at both x-boundaries - result(jx, jy, jz) = 0. - + exp(-(60. * pow(x - p, 2) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-60. * pow(-p, 2)) * x - + (-p * exp(-60. * pow(-p, 2)) - - (1 - p) * exp(-60. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); - } - } - } - } - - checkData(result); - result.applyBoundary("neumann"); - return result; -} - -Field3D generate_d1(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - - constexpr BoutReal p = 0.512547; // NOLINT - constexpr BoutReal q = 0.30908712; // NOLINT - Field3D result; - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - 1. + 0.2 * exp(-50. * pow(x - p, 2) / 4.) * sin(2. * PI * (z - q) * 3.); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - 1. + 0.2 * exp(-50. * pow(x - p, 2) / 4.) * sin(2. * PI * (z - q) * 3.); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - 1. + 0.2 * exp(-50. * pow(x - p, 2) / 4.) * sin(2. * PI * (z - q) * 3.); - } - } - } - } - checkData(result); - return result; -} - -Field3D generate_c1(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - - constexpr BoutReal p = 0.18439023; // NOLINT - constexpr BoutReal q = 0.401089473; // NOLINT - Field3D result; - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - 1. + 0.15 * exp(-50. * pow(x - p, 2) * 2.) * sin(2. * PI * (z - q) * 2.); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - 1. + 0.15 * exp(-50. * pow(x - p, 2) * 2.) * sin(2. * PI * (z - q) * 2.); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - 1. + 0.15 * exp(-50. * pow(x - p, 2) * 2.) * sin(2. * PI * (z - q) * 2.); - } - } - } - } - - checkData(result); - return result; -} - -Field3D generate_a1(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - - constexpr BoutReal p = 0.612547; // NOLINT - constexpr BoutReal q = 0.30908712; // NOLINT - Field3D result; - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; + const BoutReal x = mesh.GlobalX(jx); for (int jy = 0; jy < mesh.LocalNy; jy++) { for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - -1. + 0.1 * exp(-50. * pow(x - p, 2) * 2.5) * sin(2. * PI * (z - q) * 7.); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - -1. + 0.1 * exp(-50. * pow(x - p, 2) * 2.5) * sin(2. * PI * (z - q) * 7.); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - -1. + 0.1 * exp(-50. * pow(x - p, 2) * 2.5) * sin(2. * PI * (z - q) * 7.); - } - } - } - } - - checkData(result); - return result; -} - -Field3D generate_f5(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - constexpr BoutReal p = 0.623901; // NOLINT - constexpr BoutReal q = 0.01209489; // NOLINT - Field3D result; - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; + const BoutReal z = mesh.GlobalZ(jz); //make the gradients zero at both x-boundaries result(jx, jy, jz) = - 0. + exp(-(50. * pow(x - p, 2) + 1. - cos(2. * PI * (z - q)))) + 0. + exp(-((100. * pow(x - p, 2)) + 1. - cos(2. * PI * (z - q)))) - 50. - * (2. * p * exp(-50. * pow(-p, 2)) * x - + (-p * exp(-50. * pow(-p, 2)) - (1 - p) * exp(-50. * pow(1 - p, 2))) + * (2. * p * exp(-100. * pow(-p, 2)) * x + + (-p * exp(-100. * pow(-p, 2)) + - (1 - p) * exp(-100. * pow(1 - p, 2))) * pow(x, 2)) * exp(-(1. - cos(2. * PI * (z - q)))); } @@ -506,176 +373,45 @@ Field3D generate_f5(const Mesh& mesh) { } if (mesh.firstX()) { for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - //make the gradients zero at both x-boundaries - result(jx, jy, jz) = 0. - + exp(-(50. * pow(x - p, 2) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-50. * pow(-p, 2)) * x - + (-p * exp(-50. * pow(-p, 2)) - - (1 - p) * exp(-50. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; + const BoutReal x = mesh.GlobalX(jx); for (int jy = 0; jy < mesh.LocalNy; jy++) { for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; + const BoutReal z = mesh.GlobalZ(jz); //make the gradients zero at both x-boundaries - result(jx, jy, jz) = 0. - + exp(-(50. * pow(x - p, 2) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-50. * pow(-p, 2)) * x - + (-p * exp(-50. * pow(-p, 2)) - - (1 - p) * exp(-50. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); - } - } - } - } - result.applyBoundary("neumann"); - checkData(result); - return result; -} - -Field3D generate_d5(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - constexpr BoutReal p = 0.63298589; // NOLINT - constexpr BoutReal q = 0.889237890; // NOLINT - Field3D result; - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = 1. + p * cos(2. * PI * x) * sin(2. * PI * (z - q) * 3.); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = 1. + p * cos(2. * PI * x) * sin(2. * PI * (z - q) * 3.); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = 1. + p * cos(2. * PI * x) * sin(2. * PI * (z - q) * 3.); - } - } - } - } - checkData(result); - return result; -} - -Field3D generate_c5(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - constexpr BoutReal p = 0.160983834; // NOLINT - constexpr BoutReal q = 0.73050121087; // NOLINT - - Field3D result; - - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = 1. + p * cos(2. * PI * x * 5) * sin(2. * PI * (z - q) * 2.); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; result(jx, jy, jz) = - 1. + p * cos(2. * PI * x * 5) * sin(2. * PI * (z - q) * 2.); + 0. + exp(-((60. * pow(x - p, 2)) + 1. - cos(2. * PI * (z - q)))) + - 50. + * (2. * p * exp(-60. * pow(-p, 2)) * x + + (-p * exp(-60. * pow(-p, 2)) + - (1 - p) * exp(-60. * pow(1 - p, 2))) + * pow(x, 2)) + * exp(-(1. - cos(2. * PI * (z - q)))); } } } } if (mesh.lastX()) { for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; + const BoutReal x = mesh.GlobalX(jx); for (int jy = 0; jy < mesh.LocalNy; jy++) { for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; + const BoutReal z = mesh.GlobalZ(jz); + //make the gradients zero at both x-boundaries result(jx, jy, jz) = - 1. + p * cos(2. * PI * x * 5) * sin(2. * PI * (z - q) * 2.); + 0. + exp(-((60. * pow(x - p, 2)) + 1. - cos(2. * PI * (z - q)))) + - 50. + * (2. * p * exp(-60. * pow(-p, 2)) * x + + (-p * exp(-60. * pow(-p, 2)) + - (1 - p) * exp(-60. * pow(1 - p, 2))) + * pow(x, 2)) + * exp(-(1. - cos(2. * PI * (z - q)))); } } } } - checkData(result); - return result; -} -Field3D generate_a5(const Mesh& mesh) { - const BoutReal nx = mesh.GlobalNx - 2 * mesh.xstart - 1; - const BoutReal nz = mesh.GlobalNz; - constexpr BoutReal p = 0.5378950; // NOLINT - constexpr BoutReal q = 0.2805870; // NOLINT - Field3D result; - result.allocate(); - for (int jx = mesh.xstart; jx <= mesh.xend; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); - } - } - } - if (mesh.firstX()) { - for (int jx = mesh.xstart - 1; jx >= 0; jx--) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); - } - } - } - } - if (mesh.lastX()) { - for (int jx = mesh.xend + 1; jx < mesh.LocalNx; jx++) { - const BoutReal x = BoutReal(mesh.getGlobalXIndex(jx) - mesh.xstart) / nx; - for (int jy = 0; jy < mesh.LocalNy; jy++) { - for (int jz = mesh.zstart; jz <= mesh.zend; jz++) { - const BoutReal z = BoutReal(jz) / nz; - result(jx, jy, jz) = - -1. + p * cos(2. * PI * x * 2.) * sin(2. * PI * (z - q) * 7.); - } - } - } - } checkData(result); + result.applyBoundary("neumann"); return result; } +} // namespace From 6e0ac334eea7efb1568d0cf20e3eddf0cd9f1098 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 13:18:11 +0000 Subject: [PATCH 246/461] Reduce some duplicated loops in `LaplacePetsc` ctor --- .../laplace/impls/petsc/petsc_laplace.cxx | 142 ++++++------------ 1 file changed, 43 insertions(+), 99 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index c6a6712f4d..d9076dc6a5 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -132,6 +132,9 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, MatSetSizes(MatA, localN, localN, size, size); MatSetFromOptions(MatA); + const bool first_x = localmesh->firstX(); + const bool last_x = localmesh->lastX(); + /* Pre allocate memory * nnz denotes an array containing the number of non-zeros in the various rows * for @@ -139,109 +142,49 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, * o_nnz - The off-diagonal terms in the matrix (needed when running in * parallel) */ - PetscInt *d_nnz, *o_nnz; + PetscInt *d_nnz = nullptr; + PetscInt *o_nnz = nullptr; PetscMalloc((localN) * sizeof(PetscInt), &d_nnz); PetscMalloc((localN) * sizeof(PetscInt), &o_nnz); if (fourth_order) { - // first and last 2*localmesh-LocalNz entries are the edge x-values that (may) have 'off-diagonal' components (i.e. on another processor) - if (localmesh->firstX() && localmesh->lastX()) { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - } else if (localmesh->firstX()) { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 10; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 5; - } - } else if (localmesh->lastX()) { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 10; - o_nnz[localN - 1 - i] = 0; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 5; - o_nnz[localN - 1 - i] = 0; - } - } else { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = 10; - o_nnz[localN - 1 - i] = 10; - } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = 5; - o_nnz[localN - 1 - i] = 5; - } + // first and last 2*localmesh-LocalNz entries are the edge x-values that + // (may) have 'off-diagonal' components (i.e. on another processor) + + const int first_first_off = first_x ? 0 : 10; + const int first_last_off = last_x ? 0 : 10; + const int second_first_off = first_x ? 0 : 5; + const int second_last_off = last_x ? 0 : 5; + + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { + d_nnz[i] = 15; + d_nnz[localN - 1 - i] = 15; + o_nnz[i] = first_first_off; + o_nnz[localN - 1 - i] = first_last_off; + } + for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { + d_nnz[i] = 20; + d_nnz[localN - 1 - i] = 20; + o_nnz[i] = second_first_off; + o_nnz[localN - 1 - i] = second_last_off; } - for (int i = 2 * (localmesh->LocalNz); i < localN - 2 * ((localmesh->LocalNz)); i++) { + for (int i = 2 * (localmesh->LocalNz); i < localN - (2 * localmesh->LocalNz); i++) { d_nnz[i] = 25; d_nnz[localN - 1 - i] = 25; o_nnz[i] = 0; o_nnz[localN - 1 - i] = 0; } - - // Use d_nnz and o_nnz for preallocating the matrix - if (localmesh->firstX() && localmesh->lastX()) { - // Only one processor in X - MatSeqAIJSetPreallocation(MatA, 0, d_nnz); - } else { - MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); - } } else { - // first and last localmesh->LocalNz entries are the edge x-values that (may) have 'off-diagonal' components (i.e. on another processor) - if (localmesh->firstX() && localmesh->lastX()) { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - } else if (localmesh->firstX()) { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 3; - } - } else if (localmesh->lastX()) { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 3; - o_nnz[localN - 1 - i] = 0; - } - } else { - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = 3; - o_nnz[localN - 1 - i] = 3; - } + // first and last localmesh->LocalNz entries are the edge x-values that + // (may) have 'off-diagonal' components (i.e. on another processor) + const int first_off = first_x ? 0 : 3; + const int last_off = last_x ? 0 : 3; + + for (int i = localmesh->zstart; i <= localmesh->zend; i++) { + d_nnz[i] = 6; + d_nnz[localN - 1 - i] = 6; + o_nnz[i] = first_off; + o_nnz[localN - 1 - i] = last_off; } for (int i = localmesh->LocalNz; i < localN - (localmesh->LocalNz); i++) { @@ -250,14 +193,15 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, o_nnz[i] = 0; o_nnz[localN - 1 - i] = 0; } - - // Use d_nnz and o_nnz for preallocating the matrix - if (localmesh->firstX() && localmesh->lastX()) { - MatSeqAIJSetPreallocation(MatA, 0, d_nnz); - } else { - MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); - } } + // Use d_nnz and o_nnz for preallocating the matrix + if (localmesh->firstX() && localmesh->lastX()) { + // Only one processor in X + MatSeqAIJSetPreallocation(MatA, 0, d_nnz); + } else { + MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); + } + // Free the d_nnz and o_nnz arrays, as these are will not be used anymore PetscFree(d_nnz); PetscFree(o_nnz); From a5e3f366a79bec2ee1dc3dc8744d818b177c0790 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 13:45:33 +0000 Subject: [PATCH 247/461] Use `std::vector` instead of `PetscMalloc` --- .../laplace/impls/petsc/petsc_laplace.cxx | 31 ++++++++----------- 1 file changed, 13 insertions(+), 18 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index d9076dc6a5..1f422ad19d 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -38,6 +38,8 @@ #include #include +#include + #define KSP_RICHARDSON "richardson" #define KSP_CHEBYSHEV "chebyshev" #define KSP_CG "cg" @@ -134,18 +136,14 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, const bool first_x = localmesh->firstX(); const bool last_x = localmesh->lastX(); + // Pre allocate memory. See MatMPIAIJSetPreallocation docs for more info + // Number of non-zero elements in each row of matrix owned by this processor + // ("diagonal submatrices") + std::vector d_nnz(localN, 0); + // Number of non-zero elements in each row of matrix owned by other processors + // ("offdiagonal submatrices") + std::vector o_nnz(localN, 0); - /* Pre allocate memory - * nnz denotes an array containing the number of non-zeros in the various rows - * for - * d_nnz - The diagonal terms in the matrix - * o_nnz - The off-diagonal terms in the matrix (needed when running in - * parallel) - */ - PetscInt *d_nnz = nullptr; - PetscInt *o_nnz = nullptr; - PetscMalloc((localN) * sizeof(PetscInt), &d_nnz); - PetscMalloc((localN) * sizeof(PetscInt), &o_nnz); if (fourth_order) { // first and last 2*localmesh-LocalNz entries are the edge x-values that // (may) have 'off-diagonal' components (i.e. on another processor) @@ -194,17 +192,14 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, o_nnz[localN - 1 - i] = 0; } } - // Use d_nnz and o_nnz for preallocating the matrix - if (localmesh->firstX() && localmesh->lastX()) { + + if (first_x and last_x) { // Only one processor in X - MatSeqAIJSetPreallocation(MatA, 0, d_nnz); + MatSeqAIJSetPreallocation(MatA, 0, d_nnz.data()); } else { - MatMPIAIJSetPreallocation(MatA, 0, d_nnz, 0, o_nnz); + MatMPIAIJSetPreallocation(MatA, 0, d_nnz.data(), 0, o_nnz.data()); } - // Free the d_nnz and o_nnz arrays, as these are will not be used anymore - PetscFree(d_nnz); - PetscFree(o_nnz); // Sets up the internal matrix data structures for the later use. MatSetUp(MatA); From 0aa79db9bbe334c1c17f5451260612a0bd6c2612 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 13:51:24 +0000 Subject: [PATCH 248/461] Set member variables in `LaplacePetsc` initialiser --- .clang-tidy | 2 +- .../laplace/impls/petsc/petsc_laplace.cxx | 102 +++++++----------- .../laplace/impls/petsc/petsc_laplace.hxx | 18 ++-- 3 files changed, 52 insertions(+), 70 deletions(-) diff --git a/.clang-tidy b/.clang-tidy index cf29b759cd..121c3f3a60 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -20,7 +20,7 @@ CheckOptions: value: 'MPI_Comm' - key: misc-include-cleaner.IgnoreHeaders - value: 'adios2/.*;bits/.*;cpptrace/.*' + value: 'adios2/.*;bits/.*;cpptrace/.*;petsc.*\.h' --- Disabled checks and reasons: diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 1f422ad19d..878d18b4f3 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -31,7 +31,10 @@ #include "petsc_laplace.hxx" #include +#include #include +#include +#include #include #include #include @@ -53,21 +56,43 @@ #define KSP_BICG "bicg" #define KSP_PREONLY "preonly" -static PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { +namespace { +PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { PetscFunctionBegin; // NOLINT LaplacePetsc* laplace = nullptr; - const int ierr = PCShellGetContext(pc, reinterpret_cast(&laplace)); // NOLINT - CHKERRQ(ierr); + CHKERRQ(PCShellGetContext(pc, reinterpret_cast(&laplace))); // NOLINT PetscFunctionReturn(laplace->precon(x, y)); // NOLINT } +} // namespace LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, - Solver* UNUSED(solver)) + [[maybe_unused]] Solver* solver) : Laplacian(opt, loc, mesh_in), A(0.0), C1(1.0), C2(1.0), D(1.0), Ex(0.0), Ez(0.0), - issetD(false), issetC(false), issetE(false), - lib(opt == nullptr ? &(Options::root()["laplace"]) : opt) { + issetD(false), issetC(false), issetE(false), comm(localmesh->getXcomm()), + opts(opt == nullptr ? &(Options::root()["laplace"]) : opt), + // WARNING: only a few of these options actually make sense: see the + // PETSc documentation to work out which they are (possibly + // pbjacobi, sor might be useful choices?) + ksptype((*opts)["ksptype"].doc("KSP solver type").withDefault(KSPGMRES)), + pctype((*opts)["pctype"] + .doc("Preconditioner type. See the PETSc documentation for options") + .withDefault("none")), + richardson_damping_factor((*opts)["richardson_damping_factor"].withDefault(1.0)), + chebyshev_max((*opts)["chebyshev_max"].withDefault(100)), + chebyshev_min((*opts)["chebyshev_min"].withDefault(0.01)), + gmres_max_steps((*opts)["gmres_max_steps"].withDefault(30)), + rtol((*opts)["rtol"].doc("Relative tolerance for KSP solver").withDefault(1e-5)), + atol((*opts)["atol"].doc("Absolute tolerance for KSP solver").withDefault(1e-50)), + dtol((*opts)["dtol"].doc("Divergence tolerance for KSP solver").withDefault(1e5)), + maxits( + (*opts)["maxits"].doc("Maximum number of KSP iterations").withDefault(100000)), + direct((*opts)["direct"].doc("Use direct (LU) solver?").withDefault(false)), + fourth_order( + (*opts)["fourth_order"].doc("Use fourth order stencil").withDefault(false)), + lib(opts) { + A.setLocation(location); C1.setLocation(location); C2.setLocation(location); @@ -75,13 +100,6 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, Ex.setLocation(location); Ez.setLocation(location); - // Get Options in Laplace Section - if (!opt) { - opts = Options::getRoot()->getSection("laplace"); - } else { - opts = opt; - } - #if CHECK > 0 // Checking flags are set to something which is not implemented checkFlags(); @@ -93,24 +111,23 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, } #endif - // Get communicator for group of processors in X - all points in z-x plane for fixed y. - comm = localmesh->getXcomm(); + const bool first_x = localmesh->firstX(); + const bool last_x = localmesh->lastX(); // Need to determine local size to use based on prior parallelisation // Coefficient values are stored only on local processors. localN = (localmesh->xend - localmesh->xstart + 1) * (localmesh->LocalNz); - if (localmesh->firstX()) { - localN += - localmesh->xstart - * (localmesh->LocalNz); // If on first processor add on width of boundary region + if (first_x) { + // If on first processor add on width of boundary region + localN += localmesh->xstart * (localmesh->LocalNz); } - if (localmesh->lastX()) { - localN += - localmesh->xstart - * (localmesh->LocalNz); // If on last processor add on width of boundary region + if (last_x) { + // If on last processor add on width of boundary region + localN += localmesh->xstart * (localmesh->LocalNz); } // Calculate 'size' (the total number of points in physical grid) + size = localN; if (bout::globals::mpi->MPI_Allreduce(&localN, &size, 1, MPI_INT, MPI_SUM, comm) != MPI_SUCCESS) { throw BoutException("Error in MPI_Allreduce during LaplacePetsc initialisation"); @@ -126,16 +143,11 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, VecSetFromOptions(xs); VecDuplicate(xs, &bs); - // Get 4th order solver switch - opts->get("fourth_order", fourth_order, false); - // Set size of (the PETSc) Matrix on each processor to localN x localN MatCreate(comm, &MatA); MatSetSizes(MatA, localN, localN, size, size); MatSetFromOptions(MatA); - const bool first_x = localmesh->firstX(); - const bool last_x = localmesh->lastX(); // Pre allocate memory. See MatMPIAIJSetPreallocation docs for more info // Number of non-zero elements in each row of matrix owned by this processor // ("diagonal submatrices") @@ -206,53 +218,21 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, // Declare KSP Context (abstract PETSc object that manages all Krylov methods) KSPCreate(comm, &ksp); - // Get KSP Solver Type (Generalizes Minimal RESidual is the default) - ksptype = (*opts)["ksptype"].doc("KSP solver type").withDefault(KSP_GMRES); - - // Get preconditioner type - // WARNING: only a few of these options actually make sense: see the - // PETSc documentation to work out which they are (possibly - // pbjacobi, sor might be useful choices?) - pctype = (*opts)["pctype"] - .doc("Preconditioner type. See the PETSc documentation for options") - .withDefault("none"); - // Let "user" be a synonym for "shell" if (pctype == "user") { pctype = PCSHELL; } - // Get Options specific to particular solver types - opts->get("richardson_damping_factor", richardson_damping_factor, 1.0, true); - opts->get("chebyshev_max", chebyshev_max, 100, true); - opts->get("chebyshev_min", chebyshev_min, 0.01, true); - opts->get("gmres_max_steps", gmres_max_steps, 30, true); - - // Get Tolerances for KSP solver - rtol = (*opts)["rtol"].doc("Relative tolerance for KSP solver").withDefault(1e-5); - atol = (*opts)["atol"].doc("Absolute tolerance for KSP solver").withDefault(1e-50); - dtol = (*opts)["dtol"].doc("Divergence tolerance for KSP solver").withDefault(1e5); - maxits = (*opts)["maxits"].doc("Maximum number of KSP iterations").withDefault(100000); - - // Get direct solver switch - direct = (*opts)["direct"].doc("Use direct (LU) solver?").withDefault(false); if (direct) { - output << endl - << "Using LU decompostion for direct solution of system" << endl - << endl; + output.write("\nUsing LU decompostion for direct solution of system\n"); } if (pctype == PCSHELL) { - rightprec = (*opts)["rightprec"].doc("Right preconditioning?").withDefault(true); // Options for preconditioner are in a subsection pcsolve = Laplacian::create(opts->getSection("precon")); } - - // Ensure that the matrix is constructed first time - // coefchanged = true; - // lastflag = -1; } FieldPerp LaplacePetsc::solve(const FieldPerp& b) { return solve(b, b); } diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.hxx b/src/invert/laplace/impls/petsc/petsc_laplace.hxx index 1d56abd00b..611bfd6fa1 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.hxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.hxx @@ -220,17 +220,19 @@ private: FieldPerp sol; // solution Field - // Istart is the first row of MatA owned by the process, Iend is 1 greater than the last row. - int Istart, Iend; + /// Istart is the first row of MatA owned by the process, Iend is 1 greater than the last row. + int Istart = -1; + int Iend = -1; - int meshx, meshz, size, - localN; // Mesh sizes, total size, no of points on this processor + /// Mesh sizes, total size, no of points on this processor + int meshx, meshz, size, localN; MPI_Comm comm; - Mat MatA; - Vec xs, bs; // Solution and RHS vectors - KSP ksp; + Mat MatA = nullptr; ///< Stencil matrix + Vec xs = nullptr; ///< Solution vector + Vec bs = nullptr; ///< RHS vector + KSP ksp = nullptr; ///< PETSc solver - Options* opts; // Laplace Section Options Object + Options* opts; ///< Laplace Section Options Object std::string ksptype; ///< KSP solver type std::string pctype; ///< Preconditioner type From 3d34d0cafc5b296734c863237255afa78bab0617 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 14:02:04 +0000 Subject: [PATCH 249/461] Prepare `LaplacePetsc` for Z guards --- .../laplace/impls/petsc/petsc_laplace.cxx | 33 +++++++++++-------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 878d18b4f3..673f9c0df5 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -116,14 +116,15 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, // Need to determine local size to use based on prior parallelisation // Coefficient values are stored only on local processors. - localN = (localmesh->xend - localmesh->xstart + 1) * (localmesh->LocalNz); + const auto local_nz = localmesh->zend - localmesh->zstart + 1; + localN = (localmesh->xend - localmesh->xstart + 1) * local_nz; if (first_x) { // If on first processor add on width of boundary region - localN += localmesh->xstart * (localmesh->LocalNz); + localN += localmesh->xstart * local_nz; } if (last_x) { // If on last processor add on width of boundary region - localN += localmesh->xstart * (localmesh->LocalNz); + localN += localmesh->xstart * local_nz; } // Calculate 'size' (the total number of points in physical grid) @@ -134,7 +135,7 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, } // Calculate total (physical) grid dimensions - meshz = localmesh->LocalNz; + meshz = local_nz; meshx = size / meshz; // Create PETSc type of vectors for the solution and the RHS vector @@ -165,39 +166,39 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, const int second_first_off = first_x ? 0 : 5; const int second_last_off = last_x ? 0 : 5; - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { + for (int i = 0; i <= local_nz; i++) { d_nnz[i] = 15; d_nnz[localN - 1 - i] = 15; o_nnz[i] = first_first_off; o_nnz[localN - 1 - i] = first_last_off; } - for (int i = (localmesh->LocalNz); i < 2 * (localmesh->LocalNz); i++) { + for (int i = local_nz; i < 2 * local_nz; i++) { d_nnz[i] = 20; d_nnz[localN - 1 - i] = 20; o_nnz[i] = second_first_off; o_nnz[localN - 1 - i] = second_last_off; } - for (int i = 2 * (localmesh->LocalNz); i < localN - (2 * localmesh->LocalNz); i++) { + for (int i = 2 * local_nz; i < localN - (2 * local_nz); i++) { d_nnz[i] = 25; d_nnz[localN - 1 - i] = 25; o_nnz[i] = 0; o_nnz[localN - 1 - i] = 0; } } else { - // first and last localmesh->LocalNz entries are the edge x-values that + // first and last local_nz entries are the edge x-values that // (may) have 'off-diagonal' components (i.e. on another processor) const int first_off = first_x ? 0 : 3; const int last_off = last_x ? 0 : 3; - for (int i = localmesh->zstart; i <= localmesh->zend; i++) { + for (int i = 0; i <= local_nz; i++) { d_nnz[i] = 6; d_nnz[localN - 1 - i] = 6; o_nnz[i] = first_off; o_nnz[localN - 1 - i] = last_off; } - for (int i = localmesh->LocalNz; i < localN - (localmesh->LocalNz); i++) { + for (int i = local_nz; i < localN - local_nz; i++) { d_nnz[i] = 9; d_nnz[localN - 1 - i] = 9; o_nnz[i] = 0; @@ -639,7 +640,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { } if (i != Iend) { - throw BoutException("Petsc index sanity check failed"); + throw BoutException("Petsc index sanity check failed: i={} != Iend={}", i, Iend); } // Assemble Matrix @@ -809,9 +810,13 @@ void LaplacePetsc::Element(int i, int x, int z, int xshift, int zshift, PetscSca // Need to convert LOCAL x to GLOBAL x in order to correctly calculate // PETSC Matrix Index. int xoffset = Istart / meshz; - if (Istart % meshz != 0) { - throw BoutException("Petsc index sanity check 3 failed"); +#if CHECK > 2 + const int rem = Istart % meshz; + if (rem != 0) { + throw BoutException("Petsc index sanity check 3 failed: Istart={} % meshz={} == {}", + Istart, meshz, rem); } +#endif // Calculate the row to be set int row_new = x + xshift; // should never be out of range. @@ -820,7 +825,7 @@ void LaplacePetsc::Element(int i, int x, int z, int xshift, int zshift, PetscSca } // Calculate the column to be set - int col_new = z + zshift; + int col_new = z + zshift - localmesh->zstart; if (col_new < 0) { col_new += meshz; } else if (col_new > meshz - 1) { From df99cdba5963dd293ff0cfb892630e856db938ea Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 14:53:28 +0000 Subject: [PATCH 250/461] Apply clang-tidy fixes --- .../test-petsc_laplace/test_petsc_laplace.cxx | 48 +++++++++---------- 1 file changed, 23 insertions(+), 25 deletions(-) diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index 96e7994637..902156f4c0 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -32,6 +32,7 @@ #include "bout/field2d.hxx" #include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" #include "bout/invert_laplace.hxx" #include "bout/options.hxx" #include "bout/options_io.hxx" @@ -106,7 +107,7 @@ Field3D forward_laplace(const Field3D& field, const T& acoef, const T& ccoef, Field3D generate_f1(const Mesh& mesh); Field3D generate_d1(Mesh& mesh) { - Options options{{"p_d1", 0.512547}, {"q_d1", 0.30908712}}; + const Options options{{"p_d1", 0.512547}, {"q_d1", 0.30908712}}; return FieldFactory::get()->create3D( "1. + (0.2 * exp(-50. * (x - p_d1)^2 / 4.) * sin((z - 2. * pi * q_d1) * 3.))", @@ -114,7 +115,7 @@ Field3D generate_d1(Mesh& mesh) { } Field3D generate_c1(Mesh& mesh) { - Options options{{"p_c1", 0.18439023}, {"q_c1", 0.401089473}}; + const Options options{{"p_c1", 0.18439023}, {"q_c1", 0.401089473}}; return FieldFactory::get()->create3D( "1. + (0.15 * exp(-50. * (x - p_c1)^2 * 2.) * sin((z - 2. * pi * q_c1) * 2.))", @@ -122,7 +123,7 @@ Field3D generate_c1(Mesh& mesh) { } Field3D generate_a1(Mesh& mesh) { - Options options{{"p_a1", 0.612547}, {"q_a1", 0.30908712}}; + const Options options{{"p_a1", 0.612547}, {"q_a1", 0.30908712}}; return FieldFactory::get()->create3D( "-1. + (0.1 * exp(-50. * (x - p_a1)^2 * 2.5) * sin((z - 2. * pi * q_a1) * 7.))", @@ -130,7 +131,7 @@ Field3D generate_a1(Mesh& mesh) { } Field3D generate_f5(Mesh& mesh) { - Options options{{"p_f5", 0.623901}, {"q_f5", 0.01209489}}; + const Options options{{"p_f5", 0.623901}, {"q_f5", 0.01209489}}; auto result = FieldFactory::get()->create3D( "exp(-((50. * (x - p_f5)^2) + 1. - cos((z - 2. * pi * q_f5))))" @@ -145,14 +146,14 @@ Field3D generate_f5(Mesh& mesh) { } Field3D generate_d5(Mesh& mesh) { - Options options{{"p_d5", 0.63298589}, {"q_d5", 0.889237890}}; + const Options options{{"p_d5", 0.63298589}, {"q_d5", 0.889237890}}; return FieldFactory::get()->create3D( "1. + (p_d5 * cos(2. * pi * x) * sin((z - 2 * pi * q_d5) * 3.))", &options, &mesh); } Field3D generate_c5(Mesh& mesh) { - Options options{{"p_c5", 0.160983834}, {"q_c5", 0.73050121087}}; + const Options options{{"p_c5", 0.160983834}, {"q_c5", 0.73050121087}}; return FieldFactory::get()->create3D( "1. + (p_c5 * cos(2. * pi * x * 5) * sin((z - 2 * pi * q_c5) * 2.))", &options, @@ -160,7 +161,7 @@ Field3D generate_c5(Mesh& mesh) { } Field3D generate_a5(Mesh& mesh) { - Options options{{"p_a5", 0.5378950}, {"q_a5", 0.2805870}}; + const Options options{{"p_a5", 0.5378950}, {"q_a5", 0.2805870}}; return FieldFactory::get()->create3D( "-1. + (p_a5 * cos(2. * pi * x * 2.) * sin((z - 2. * pi * q_a5) * 7.))", &options, @@ -362,12 +363,11 @@ Field3D generate_f1(const Mesh& mesh) { //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. + exp(-((100. * pow(x - p, 2)) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-100. * pow(-p, 2)) * x - + (-p * exp(-100. * pow(-p, 2)) - - (1 - p) * exp(-100. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); + - (50. + * (2. * p * exp(-100. * pow(-p, 2)) * x + + (-p * exp(-100. * pow(-p, 2)) - (1 - p) * exp(-100. * pow(1 - p, 2))) + * pow(x, 2)) + * exp(-(1. - cos(2. * PI * (z - q))))); } } } @@ -380,12 +380,11 @@ Field3D generate_f1(const Mesh& mesh) { //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. + exp(-((60. * pow(x - p, 2)) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-60. * pow(-p, 2)) * x - + (-p * exp(-60. * pow(-p, 2)) - - (1 - p) * exp(-60. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); + - (50. + * (2. * p * exp(-60. * pow(-p, 2)) * x + + (-p * exp(-60. * pow(-p, 2)) - (1 - p) * exp(-60. * pow(1 - p, 2))) + * pow(x, 2)) + * exp(-(1. - cos(2. * PI * (z - q))))); } } } @@ -399,12 +398,11 @@ Field3D generate_f1(const Mesh& mesh) { //make the gradients zero at both x-boundaries result(jx, jy, jz) = 0. + exp(-((60. * pow(x - p, 2)) + 1. - cos(2. * PI * (z - q)))) - - 50. - * (2. * p * exp(-60. * pow(-p, 2)) * x - + (-p * exp(-60. * pow(-p, 2)) - - (1 - p) * exp(-60. * pow(1 - p, 2))) - * pow(x, 2)) - * exp(-(1. - cos(2. * PI * (z - q)))); + - (50. + * (2. * p * exp(-60. * pow(-p, 2)) * x + + (-p * exp(-60. * pow(-p, 2)) - (1 - p) * exp(-60. * pow(1 - p, 2))) + * pow(x, 2)) + * exp(-(1. - cos(2. * PI * (z - q))))); } } } From 3ca3e689f2fedb8b4979998849904890258e6405 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 15:01:51 +0000 Subject: [PATCH 251/461] Try `sor` preconditioner for 3D metrics in test-petsc-laplace --- tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index 902156f4c0..d4f375e1d7 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -186,7 +186,7 @@ int main(int argc, char** argv) { #endif // Preconditioner to use for 3D metrics - constexpr auto petsc_pc = petsc_has_mumps ? "mumps" : "lu"; + constexpr auto petsc_pc = petsc_has_mumps ? "mumps" : "sor"; auto& options_2nd = Options::root()["petsc2nd"]; if constexpr (bout::build::use_metric_3d) { From 63af212ca1d2bec365c11f74f0825770f15823e3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 25 Feb 2026 10:19:57 +0100 Subject: [PATCH 252/461] Remove v prefix from version for wheels --- tools/pylib/_boutpp_build/backend.py | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/tools/pylib/_boutpp_build/backend.py b/tools/pylib/_boutpp_build/backend.py index 9e305b3048..922ef1c48c 100755 --- a/tools/pylib/_boutpp_build/backend.py +++ b/tools/pylib/_boutpp_build/backend.py @@ -33,7 +33,7 @@ def getversion(): with contextlib.suppress(KeyError): # 0. Check whether version is set via environment variable version = os.environ["BOUT_PRETEND_VERSION"] - return version + return version.lstrip("v") _bout_previous_version = "v5.2.0" _bout_next_version = "v5.2.1" @@ -74,7 +74,7 @@ def getversion(): version = _bout_previous_version + ".rc+" + hash with open("_version.txt", "w") as f: f.write(version + "\n") - return version + return version.lstrip("v") def run(cmd): @@ -212,10 +212,12 @@ def build_sdist(sdist_directory, config_settings=None): run(f"rm {tmp}") with open(tmp, "w") as f: - f.write(f"""Metadata-Version: 2.1 + f.write( + f"""Metadata-Version: 2.1 Name: {pkgname} Version: {getversion()} -""") +""" + ) with open("LICENSE") as src: pre = "License: " for l in src: @@ -267,19 +269,23 @@ def prepare_metadata_for_build_wheel( distinfo = f"{metadata_directory}/{thisdir}" mkdir_p(distinfo) with open(f"{distinfo}/METADATA", "w") as f: - f.write(f"""Metadata-Version: 2.1 + f.write( + f"""Metadata-Version: 2.1 Name: {pkgname} Version: {getversion()} License-File: COPYING -""") +""" + ) run(f"cp LICENSE {distinfo}/COPYING") run(f"cp LICENSE.GPL {distinfo}/COPYING.GPL") with open(f"{distinfo}/WHEEL", "w") as f: - f.write(f"""Wheel-Version: 1.0 -Generator: boutpp_custom_build_wheel ({getversion()}) + f.write( + f"""Wheel-Version: 1.0 +Generator: boutpp_custom_build_wheel (version {getversion()}) Root-Is-Purelib: false Tag: {gettag()} -""") +""" + ) if record: with open(f"{distinfo}/RECORD", "w") as f: From 6007eeddc6ab7ae0dcc5325a105a4e3aa54c278b Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 13:00:34 +0100 Subject: [PATCH 253/461] Use fmt::runtime Otherwise fmt tries to do compile-time checks, but fails as it cannot be checked at compile time. https://github.com/fmtlib/fmt/issues/4179 --- include/bout/boutexception.hxx | 6 +-- include/bout/msg_stack.hxx | 6 +-- include/bout/optionsreader.hxx | 4 +- include/bout/output.hxx | 15 ++++--- include/bout/sys/expressionparser.hxx | 2 +- src/bout++.cxx | 2 +- src/mesh/impls/bout/boutmesh.cxx | 59 +++++++++++++++------------ src/sys/expressionparser.cxx | 10 +++-- src/sys/options.cxx | 15 ++++--- src/sys/options/options_ini.cxx | 2 +- tests/unit/sys/test_options.cxx | 22 +++++----- 11 files changed, 81 insertions(+), 62 deletions(-) diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index 565eb6b46d..d525aeb608 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -21,7 +21,7 @@ public: template BoutException(S&& format, Args&&... args) - : BoutException(fmt::format(std::forward(format), + : BoutException(fmt::format(fmt::runtime(std::forward(format)), std::forward(args)...)) {} ~BoutException() override; @@ -46,7 +46,7 @@ public: BoutRhsFail(std::string message) : BoutException(std::move(message)) {} template BoutRhsFail(S&& format, Args&&... args) - : BoutRhsFail(fmt::format(std::forward(format), + : BoutRhsFail(fmt::format(fmt::runtime(std::forward(format)), std::forward(args)...)) {} }; @@ -55,7 +55,7 @@ public: BoutIterationFail(std::string message) : BoutException(std::move(message)) {} template BoutIterationFail(S&& format, Args&&... args) - : BoutIterationFail(fmt::format(std::forward(format), + : BoutIterationFail(fmt::format(fmt::runtime(std::forward(format)), std::forward(args)...)) {} }; diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 1abb26d2c7..8771aeab42 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -62,7 +62,7 @@ public: template int push(const S& format, const Args&... args) { - return push(fmt::format(format, args...)); + return push(fmt::format(fmt::runtime(format), args...)); } void pop(); ///< Remove the last message @@ -134,8 +134,8 @@ public: template MsgStackItem(const std::string& file, int line, const S& msg, const Args&... args) - : point(msg_stack.push("{:s} on line {:d} of '{:s}'", fmt::format(msg, args...), - line, file)) {} + : point(msg_stack.push("{:s} on line {:d} of '{:s}'", + fmt::format(fmt::runtime(msg), args...), line, file)) {} ~MsgStackItem() { // If an exception has occurred, don't pop the message if (exception_count == std::uncaught_exceptions()) { diff --git a/include/bout/optionsreader.hxx b/include/bout/optionsreader.hxx index de3d40514d..0c9c227916 100644 --- a/include/bout/optionsreader.hxx +++ b/include/bout/optionsreader.hxx @@ -71,7 +71,7 @@ public: template void read(Options* options, const S& format, const Args&... args) { - return read(options, fmt::format(format, args...)); + return read(options, fmt::format(fmt::runtime(format), args...)); } /// Write options to file @@ -82,7 +82,7 @@ public: template void write(Options* options, const S& format, const Args&... args) { - return write(options, fmt::format(format, args...)); + return write(options, fmt::format(fmt::runtime(format), args...)); } /// Parse options from the command line diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 34b4f19376..4eb1d615ca 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -83,7 +83,8 @@ public: template Output(const S& format, Args&&... args) - : Output(fmt::format(format, std::forward(args)...)) {} + : Output(fmt::format(fmt::runtime(format), std::forward(args)...)) { + } ~Output() override { close(); } @@ -95,7 +96,7 @@ public: template int open(const S& format, Args&&... args) { - return open(fmt::format(format, std::forward(args)...)); + return open(fmt::format(fmt::runtime(format), std::forward(args)...)); } /// Close the log file @@ -106,14 +107,14 @@ public: template void write(const S& format, Args&&... args) { - write(fmt::format(format, std::forward(args)...)); + write(fmt::format(fmt::runtime(format), std::forward(args)...)); } /// Same as write, but only to screen virtual void print(const std::string& message); template void print(const S& format, Args&&... args) { - print(fmt::format(format, std::forward(args)...)); + print(fmt::format(fmt::runtime(format), std::forward(args)...)); } /// Add an output stream. All output will be sent to all streams @@ -178,7 +179,8 @@ public: void write(const S& format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->write(fmt::format(format, std::forward(args)...)); + base->write( + fmt::format(fmt::runtime(format), std::forward(args)...)); } } @@ -190,7 +192,8 @@ public: void print(const S& format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->print(fmt::format(format, std::forward(args)...)); + base->print( + fmt::format(fmt::runtime(format), std::forward(args)...)); } } diff --git a/include/bout/sys/expressionparser.hxx b/include/bout/sys/expressionparser.hxx index b312ce2fb1..78525e5b69 100644 --- a/include/bout/sys/expressionparser.hxx +++ b/include/bout/sys/expressionparser.hxx @@ -243,7 +243,7 @@ public: template ParseException(const S& format, const Args&... args) - : message(fmt::format(format, args...)) {} + : message(fmt::format(fmt::runtime(format), args...)) {} ~ParseException() override = default; diff --git a/src/bout++.cxx b/src/bout++.cxx index d6888874c6..4ca4d352ca 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -324,7 +324,7 @@ template // type. Note that this does require all the options are used in the // constructor, and not in a `init` method or similar std::cout << fmt::format("Input options for {} '{}':\n\n", Factory::type_name, type); - std::cout << fmt::format("{:id}\n", help_options); + std::cout << fmt::format(fmt::runtime("{:id}\n"), help_options); std::exit(EXIT_SUCCESS); } diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 4aaa760c04..2e4aebdd44 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -165,14 +165,16 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, // Check size of Y mesh if we've got multiple processors in Y if (num_local_y_points < num_y_guards and num_y_processors != 1) { return {false, - fmt::format(_("\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n"), ny, - num_y_processors, num_local_y_points, num_y_guards)}; + fmt::format(fmt::runtime(_( + "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n")), + ny, num_y_processors, num_local_y_points, num_y_guards)}; } // Check branch cuts if ((jyseps1_1 + 1) % num_local_y_points != 0) { - return {false, fmt::format(_("\t -> Leg region jyseps1_1+1 ({:d}) must be a " - "multiple of MYSUB ({:d})\n"), - jyseps1_1 + 1, num_local_y_points)}; + return {false, + fmt::format(fmt::runtime(_("\t -> Leg region jyseps1_1+1 ({:d}) must be a " + "multiple of MYSUB ({:d})\n")), + jyseps1_1 + 1, num_local_y_points)}; } if (jyseps2_1 != jyseps1_2) { @@ -181,50 +183,57 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((jyseps2_1 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(_("\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), + fmt::format(fmt::runtime(_( + "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n")), jyseps2_1, jyseps1_1, jyseps2_1 - jyseps1_1, num_local_y_points)}; } if ((jyseps2_2 - jyseps1_2) % num_local_y_points != 0) { return { false, - fmt::format(_("\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), + fmt::format(fmt::runtime(_( + "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n")), jyseps2_2, jyseps1_2, jyseps2_2 - jyseps1_2, num_local_y_points)}; } // Check upper legs if ((ny_inner - jyseps2_1 - 1) % num_local_y_points != 0) { - return { - false, - fmt::format(_("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), - ny_inner, jyseps2_1, ny_inner - jyseps2_1 - 1, num_local_y_points)}; + return {false, + fmt::format( + fmt::runtime( + _("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n")), + ny_inner, jyseps2_1, ny_inner - jyseps2_1 - 1, num_local_y_points)}; } if ((jyseps1_2 - ny_inner + 1) % num_local_y_points != 0) { - return { - false, - fmt::format(_("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), - jyseps1_2, ny_inner, jyseps1_2 - ny_inner + 1, num_local_y_points)}; + return {false, + fmt::format( + fmt::runtime( + _("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n")), + jyseps1_2, ny_inner, jyseps1_2 - ny_inner + 1, num_local_y_points)}; } } else { // Single Null if ((jyseps2_2 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(_("\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n"), + fmt::format(fmt::runtime(_( + "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n")), jyseps2_2, jyseps1_1, jyseps2_2 - jyseps1_1, num_local_y_points)}; } } if ((ny - jyseps2_2 - 1) % num_local_y_points != 0) { - return {false, fmt::format( - _("\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " - "multiple of MYSUB ({:d})\n"), - ny, jyseps2_2, ny - jyseps2_2 - 1, num_local_y_points)}; + return { + false, + fmt::format(fmt::runtime(_( + "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " + "multiple of MYSUB ({:d})\n")), + ny, jyseps2_2, ny - jyseps2_2 - 1, num_local_y_points)}; } return {true, ""}; diff --git a/src/sys/expressionparser.cxx b/src/sys/expressionparser.cxx index 804f371bbe..1c381b26df 100644 --- a/src/sys/expressionparser.cxx +++ b/src/sys/expressionparser.cxx @@ -327,13 +327,15 @@ may need to change your input file. // No matches, just point out the error if (possible_matches.empty()) { - return fmt::format(message_template, name, problem_bit); + return fmt::format(fmt::runtime(message_template), name, problem_bit); } // Give the first suggestion as a possible alternative - std::string error_message = fmt::format(message_template, name, problem_bit); - error_message += fmt::format(_("\n {1: ^{2}}{0}\n Did you mean '{0}'?"), - possible_matches.begin()->name, "", start); + std::string error_message = + fmt::format(fmt::runtime(message_template), name, problem_bit); + error_message += + fmt::format(fmt::runtime(_("\n {1: ^{2}}{0}\n Did you mean '{0}'?")), + possible_matches.begin()->name, "", start); return error_message; }; diff --git a/src/sys/options.cxx b/src/sys/options.cxx index bb4c920b90..fb90868081 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -760,8 +760,9 @@ T as_amt(const Options& self, const T& similar_to) { const T result = bout::utils::visit( ConvertContainer{ - fmt::format(_("Value for option {:s} cannot be converted to an {}"), self.str(), - bout::utils::typeName()), + fmt::format( + fmt::runtime(_("Value for option {:s} cannot be converted to an {}")), + self.str(), bout::utils::typeName()), similar_to}, self.value); @@ -1098,20 +1099,22 @@ bout::details::OptionsFormatterBase::format(const Options& options, // Get all the child values first for (const auto& child : children) { if (child.second.isValue()) { - fmt::format_to(ctx.out(), format_string, child.second); + fmt::format_to(ctx.out(), fmt::runtime(format_string), child.second); fmt::format_to(ctx.out(), "\n"); } } // Now descend the tree, accumulating subsections for (const auto& subsection : options.subsections()) { - fmt::format_to(ctx.out(), format_string, *subsection.second); + fmt::format_to(ctx.out(), fmt::runtime(format_string), *subsection.second); } return ctx.out(); } -std::string toString(const Options& value) { return fmt::format("{}", value); } +std::string toString(const Options& value) { + return fmt::format(fmt::runtime("{}"), value); +} namespace bout { void checkForUnusedOptions() { @@ -1157,7 +1160,7 @@ void checkForUnusedOptions(const Options& options, const std::string& data_dir, } possible_misspellings += fmt::format("\nUnused option '{}', did you mean:\n", key); for (const auto& match : fuzzy_matches) { - possible_misspellings += fmt::format("\t{:idk}\n", match.match); + possible_misspellings += fmt::format(fmt::runtime("\t{:idk}\n"), match.match); } } diff --git a/src/sys/options/options_ini.cxx b/src/sys/options/options_ini.cxx index 939e100f1b..b51af8fdcf 100644 --- a/src/sys/options/options_ini.cxx +++ b/src/sys/options/options_ini.cxx @@ -157,7 +157,7 @@ void OptionINI::write(Options* options, const std::string& filename) { } // Call recursive function to write to file - fout << fmt::format("{:uds}", *options); + fout << fmt::format(fmt::runtime("{:uds}"), *options); fout.close(); } diff --git a/tests/unit/sys/test_options.cxx b/tests/unit/sys/test_options.cxx index b74cfdcb9f..f0256b6aa7 100644 --- a/tests/unit/sys/test_options.cxx +++ b/tests/unit/sys/test_options.cxx @@ -1099,7 +1099,9 @@ value6 = 12 } TEST_F(OptionsTest, InvalidFormat) { - EXPECT_THROW([[maybe_unused]] auto none = fmt::format("{:nope}", Options{}), fmt::format_error); + EXPECT_THROW([[maybe_unused]] auto none = + fmt::format(fmt::runtime("{:nope}"), Options{}), + fmt::format_error); } TEST_F(OptionsTest, FormatValue) { @@ -1110,7 +1112,7 @@ TEST_F(OptionsTest, FormatValue) { const std::string expected = "value1 = 4 # type: int, doc: This is a value, source: some test"; - EXPECT_EQ(expected, fmt::format("{:ds}", options["value1"])); + EXPECT_EQ(expected, fmt::format(fmt::runtime("{:ds}"), options["value1"])); } TEST_F(OptionsTest, FormatDefault) { @@ -1138,7 +1140,7 @@ value4 = 3.2 value6 = 12 )"; - EXPECT_EQ(fmt::format("{}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{}"), option), expected); } TEST_F(OptionsTest, FormatDocstrings) { @@ -1169,7 +1171,7 @@ value4 = 3.2 value6 = 12 )"; - EXPECT_EQ(fmt::format("{:d}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:d}"), option), expected); } TEST_F(OptionsTest, FormatDocstringsAndInline) { @@ -1192,9 +1194,9 @@ section2:subsection1:value4 = 3.2 section3:subsection2:value6 = 12 )"; - EXPECT_EQ(fmt::format("{:di}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:di}"), option), expected); // Order of format spec shouldn't matter - EXPECT_EQ(fmt::format("{:id}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:id}"), option), expected); } TEST_F(OptionsTest, FormatDocstringsAndInlineKeysOnly) { @@ -1219,16 +1221,16 @@ section2:subsection1:value4 section3:subsection2:value6 # source: a test )"; - EXPECT_EQ(fmt::format("{:ksdi}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:ksdi}"), option), expected); // Order of format spec shouldn't matter - EXPECT_EQ(fmt::format("{:idsk}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:idsk}"), option), expected); } TEST_F(OptionsTest, FormatUnused) { Options option{{"section1", {{"value1", 42}}}}; std::string expected = "section1:value1\t\t# unused value (NOT marked conditionally used)\n"; - EXPECT_EQ(fmt::format("{:iku}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:iku}"), option), expected); } TEST_F(OptionsTest, FormatConditionallyUsed) { @@ -1236,7 +1238,7 @@ TEST_F(OptionsTest, FormatConditionallyUsed) { option.setConditionallyUsed(); std::string expected = "section1:value1\t\t# unused value (marked conditionally used)\n"; - EXPECT_EQ(fmt::format("{:iku}", option), expected); + EXPECT_EQ(fmt::format(fmt::runtime("{:iku}"), option), expected); } TEST_F(OptionsTest, GetUnused) { From 7d8418e00286d466c3bac4d69a1fde9172a5e6cf Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 13:00:47 +0100 Subject: [PATCH 254/461] Expose erase even with C++20 --- include/bout/utils.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index 04aa0b281d..560f97f954 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -186,6 +186,7 @@ typename std::vector::size_type erase_if(std::vector& c, Pre return r; } #else +using std::erase; using std::erase_if; #endif } // namespace utils From 27d5c932b6cc4ba0eed6b3d379663ca4b9f35ea9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 13:01:47 +0100 Subject: [PATCH 255/461] Do not pass by reference With C++ std::accumulate uses std::move, which fails with the reference. --- tests/unit/fake_mesh.hxx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 4656ee0282..7c5d0b3637 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -236,18 +236,17 @@ public: "RGN_OUTER_X"}; // Sum up and get unique points in the boundaries defined above - addRegion2D("RGN_BNDRY", - std::accumulate(begin(boundary_names), end(boundary_names), - Region{}, - [this](Region& a, const std::string& b) { - return a + getRegion2D(b); - }) - .unique()); + addRegion2D("RGN_BNDRY", std::accumulate(begin(boundary_names), end(boundary_names), + Region{}, + [&](Region a, const std::string& b) { + return a + getRegion2D(b); + }) + .unique()); addRegion3D("RGN_BNDRY", std::accumulate(begin(boundary_names), end(boundary_names), Region{}, - [this](Region& a, const std::string& b) { + [this](Region a, const std::string& b) { return a + getRegion3D(b); }) .unique()); From cf1c7f49854acf337f03026ce533ac7516d9bbe6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 13:03:14 +0100 Subject: [PATCH 256/461] Do not include unneeded header isatty is provided by both cpptrace and unistd.h, so we should only include one. --- src/sys/boutexception.cxx | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/sys/boutexception.cxx b/src/sys/boutexception.cxx index b5ea01d231..eff2de2e1f 100644 --- a/src/sys/boutexception.cxx +++ b/src/sys/boutexception.cxx @@ -1,7 +1,6 @@ #include #include #include -#include #include @@ -11,7 +10,6 @@ #include #include -#include #include From caad51bec7b68514bf7455fcc92104ce4f7966bd Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Feb 2026 13:04:11 +0100 Subject: [PATCH 257/461] Switch to C++20 --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index c05bad29db..e52d1753f1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -522,10 +522,10 @@ set_target_properties( set(CONFIG_LDFLAGS "${CONFIG_LDFLAGS} -L\$BOUT_LIB_PATH -lbout++") set(BOUT_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/include") set(CONFIG_CFLAGS - "${CONFIG_CFLAGS} -I\${BOUT_INCLUDE_PATH} -I${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CXX_FLAGS} -std=c++17" + "${CONFIG_CFLAGS} -I\${BOUT_INCLUDE_PATH} -I${CMAKE_CURRENT_BINARY_DIR}/include ${CMAKE_CXX_FLAGS} -std=c++20" ) -target_compile_features(bout++ PUBLIC cxx_std_17) +target_compile_features(bout++ PUBLIC cxx_std_20) set_target_properties(bout++ PROPERTIES CXX_EXTENSIONS OFF) # Optional compiler features From bd3175bf09861f52105c844bd3fbb1856727b3b6 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 17 Feb 2026 14:24:19 +0000 Subject: [PATCH 258/461] Re-enable compile-time format checks Mostly involves using `fmt::vformat` with `fmt::make_format_args` in functions/ctors that act like `fmt::format`. Note that `fmt::make_format_args` requires lvalues, so although we take `Args&&` we _must not_ call `std::forward`. We also have to introduce a new gettext macro `_f` to allow compile-time format arg checking _and_ runtime i18n substitution. --- include/bout/boutexception.hxx | 25 +-- include/bout/msg_stack.hxx | 21 ++- include/bout/options.hxx | 46 ++++- include/bout/optionsreader.hxx | 17 +- include/bout/output.hxx | 48 +++--- include/bout/sys/expressionparser.hxx | 14 +- include/bout/sys/gettext.hxx | 36 +++- src/bout++.cxx | 176 +++++++++++--------- src/mesh/coordinates.cxx | 4 +- src/mesh/impls/bout/boutmesh.cxx | 108 ++++++------ src/mesh/mesh.cxx | 57 +++++-- src/solver/impls/petsc/petsc.cxx | 2 +- src/solver/impls/snes/snes.cxx | 2 +- src/solver/impls/split-rk/split-rk.cxx | 18 +- src/solver/solver.cxx | 45 +++-- src/sys/expressionparser.cxx | 49 ++++-- src/sys/options.cxx | 110 ++++-------- src/sys/options/options_ini.cxx | 21 ++- src/sys/optionsreader.cxx | 13 +- tests/unit/sys/test_options.cxx | 18 +- tests/unit/sys/test_output.cxx | 2 +- tools/pylib/_boutpp_build/boutcpp.pxd.jinja | 2 +- tools/pylib/_boutpp_build/boutpp.pyx.jinja | 2 +- 23 files changed, 495 insertions(+), 341 deletions(-) diff --git a/include/bout/boutexception.hxx b/include/bout/boutexception.hxx index d525aeb608..735f98c7d6 100644 --- a/include/bout/boutexception.hxx +++ b/include/bout/boutexception.hxx @@ -5,6 +5,7 @@ #include #include +#include "fmt/base.h" #include "fmt/core.h" /// Throw BoutRhsFail with \p message if any one process has non-zero @@ -19,10 +20,10 @@ public: BoutException& operator=(BoutException&&) = delete; BoutException(std::string msg); - template - BoutException(S&& format, Args&&... args) - : BoutException(fmt::format(fmt::runtime(std::forward(format)), - std::forward(args)...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutException(fmt::format_string format, Args&&... args) + : BoutException(fmt::vformat(format, fmt::make_format_args(args...))) {} ~BoutException() override; @@ -44,19 +45,19 @@ private: class BoutRhsFail : public BoutException { public: BoutRhsFail(std::string message) : BoutException(std::move(message)) {} - template - BoutRhsFail(S&& format, Args&&... args) - : BoutRhsFail(fmt::format(fmt::runtime(std::forward(format)), - std::forward(args)...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutRhsFail(fmt::format_string format, Args&&... args) + : BoutRhsFail(fmt::vformat(format, fmt::make_format_args(args...))) {} }; class BoutIterationFail : public BoutException { public: BoutIterationFail(std::string message) : BoutException(std::move(message)) {} - template - BoutIterationFail(S&& format, Args&&... args) - : BoutIterationFail(fmt::format(fmt::runtime(std::forward(format)), - std::forward(args)...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + BoutIterationFail(fmt::format_string format, Args&&... args) + : BoutIterationFail(fmt::vformat(format, fmt::make_format_args(args...))) {} }; #endif diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 8771aeab42..89b9f2d8d5 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -31,6 +31,7 @@ class MsgStack; #include "bout/build_defines.hxx" +#include "fmt/base.h" #include "fmt/core.h" #include @@ -60,9 +61,10 @@ public: int push(std::string message); int push() { return push(""); } - template - int push(const S& format, const Args&... args) { - return push(fmt::format(fmt::runtime(format), args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + int push(fmt::format_string format, Args&&... args) { + return push(fmt::vformat(format, fmt::make_format_args(args...))); } void pop(); ///< Remove the last message @@ -74,8 +76,8 @@ public: #else /// Dummy functions which should be optimised out int push(const std::string&) { return 0; } - template - int push(const S&, const Args&...) { + template + int push(fmt::format_string format, const Args&... args) { return 0; } @@ -132,10 +134,13 @@ public: MsgStackItem(const std::string& file, int line, const char* msg) : point(msg_stack.push("{:s} on line {:d} of '{:s}'", msg, line, file)) {} - template - MsgStackItem(const std::string& file, int line, const S& msg, const Args&... args) + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + MsgStackItem(const std::string& file, int line, fmt::format_string msg, + Args&&... args) : point(msg_stack.push("{:s} on line {:d} of '{:s}'", - fmt::format(fmt::runtime(msg), args...), line, file)) {} + fmt::vformat(msg, fmt::make_format_args(args...)), line, + file)) {} ~MsgStackItem() { // If an exception has occurred, don't pop the message if (exception_count == std::uncaught_exceptions()) { diff --git a/include/bout/options.hxx b/include/bout/options.hxx index 1c552cd4f9..775b65ba1c 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -52,6 +52,7 @@ class Options; #include "bout/utils.hxx" #include +#include #include #include @@ -626,8 +627,8 @@ public: // Option not found. Copy the value from the default. this->_set_no_check(def.value, DEFAULT_SOURCE); - output_info << _("\tOption ") << full_name << " = " << def.full_name << " (" - << DEFAULT_SOURCE << ")\n"; + output_info.write("{}{} = {}({})\n", _("\tOption "), full_name, def.full_name, + DEFAULT_SOURCE); } else { // Check if this was previously set as a default option if (bout::utils::variantEqualTo(attributes.at("source"), DEFAULT_SOURCE)) { @@ -911,8 +912,8 @@ private: << ")\n"; } else { throw BoutException( - _("Options: Setting a value from same source ({:s}) to new value " - "'{:s}' - old value was '{:s}'."), + _f("Options: Setting a value from same source ({:s}) to new value " + "'{:s}' - old value was '{:s}'."), source, toString(val), bout::utils::variantToString(value)); } } @@ -1043,7 +1044,40 @@ namespace details { /// so that we can put the function definitions in the .cxx file, /// avoiding lengthy recompilation if we change it struct OptionsFormatterBase { - auto parse(fmt::format_parse_context& ctx) -> fmt::format_parse_context::iterator; + constexpr auto parse(fmt::format_parse_context& ctx) { + const auto* it = ctx.begin(); + const auto* const end = ctx.end(); + + while (it != end and *it != '}') { + switch (*it) { + case 'd': + docstrings = true; + ++it; + break; + case 'i': + inline_section_names = true; + ++it; + break; + case 'k': + key_only = true; + ++it; + break; + case 's': + source = true; + ++it; + break; + case 'u': + unused = true; + ++it; + break; + default: + throw fmt::format_error("invalid format for 'Options'"); + } + } + + return it; + } + auto format(const Options& options, fmt::format_context& ctx) const -> fmt::format_context::iterator; @@ -1060,8 +1094,6 @@ private: bool key_only{false}; /// Include the 'source' attribute, if present bool source{false}; - /// Format string to passed down to subsections - std::string format_string; }; } // namespace details } // namespace bout diff --git a/include/bout/optionsreader.hxx b/include/bout/optionsreader.hxx index 0c9c227916..c5cda6ef1f 100644 --- a/include/bout/optionsreader.hxx +++ b/include/bout/optionsreader.hxx @@ -36,7 +36,8 @@ class OptionsReader; #include "bout/options.hxx" -#include "fmt/core.h" +#include +#include #include #include @@ -69,9 +70,10 @@ public: /// @param[in] file The name of the file. printf style arguments can be used to create the file name. void read(Options* options, const std::string& filename); - template - void read(Options* options, const S& format, const Args&... args) { - return read(options, fmt::format(fmt::runtime(format), args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void read(Options* options, fmt::format_string format, Args&&... args) { + return read(options, fmt::vformat(format, fmt::make_format_args(args...))); } /// Write options to file @@ -80,9 +82,10 @@ public: /// @param[in] file The name of the file to (over)write void write(Options* options, const std::string& filename); - template - void write(Options* options, const S& format, const Args&... args) { - return write(options, fmt::format(fmt::runtime(format), args...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(Options* options, fmt::format_string format, Args&&... args) { + return write(options, fmt::vformat(format, fmt::make_format_args(args...))); } /// Parse options from the command line diff --git a/include/bout/output.hxx b/include/bout/output.hxx index 4eb1d615ca..9416ab411b 100644 --- a/include/bout/output.hxx +++ b/include/bout/output.hxx @@ -39,7 +39,8 @@ class Output; #include "bout/sys/gettext.hxx" // IWYU pragma: keep for gettext _() macro #include "bout/unused.hxx" -#include "fmt/core.h" +#include +#include #include @@ -81,10 +82,10 @@ public: open(filename); } - template - Output(const S& format, Args&&... args) - : Output(fmt::format(fmt::runtime(format), std::forward(args)...)) { - } + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + Output(fmt::format_string format, Args&&... args) + : Output(fmt::vformat(format, fmt::make_format_args(args...))) {} ~Output() override { close(); } @@ -94,9 +95,10 @@ public: /// Open an output log file int open(const std::string& filename); - template - int open(const S& format, Args&&... args) { - return open(fmt::format(fmt::runtime(format), std::forward(args)...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + int open(fmt::format_string format, Args&&... args) { + return open(fmt::vformat(format, fmt::make_format_args(args...))); } /// Close the log file @@ -105,16 +107,18 @@ public: /// Write a string using fmt format virtual void write(const std::string& message); - template - void write(const S& format, Args&&... args) { - write(fmt::format(fmt::runtime(format), std::forward(args)...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(fmt::format_string format, Args&&... args) { + write(fmt::vformat(format, fmt::make_format_args(args...))); } /// Same as write, but only to screen virtual void print(const std::string& message); - template - void print(const S& format, Args&&... args) { - print(fmt::format(fmt::runtime(format), std::forward(args)...)); + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void print(fmt::format_string format, Args&&... args) { + print(fmt::vformat(format, fmt::make_format_args(args...))); } /// Add an output stream. All output will be sent to all streams @@ -175,12 +179,12 @@ public: /// This string is then sent to log file and stdout (on processor 0) void write(const std::string& message) override; - template - void write(const S& format, Args&&... args) { + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void write(fmt::format_string format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->write( - fmt::format(fmt::runtime(format), std::forward(args)...)); + base->write(fmt::vformat(format, fmt::make_format_args(args...))); } } @@ -188,12 +192,12 @@ public: /// note: unlike write, this is not also sent to log files void print(const std::string& message) override; - template - void print(const S& format, Args&&... args) { + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + void print(fmt::format_string format, Args&&... args) { if (enabled) { ASSERT1(base != nullptr); - base->print( - fmt::format(fmt::runtime(format), std::forward(args)...)); + base->print(fmt::vformat(format, fmt::make_format_args(args...))); } } diff --git a/include/bout/sys/expressionparser.hxx b/include/bout/sys/expressionparser.hxx index 78525e5b69..8b2442e843 100644 --- a/include/bout/sys/expressionparser.hxx +++ b/include/bout/sys/expressionparser.hxx @@ -29,7 +29,8 @@ #include "bout/unused.hxx" -#include "fmt/core.h" +#include +#include #include #include @@ -239,11 +240,16 @@ private: class ParseException : public std::exception { public: + ParseException(const ParseException&) = default; + ParseException(ParseException&&) = delete; + ParseException& operator=(const ParseException&) = default; + ParseException& operator=(ParseException&&) = delete; ParseException(const std::string& message_) : message(message_) {} - template - ParseException(const S& format, const Args&... args) - : message(fmt::format(fmt::runtime(format), args...)) {} + template + // NOLINTNEXTLINE(cppcoreguidelines-missing-std-forward) + ParseException(fmt::format_string format, Args&&... args) + : message(fmt::vformat(format, fmt::make_format_args(args...))) {} ~ParseException() override = default; diff --git a/include/bout/sys/gettext.hxx b/include/bout/sys/gettext.hxx index d67becd54d..d62cd66882 100644 --- a/include/bout/sys/gettext.hxx +++ b/include/bout/sys/gettext.hxx @@ -7,15 +7,47 @@ #if BOUT_HAS_GETTEXT -#include +#include // IWYU pragma: keep + #include #define GETTEXT_PACKAGE "libbout" -#define _(string) dgettext(GETTEXT_PACKAGE, string) +// If we have C++23, we can get fmt to do compile-time checks of our format +// strings, _and_ have gettext do runtime replacement +#if __cpp_if_consteval >= 202106L +constexpr const char* dgettext_wrap(const char* __domainname, const char* __msgid) __THROW + __attribute_format_arg__(2); + +constexpr const char* dgettext_wrap(const char* __domainname, const char* __msgid) { + if consteval { + return __msgid; + } + return dgettext(__domainname, __msgid); +} + +/// Gettext i18n macro for text containing fmt format specifiers +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) dgettext_wrap(GETTEXT_PACKAGE, string) #else +// We're pre-C++23, so all our i18n text must be fmt runtime formats +#include "fmt/base.h" // IWYU pragma: keep + +/// Gettext i18n macro for text containing fmt format specifiers +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) fmt::runtime(dgettext(GETTEXT_PACKAGE, string)) +#endif + +/// Gettext i18n macro for plain text that doesn't need formatting +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _(string) dgettext(GETTEXT_PACKAGE, string) + +#else // BOUT_HAS_GETTEXT +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) +#define _f(string) string +// NOLINTNEXTLINE(cppcoreguidelines-macro-usage) #define _(string) string #endif // BOUT_HAS_GETTEXT diff --git a/src/bout++.cxx b/src/bout++.cxx index 4ca4d352ca..cad3edf6c3 100644 --- a/src/bout++.cxx +++ b/src/bout++.cxx @@ -32,16 +32,23 @@ static constexpr auto DEFAULT_DIR = "data"; #define GLOBALORIGIN #include "bout++-time.hxx" +#include "bout/array.hxx" #include "bout/boundary_factory.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/coordinates_accessor.hxx" +#include "bout/dcomplex.hxx" +#include "bout/globals.hxx" #include "bout/hyprelib.hxx" #include "bout/interpolation_xz.hxx" #include "bout/interpolation_z.hxx" #include "bout/invert/laplacexz.hxx" #include "bout/invert_laplace.hxx" #include "bout/invert_parderiv.hxx" +#include "bout/mask.hxx" +#include "bout/monitor.hxx" #include "bout/mpi_wrapper.hxx" #include "bout/msg_stack.hxx" #include "bout/openmpwrap.hxx" @@ -52,7 +59,9 @@ static constexpr auto DEFAULT_DIR = "data"; #include "bout/rkscheme.hxx" #include "bout/slepclib.hxx" #include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" #include "bout/sys/timer.hxx" +#include "bout/utils.hxx" #include "bout/version.hxx" #define BOUT_NO_USING_NAMESPACE_BOUTGLOBALS @@ -63,11 +72,22 @@ static constexpr auto DEFAULT_DIR = "data"; #include "bout/adios_object.hxx" #endif +#include #include +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include +#include #include #include @@ -137,7 +157,7 @@ int BoutInitialise(int& argc, char**& argv) { try { args = parseCommandLineArgs(argc, argv); } catch (const BoutException& e) { - output_error << _("Bad command line arguments:\n") << e.what() << std::endl; + output_error.write("{:s}{:s}\n", _("Bad command line arguments:\n"), e.what()); return 1; } @@ -323,8 +343,8 @@ template // Now we can print all the options used in constructing our // type. Note that this does require all the options are used in the // constructor, and not in a `init` method or similar - std::cout << fmt::format("Input options for {} '{}':\n\n", Factory::type_name, type); - std::cout << fmt::format(fmt::runtime("{:id}\n"), help_options); + fmt::println("Input options for {:s} '{}':\n", Factory::type_name, type); + fmt::println("{:id}", help_options); std::exit(EXIT_SUCCESS); } @@ -344,7 +364,7 @@ void handleFactoryHelp(const std::string& current_arg, int i, int argc, char** a if (current_arg == help_arg) { if (i + 1 >= argc) { - throw BoutException(_("Usage is {} {} \n"), argv[0], help_arg); + throw BoutException(_f("Usage is {} {} \n"), argv[0], help_arg); } printTypeOptions(argv[i + 1]); } @@ -358,9 +378,10 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { if (current_arg == "-h" || current_arg == "--help") { // Print help message -- note this will be displayed once per processor as we've not // started MPI yet. - output.write(_("Usage: {:s} [-d ] [-f ] [restart " - "[append]] [VAR=VALUE]\n"), - argv[0]); + output.write( + _f("Usage: {:s} [-d ] [-f ] [restart " + "[append]] [VAR=VALUE]\n"), + argv[0]); output.write( _("\n" " -d \t\tLook in for input/output files\n" @@ -373,33 +394,33 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { output.write(_(" -c, --color\t\t\tColor output using bout-log-color\n")); #endif output.write( - _(" --print-config\t\tPrint the compile-time configuration\n" - " --list-solvers\t\tList the available time solvers\n" - " --help-solver \tPrint help for the given time solver\n" - " --list-laplacians\t\tList the available Laplacian inversion solvers\n" - " --help-laplacian \tPrint help for the given Laplacian " - "inversion solver\n" - " --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" - " --help-laplacexz \tPrint help for the given LaplaceXZ " - "inversion solver\n" - " --list-invertpars\t\tList the available InvertPar solvers\n" - " --help-invertpar \tPrint help for the given InvertPar solver\n" - " --list-rkschemes\t\tList the available Runge-Kutta schemes\n" - " --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" - " --list-meshes\t\t\tList the available Meshes\n" - " --help-mesh \t\tPrint help for the given Mesh\n" - " --list-xzinterpolations\tList the available XZInterpolations\n" - " --help-xzinterpolation \tPrint help for the given " - "XZInterpolation\n" - " --list-zinterpolations\tList the available ZInterpolations\n" - " --help-zinterpolation \tPrint help for the given " - "ZInterpolation\n" - " -h, --help\t\t\tThis message\n" - " restart [append]\t\tRestart the simulation. If append is specified, " - "append to the existing output files, otherwise overwrite them\n" - " VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" - "\nFor all possible input parameters, see the user manual and/or the " - "physics model source (e.g. {:s}.cxx)\n"), + _f(" --print-config\t\tPrint the compile-time configuration\n" + " --list-solvers\t\tList the available time solvers\n" + " --help-solver \tPrint help for the given time solver\n" + " --list-laplacians\t\tList the available Laplacian inversion solvers\n" + " --help-laplacian \tPrint help for the given Laplacian " + "inversion solver\n" + " --list-laplacexz\t\tList the available LaplaceXZ inversion solvers\n" + " --help-laplacexz \tPrint help for the given LaplaceXZ " + "inversion solver\n" + " --list-invertpars\t\tList the available InvertPar solvers\n" + " --help-invertpar \tPrint help for the given InvertPar solver\n" + " --list-rkschemes\t\tList the available Runge-Kutta schemes\n" + " --help-rkscheme \tPrint help for the given Runge-Kutta scheme\n" + " --list-meshes\t\t\tList the available Meshes\n" + " --help-mesh \t\tPrint help for the given Mesh\n" + " --list-xzinterpolations\tList the available XZInterpolations\n" + " --help-xzinterpolation \tPrint help for the given " + "XZInterpolation\n" + " --list-zinterpolations\tList the available ZInterpolations\n" + " --help-zinterpolation \tPrint help for the given " + "ZInterpolation\n" + " -h, --help\t\t\tThis message\n" + " restart [append]\t\tRestart the simulation. If append is specified, " + "append to the existing output files, otherwise overwrite them\n" + " VAR=VALUE\t\t\tSpecify a VALUE for input parameter VAR\n" + "\nFor all possible input parameters, see the user manual and/or the " + "physics model source (e.g. {:s}.cxx)\n"), argv[0]); std::exit(EXIT_SUCCESS); @@ -430,7 +451,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { if (string(argv[i]) == "-d") { // Set data directory if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -d \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -d \n"), argv[0]); } args.data_dir = argv[++i]; @@ -439,7 +460,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if (string(argv[i]) == "-f") { // Set options file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -f \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -f \n"), argv[0]); } args.opt_file = argv[++i]; @@ -448,7 +469,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if (string(argv[i]) == "-o") { // Set options file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -o \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -o \n"), argv[0]); } args.set_file = argv[++i]; @@ -457,7 +478,7 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { } else if ((string(argv[i]) == "-l") || (string(argv[i]) == "--log")) { // Set log file if (i + 1 >= argc) { - throw BoutException(_("Usage is {:s} -l \n"), argv[0]); + throw BoutException(_f("Usage is {:s} -l \n"), argv[0]); } args.log_file = argv[++i]; @@ -495,10 +516,10 @@ auto parseCommandLineArgs(int argc, char** argv) -> CommandLineArgs { void checkDataDirectoryIsAccessible(const std::string& data_dir) { if (std::filesystem::exists(data_dir)) { if (!std::filesystem::is_directory(data_dir)) { - throw BoutException(_("DataDir \"{:s}\" is not a directory\n"), data_dir); + throw BoutException(_f("DataDir \"{:s}\" is not a directory\n"), data_dir); } } else { - throw BoutException(_("DataDir \"{:s}\" does not exist or is not accessible\n"), + throw BoutException(_f("DataDir \"{:s}\" does not exist or is not accessible\n"), data_dir); } } @@ -510,7 +531,7 @@ void savePIDtoFile(const std::string& data_dir, int MYPE) { pid_file.open(filename.str(), std::ios::out | std::ios::trunc); if (not pid_file.is_open()) { - throw BoutException(_("Could not create PID file {:s}"), filename.str()); + throw BoutException(_f("Could not create PID file {:s}"), filename.str()); } pid_file << getpid() << "\n"; @@ -518,17 +539,17 @@ void savePIDtoFile(const std::string& data_dir, int MYPE) { } void printStartupHeader(int MYPE, int NPES) { - output_progress.write(_("BOUT++ version {:s}\n"), bout::version::full); - output_progress.write(_("Revision: {:s}\n"), bout::version::revision); + output_progress.write(_f("BOUT++ version {:s}\n"), bout::version::full); + output_progress.write(_f("Revision: {:s}\n"), bout::version::revision); #ifdef MD5SUM output_progress.write("MD5 checksum: {:s}\n", BUILDFLAG(MD5SUM)); #endif - output_progress.write(_("Code compiled on {:s} at {:s}\n\n"), boutcompiledate, + output_progress.write(_f("Code compiled on {:s} at {:s}\n\n"), boutcompiledate, boutcompiletime); output_info.write("B.Dudson (University of York), M.Umansky (LLNL) 2007\n"); output_info.write("Based on BOUT by Xueqiao Xu, 1999\n\n"); - output_info.write(_("Processor number: {:d} of {:d}\n\n"), MYPE, NPES); + output_info.write(_f("Processor number: {:d} of {:d}\n\n"), MYPE, NPES); output_info.write("pid: {:d}\n\n", getpid()); } @@ -538,53 +559,51 @@ void printCompileTimeOptions() { using namespace bout::build; - output_info.write(_("\tRuntime error checking {}"), is_enabled(check_level > 0)); + output_info.write(_f("\tRuntime error checking {}"), is_enabled(check_level > 0)); if (check_level > 0) { - output_info.write(_(", level {}"), check_level); + output_info.write(_f(", level {}"), check_level); } output_info.write("\n"); #ifdef PNCDF - output_info.write(_("\tParallel NetCDF support enabled\n")); + output_info.write(_f("\tParallel NetCDF support enabled\n")); #else - output_info.write(_("\tParallel NetCDF support disabled\n")); + output_info.write(_f("\tParallel NetCDF support disabled\n")); #endif - output_info.write(_("\tMetrics mode is {}\n"), use_metric_3d ? "3D" : "2D"); - output_info.write(_("\tFFT support {}\n"), is_enabled(has_fftw)); - output_info.write(_("\tNatural language support {}\n"), is_enabled(has_gettext)); - output_info.write(_("\tLAPACK support {}\n"), is_enabled(has_lapack)); + output_info.write(_f("\tMetrics mode is {}\n"), use_metric_3d ? "3D" : "2D"); + output_info.write(_f("\tFFT support {}\n"), is_enabled(has_fftw)); + output_info.write(_f("\tNatural language support {}\n"), is_enabled(has_gettext)); + output_info.write(_f("\tLAPACK support {}\n"), is_enabled(has_lapack)); // Horrible nested ternary to set this at compile time constexpr auto netcdf_flavour = has_netcdf ? (has_legacy_netcdf ? " (Legacy)" : " (NetCDF4)") : ""; - output_info.write(_("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), netcdf_flavour); - output_info.write(_("\tADIOS2 support {}\n"), is_enabled(has_adios2)); - output_info.write(_("\tPETSc support {}\n"), is_enabled(has_petsc)); - output_info.write(_("\tPVODE support {}\n"), is_enabled(has_pvode)); - output_info.write(_("\tScore-P support {}\n"), is_enabled(has_scorep)); - output_info.write(_("\tSLEPc support {}\n"), is_enabled(has_slepc)); - output_info.write(_("\tSUNDIALS support {}\n"), is_enabled(has_sundials)); - output_info.write(_("\tBacktrace in exceptions {}\n"), is_enabled(use_backtrace)); - output_info.write(_("\tColour in logs {}\n"), is_enabled(use_color)); - output_info.write(_("\tOpenMP parallelisation {}, using {} threads\n"), + output_info.write(_f("\tNetCDF support {}{}\n"), is_enabled(has_netcdf), + netcdf_flavour); + output_info.write(_f("\tADIOS2 support {}\n"), is_enabled(has_adios2)); + output_info.write(_f("\tPETSc support {}\n"), is_enabled(has_petsc)); + output_info.write(_f("\tPVODE support {}\n"), is_enabled(has_pvode)); + output_info.write(_f("\tScore-P support {}\n"), is_enabled(has_scorep)); + output_info.write(_f("\tSLEPc support {}\n"), is_enabled(has_slepc)); + output_info.write(_f("\tSUNDIALS support {}\n"), is_enabled(has_sundials)); + output_info.write(_f("\tBacktrace in exceptions {}\n"), is_enabled(use_backtrace)); + output_info.write(_f("\tColour in logs {}\n"), is_enabled(use_color)); + output_info.write(_f("\tOpenMP parallelisation {}, using {} threads\n"), is_enabled(use_openmp), omp_get_max_threads()); - output_info.write(_("\tExtra debug output {}\n"), is_enabled(use_output_debug)); - output_info.write(_("\tFloating-point exceptions {}\n"), is_enabled(use_sigfpe)); - output_info.write(_("\tSignal handling support {}\n"), is_enabled(use_signal)); - output_info.write(_("\tField name tracking {}\n"), is_enabled(use_track)); - output_info.write(_("\tMessage stack {}\n"), is_enabled(use_msgstack)); + output_info.write(_f("\tExtra debug output {}\n"), is_enabled(use_output_debug)); + output_info.write(_f("\tFloating-point exceptions {}\n"), is_enabled(use_sigfpe)); + output_info.write(_f("\tSignal handling support {}\n"), is_enabled(use_signal)); + output_info.write(_f("\tField name tracking {}\n"), is_enabled(use_track)); + output_info.write(_f("\tMessage stack {}\n"), is_enabled(use_msgstack)); // The stringify is needed here as BOUT_FLAGS_STRING may already contain quoted strings // which could cause problems (e.g. terminate strings). - output_info.write(_("\tCompiled with flags : {:s}\n"), STRINGIFY(BOUT_FLAGS_STRING)); + output_info.write(_f("\tCompiled with flags : {:s}\n"), STRINGIFY(BOUT_FLAGS_STRING)); } void printCommandLineArguments(const std::vector& original_argv) { - output_info.write(_("\tCommand line options for this run : ")); - for (auto& arg : original_argv) { - output_info << arg << " "; - } - output_info.write("\n"); + output_info.write("{:s}{}\n", _("\tCommand line options for this run : "), + fmt::join(original_argv, " ")); } bool setupBoutLogColor(bool color_output, int MYPE) { @@ -618,7 +637,8 @@ bool setupBoutLogColor(bool color_output, int MYPE) { } if (!success) { // Failed . Probably not important enough to stop the simulation - std::cerr << _("Could not run bout-log-color. Make sure it is in your PATH\n"); + fmt::print(stderr, "{:s}", + _("Could not run bout-log-color. Make sure it is in your PATH\n")); } return success; } @@ -638,7 +658,7 @@ void setupOutput(const std::string& data_dir, const std::string& log_file, int v /// Open an output file to echo everything to /// On processor 0 anything written to output will go to stdout and the file if (output.open("{:s}/{:s}.{:d}", data_dir, log_file, MYPE)) { - throw BoutException(_("Could not open {:s}/{:s}.{:d} for writing"), data_dir, + throw BoutException(_f("Could not open {:s}/{:s}.{:d} for writing"), data_dir, log_file, MYPE); } } @@ -731,7 +751,7 @@ int BoutFinalise(bool write_settings) { writeSettingsFile(options, data_dir, set_file); } } catch (const BoutException& e) { - output_error << _("Error whilst writing settings") << e.what() << endl; + output_error.write("{} {}\n", _("Error whilst writing settings"), e.what()); } } @@ -888,7 +908,7 @@ int BoutMonitor::call(Solver* solver, BoutReal t, [[maybe_unused]] int iter, int BoutReal t_remain = mpi_start_time + wall_limit - bout::globals::mpi->MPI_Wtime(); if (t_remain < run_data.wtime * 2) { // Less than 2 time-steps left - output_warn.write(_("Only {:e} seconds ({:.2f} steps) left. Quitting\n"), t_remain, + output_warn.write(_f("Only {:e} seconds ({:.2f} steps) left. Quitting\n"), t_remain, t_remain / run_data.wtime); user_requested_exit = true; } else { diff --git a/src/mesh/coordinates.cxx b/src/mesh/coordinates.cxx index 91bbddfd56..79ed2e1f21 100644 --- a/src/mesh/coordinates.cxx +++ b/src/mesh/coordinates.cxx @@ -1232,7 +1232,7 @@ int Coordinates::calcCovariant(const std::string& region) { a(0, 2) = a(2, 0) = g13[i]; if (const auto det = bout::invert3x3(a); det.has_value()) { - output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:d}\n", + output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:e}\n", i, det.value()); return 1; } @@ -1287,7 +1287,7 @@ int Coordinates::calcContravariant(const std::string& region) { a(0, 2) = a(2, 0) = g_13[i]; if (const auto det = bout::invert3x3(a); det.has_value()) { - output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:d}\n", + output_error.write("\tERROR: metric tensor is singular at {}, determinant: {:e}\n", i, det.value()); return 1; } diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 2e4aebdd44..00314c20f6 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -26,24 +26,44 @@ #include "boutmesh.hxx" +#include #include +#include #include #include #include #include #include #include +#include +#include +#include +#include +#include #include #include #include #include #include +#include +#include +#include #include #include +#include + #include +#include +#include #include +#include +#include +#include #include +#include +#include +#include /// MPI type of BoutReal for communications #define PVEC_REAL_MPI_TYPE MPI_DOUBLE @@ -165,16 +185,14 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, // Check size of Y mesh if we've got multiple processors in Y if (num_local_y_points < num_y_guards and num_y_processors != 1) { return {false, - fmt::format(fmt::runtime(_( - "\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n")), + fmt::format(_f("\t -> ny/NYPE ({:d}/{:d} = {:d}) must be >= MYG ({:d})\n"), ny, num_y_processors, num_local_y_points, num_y_guards)}; } // Check branch cuts if ((jyseps1_1 + 1) % num_local_y_points != 0) { - return {false, - fmt::format(fmt::runtime(_("\t -> Leg region jyseps1_1+1 ({:d}) must be a " - "multiple of MYSUB ({:d})\n")), - jyseps1_1 + 1, num_local_y_points)}; + return {false, fmt::format(_f("\t -> Leg region jyseps1_1+1 ({:d}) must be a " + "multiple of MYSUB ({:d})\n"), + jyseps1_1 + 1, num_local_y_points)}; } if (jyseps2_1 != jyseps1_2) { @@ -183,18 +201,16 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((jyseps2_1 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> Core region jyseps2_1-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_1, jyseps1_1, jyseps2_1 - jyseps1_1, num_local_y_points)}; } if ((jyseps2_2 - jyseps1_2) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> Core region jyseps2_2-jyseps1_2 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_2, jyseps1_2, jyseps2_2 - jyseps1_2, num_local_y_points)}; } @@ -202,17 +218,15 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((ny_inner - jyseps2_1 - 1) % num_local_y_points != 0) { return {false, fmt::format( - fmt::runtime( - _("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + _f("\t -> leg region ny_inner-jyseps2_1-1 ({:d}-{:d}-1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), ny_inner, jyseps2_1, ny_inner - jyseps2_1 - 1, num_local_y_points)}; } if ((jyseps1_2 - ny_inner + 1) % num_local_y_points != 0) { return {false, fmt::format( - fmt::runtime( - _("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + _f("\t -> leg region jyseps1_2-ny_inner+1 ({:d}-{:d}+1 = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps1_2, ny_inner, jyseps1_2 - ny_inner + 1, num_local_y_points)}; } } else { @@ -220,9 +234,8 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((jyseps2_2 - jyseps1_1) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " - "be a multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> Core region jyseps2_2-jyseps1_1 ({:d}-{:d} = {:d}) must " + "be a multiple of MYSUB ({:d})\n"), jyseps2_2, jyseps1_1, jyseps2_2 - jyseps1_1, num_local_y_points)}; } } @@ -230,9 +243,8 @@ CheckMeshResult checkBoutMeshYDecomposition(int num_y_processors, int ny, if ((ny - jyseps2_2 - 1) % num_local_y_points != 0) { return { false, - fmt::format(fmt::runtime(_( - "\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " - "multiple of MYSUB ({:d})\n")), + fmt::format(_f("\t -> leg region ny-jyseps2_2-1 ({:d}-{:d}-1 = {:d}) must be a " + "multiple of MYSUB ({:d})\n"), ny, jyseps2_2, ny - jyseps2_2 - 1, num_local_y_points)}; } @@ -253,7 +265,7 @@ void BoutMesh::chooseProcessorSplit(Options& options) { .withDefault(1); if ((NPES % NXPE) != 0) { throw BoutException( - _("Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n"), + _f("Number of processors ({:d}) not divisible by NPs in x direction ({:d})\n"), NPES, NXPE); } @@ -266,7 +278,7 @@ void BoutMesh::chooseProcessorSplit(Options& options) { .withDefault(1); if ((NPES % NYPE) != 0) { throw BoutException( - _("Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n"), + _f("Number of processors ({:d}) not divisible by NPs in y direction ({:d})\n"), NPES, NYPE); } @@ -289,14 +301,14 @@ void BoutMesh::findProcessorSplit() { // Results in square domains const BoutReal ideal = sqrt(MX * NPES / static_cast(ny)); - output_info.write(_("Finding value for NXPE (ideal = {:f})\n"), ideal); + output_info.write(_f("Finding value for NXPE (ideal = {:f})\n"), ideal); for (int i = 1; i <= NPES; i++) { // Loop over all possibilities if ((NPES % i == 0) && // Processors divide equally (MX % i == 0) && // Mesh in X divides equally (ny % (NPES / i) == 0)) { // Mesh in Y divides equally - output_info.write(_("\tCandidate value: {:d}\n"), i); + output_info.write(_f("\tCandidate value: {:d}\n"), i); const int nyp = NPES / i; @@ -323,15 +335,15 @@ void BoutMesh::findProcessorSplit() { NYPE = NPES / NXPE; - output_progress.write(_("\tDomain split (NXPE={:d}, NYPE={:d}) into domains " - "(localNx={:d}, localNy={:d})\n"), + output_progress.write(_f("\tDomain split (NXPE={:d}, NYPE={:d}) into domains " + "(localNx={:d}, localNy={:d})\n"), NXPE, NYPE, MX / NXPE, ny / NYPE); } void BoutMesh::setDerivedGridSizes() { // Check that nx is large enough if (nx <= 2 * MXG) { - throw BoutException(_("Error: nx must be greater than 2 times MXG (2 * {:d})"), MXG); + throw BoutException(_f("Error: nx must be greater than 2 times MXG (2 * {:d})"), MXG); } GlobalNx = nx; @@ -356,8 +368,8 @@ void BoutMesh::setDerivedGridSizes() { MX = nx - 2 * MXG; MXSUB = MX / NXPE; if ((MX % NXPE) != 0) { - throw BoutException(_("Cannot split {:d} X points equally between {:d} processors\n"), - MX, NXPE); + throw BoutException( + _f("Cannot split {:d} X points equally between {:d} processors\n"), MX, NXPE); } // NOTE: No grid data reserved for Y boundary cells - copy from neighbours @@ -365,7 +377,7 @@ void BoutMesh::setDerivedGridSizes() { MYSUB = MY / NYPE; if ((MY % NYPE) != 0) { throw BoutException( - _("\tERROR: Cannot split {:d} Y points equally between {:d} processors\n"), MY, + _f("\tERROR: Cannot split {:d} Y points equally between {:d} processors\n"), MY, NYPE); } @@ -373,7 +385,7 @@ void BoutMesh::setDerivedGridSizes() { MZSUB = MZ / NZPE; if ((MZ % NZPE) != 0) { throw BoutException( - _("\tERROR: Cannot split {:d} Z points equally between {:d} processors\n"), MZ, + _f("\tERROR: Cannot split {:d} Z points equally between {:d} processors\n"), MZ, NZPE); } @@ -474,8 +486,8 @@ int BoutMesh::load() { if (!is_pow2(nz)) { // Should be a power of 2 for efficient FFTs output_warn.write( - _("WARNING: Number of toroidal points should be 2^n for efficient " - "FFT performance -- consider changing MZ ({:d}) if using FFTs\n"), + _f("WARNING: Number of toroidal points should be 2^n for efficient " + "FFT performance -- consider changing MZ ({:d}) if using FFTs\n"), nz); } } else { @@ -1556,7 +1568,7 @@ bool BoutMesh::lastX() const { return PE_XIND == NXPE - 1; } int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == NXPE - 1) { if (periodicX) { // Wrap around to first processor in X @@ -1568,8 +1580,7 @@ int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { proc = PROC_NUM(PE_XIND + 1, PE_YIND); } - mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get()); + mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get()); return 0; } @@ -1577,7 +1588,7 @@ int BoutMesh::sendXOut(BoutReal* buffer, int size, int tag) { int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == 0) { if (periodicX) { // Wrap around to last processor in X @@ -1589,8 +1600,7 @@ int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { proc = PROC_NUM(PE_XIND - 1, PE_YIND); } - mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get()); + mpi->MPI_Send(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get()); return 0; } @@ -1598,7 +1608,7 @@ int BoutMesh::sendXIn(BoutReal* buffer, int size, int tag) { comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == NXPE - 1) { if (periodicX) { // Wrap around to first processor in X @@ -1613,8 +1623,8 @@ comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { // Get a communications handle. Not fussy about size of arrays CommHandle* ch = get_handle(0, 0); - mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get(), ch->request); + mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get(), + ch->request); ch->in_progress = true; @@ -1624,7 +1634,7 @@ comm_handle BoutMesh::irecvXOut(BoutReal* buffer, int size, int tag) { comm_handle BoutMesh::irecvXIn(BoutReal* buffer, int size, int tag) { Timer timer("comms"); - int proc {-1}; + int proc{-1}; if (PE_XIND == 0) { if (periodicX) { // Wrap around to last processor in X @@ -1639,8 +1649,8 @@ comm_handle BoutMesh::irecvXIn(BoutReal* buffer, int size, int tag) { // Get a communications handle. Not fussy about size of arrays CommHandle* ch = get_handle(0, 0); - mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, - BoutComm::get(), ch->request); + mpi->MPI_Irecv(buffer, size, PVEC_REAL_MPI_TYPE, proc, tag, BoutComm::get(), + ch->request); ch->in_progress = true; diff --git a/src/mesh/mesh.cxx b/src/mesh/mesh.cxx index 8964a6b797..6278f2741a 100644 --- a/src/mesh/mesh.cxx +++ b/src/mesh/mesh.cxx @@ -1,14 +1,38 @@ +#include +#include #include +#include +#include #include #include +#include +#include +#include +#include +#include #include #include #include #include +#include +#include #include +#include +#include +#include +#include #include - -#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include #include "impls/bout/boutmesh.hxx" @@ -509,11 +533,11 @@ const std::vector Mesh::readInts(const std::string& name, int n) { if (source->hasVar(name)) { if (!source->get(this, result, name, n, 0)) { // Error reading - throw BoutException(_("Could not read integer array '{:s}'\n"), name.c_str()); + throw BoutException(_f("Could not read integer array '{:s}'\n"), name.c_str()); } } else { // Not found - throw BoutException(_("Missing integer array {:s}\n"), name.c_str()); + throw BoutException(_f("Missing integer array {:s}\n"), name.c_str()); } return result; @@ -537,7 +561,7 @@ Mesh::createDefaultCoordinates(const CELL_LOC location, const Region<>& Mesh::getRegion3D(const std::string& region_name) const { const auto found = regionMap3D.find(region_name); if (found == end(regionMap3D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap3D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap3D"), region_name); } return region3D[found->second]; } @@ -545,7 +569,7 @@ const Region<>& Mesh::getRegion3D(const std::string& region_name) const { size_t Mesh::getRegionID(const std::string& region_name) const { const auto found = regionMap3D.find(region_name); if (found == end(regionMap3D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap3D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap3D"), region_name); } return found->second; } @@ -553,7 +577,7 @@ size_t Mesh::getRegionID(const std::string& region_name) const { const Region& Mesh::getRegion2D(const std::string& region_name) const { const auto found = regionMap2D.find(region_name); if (found == end(regionMap2D)) { - throw BoutException(_("Couldn't find region {:s} in regionMap2D"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMap2D"), region_name); } return found->second; } @@ -561,7 +585,7 @@ const Region& Mesh::getRegion2D(const std::string& region_name) const { const Region& Mesh::getRegionPerp(const std::string& region_name) const { const auto found = regionMapPerp.find(region_name); if (found == end(regionMapPerp)) { - throw BoutException(_("Couldn't find region {:s} in regionMapPerp"), region_name); + throw BoutException(_f("Couldn't find region {:s} in regionMapPerp"), region_name); } return found->second; } @@ -580,8 +604,8 @@ bool Mesh::hasRegionPerp(const std::string& region_name) const { void Mesh::addRegion3D(const std::string& region_name, const Region<>& region) { if (regionMap3D.count(region_name)) { - throw BoutException(_("Trying to add an already existing region {:s} to regionMap3D"), - region_name); + throw BoutException( + _f("Trying to add an already existing region {:s} to regionMap3D"), region_name); } std::optional id; @@ -598,27 +622,28 @@ void Mesh::addRegion3D(const std::string& region_name, const Region<>& region) { regionMap3D[region_name] = id.value(); - output_verbose.write(_("Registered region 3D {:s}"), region_name); + output_verbose.write(_f("Registered region 3D {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } void Mesh::addRegion2D(const std::string& region_name, const Region& region) { if (regionMap2D.count(region_name)) { - throw BoutException(_("Trying to add an already existing region {:s} to regionMap2D"), - region_name); + throw BoutException( + _f("Trying to add an already existing region {:s} to regionMap2D"), region_name); } regionMap2D[region_name] = region; - output_verbose.write(_("Registered region 2D {:s}"), region_name); + output_verbose.write(_f("Registered region 2D {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } void Mesh::addRegionPerp(const std::string& region_name, const Region& region) { if (regionMapPerp.count(region_name)) { throw BoutException( - _("Trying to add an already existing region {:s} to regionMapPerp"), region_name); + _f("Trying to add an already existing region {:s} to regionMapPerp"), + region_name); } regionMapPerp[region_name] = region; - output_verbose.write(_("Registered region Perp {:s}"), region_name); + output_verbose.write(_f("Registered region Perp {:s}"), region_name); output_verbose << "\n:\t" << region.getStats() << "\n"; } diff --git a/src/solver/impls/petsc/petsc.cxx b/src/solver/impls/petsc/petsc.cxx index 13e4876048..1e1e05596b 100644 --- a/src/solver/impls/petsc/petsc.cxx +++ b/src/solver/impls/petsc/petsc.cxx @@ -807,7 +807,7 @@ int PetscSolver::init() { if (ierr != 0) { output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", - row, x, y, xi, yi, ind2, ind2 + n3d - 1); + row, col, x, y, xi, yi, ind2, ind2 + n3d - 1); } CHKERRQ(ierr); } diff --git a/src/solver/impls/snes/snes.cxx b/src/solver/impls/snes/snes.cxx index 2e4744eb44..31877f8aac 100644 --- a/src/solver/impls/snes/snes.cxx +++ b/src/solver/impls/snes/snes.cxx @@ -347,7 +347,7 @@ PetscErrorCode SNESSolver::FDJinitialise() { if (ierr != PETSC_SUCCESS) { output.write("ERROR: {} {} : ({}, {}) -> ({}, {}) : {} -> {}\n", row, - x, y, xi, yi, ind2, ind2 + n3d - 1); + col, x, y, xi, yi, ind2, ind2 + n3d - 1); } CHKERRQ(ierr); } diff --git a/src/solver/impls/split-rk/split-rk.cxx b/src/solver/impls/split-rk/split-rk.cxx index fd9a99a6c5..ac4d62ebe2 100644 --- a/src/solver/impls/split-rk/split-rk.cxx +++ b/src/solver/impls/split-rk/split-rk.cxx @@ -1,5 +1,21 @@ #include "split-rk.hxx" +#include "bout/array.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/globals.hxx" +#include "bout/openmpwrap.hxx" +#include "bout/options.hxx" +#include "bout/output.hxx" +#include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" +#include "bout/utils.hxx" + +#include +#include + SplitRK::SplitRK(Options* opts) : Solver(opts), nstages((*options)["nstages"] .doc("Number of stages in RKL step. Must be > 1") @@ -72,7 +88,7 @@ int SplitRK::init() { ASSERT0(ninternal_steps > 0); timestep = getOutputTimestep() / ninternal_steps; - output.write(_("\tUsing a timestep {:e}\n"), timestep); + output.write(_f("\tUsing a timestep {:e}\n"), timestep); return 0; } diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index ac3d632419..c6e9b464fe 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -24,23 +24,38 @@ #include "bout/array.hxx" #include "bout/assert.hxx" +#include "bout/bout_types.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" #include "bout/field_factory.hxx" +#include "bout/globals.hxx" #include "bout/initialprofiles.hxx" #include "bout/interpolation.hxx" +#include "bout/monitor.hxx" #include "bout/msg_stack.hxx" +#include "bout/options.hxx" #include "bout/output.hxx" #include "bout/region.hxx" #include "bout/solver.hxx" +#include "bout/sys/gettext.hxx" #include "bout/sys/timer.hxx" #include "bout/sys/uuid.h" +#include "bout/unused.hxx" +#include "bout/utils.hxx" +#include "bout/vector2d.hxx" +#include "bout/vector3d.hxx" + +#include #include -#include #include +#include #include #include +#include +#include // Implementations: #include "impls/adams_bashforth/adams_bashforth.hxx" @@ -507,11 +522,11 @@ int Solver::solve(int nout, BoutReal timestep) { finaliseMonitorPeriods(nout, timestep); output_progress.write( - _("Solver running for {:d} outputs with output timestep of {:e}\n"), nout, + _f("Solver running for {:d} outputs with output timestep of {:e}\n"), nout, timestep); if (default_monitor_period > 1) { output_progress.write( - _("Solver running for {:d} outputs with monitor timestep of {:e}\n"), + _f("Solver running for {:d} outputs with monitor timestep of {:e}\n"), nout / default_monitor_period, timestep * default_monitor_period); } @@ -537,7 +552,7 @@ int Solver::solve(int nout, BoutReal timestep) { } time_t start_time = time(nullptr); - output_progress.write(_("\nRun started at : {:s}\n"), toString(start_time)); + output_progress.write(_f("\nRun started at : {:s}\n"), toString(start_time)); Timer timer("run"); // Start timer @@ -583,7 +598,7 @@ int Solver::solve(int nout, BoutReal timestep) { status = run(); time_t end_time = time(nullptr); - output_progress.write(_("\nRun finished at : {:s}\n"), toString(end_time)); + output_progress.write(_f("\nRun finished at : {:s}\n"), toString(end_time)); output_progress.write(_("Run time : ")); int dt = end_time - start_time; @@ -766,7 +781,7 @@ BoutReal Solver::adjustMonitorPeriods(Monitor* new_monitor) { } if (!isMultiple(internal_timestep, new_monitor->timestep)) { - throw BoutException(_("Couldn't add Monitor: {:g} is not a multiple of {:g}!"), + throw BoutException(_f("Couldn't add Monitor: {:g} is not a multiple of {:g}!"), internal_timestep, new_monitor->timestep); } @@ -782,8 +797,8 @@ BoutReal Solver::adjustMonitorPeriods(Monitor* new_monitor) { if (initialised) { throw BoutException( - _("Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) " - "after init is called!"), + _f("Solver::addMonitor: Cannot reduce timestep (from {:g} to {:g}) " + "after init is called!"), internal_timestep, new_monitor->timestep); } @@ -883,7 +898,7 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { monitor.monitor->call(this, simtime, iter / monitor.monitor->period, NOUT / monitor.monitor->period); if (ret != 0) { - throw BoutException(_("Monitor signalled to quit (return code {})"), ret); + throw BoutException(_f("Monitor signalled to quit (return code {})"), ret); } // Write the monitor's diagnostics to the main output file Options monitor_dump; @@ -905,7 +920,7 @@ int Solver::call_monitors(BoutReal simtime, int iter, int NOUT) { for (const auto& monitor : monitors) { monitor.monitor->cleanup(); } - output_error.write(_("Monitor signalled to quit (exception {})\n"), e.what()); + output_error.write(_f("Monitor signalled to quit (exception {})\n"), e.what()); throw; } @@ -1234,13 +1249,13 @@ void Solver::load_derivs(BoutReal* udata) { void Solver::save_vars(BoutReal* udata) { for (const auto& f : f2d) { if (!f.var->isAllocated()) { - throw BoutException(_("Variable '{:s}' not initialised"), f.name); + throw BoutException(_f("Variable '{:s}' not initialised"), f.name); } } for (const auto& f : f3d) { if (!f.var->isAllocated()) { - throw BoutException(_("Variable '{:s}' not initialised"), f.name); + throw BoutException(_f("Variable '{:s}' not initialised"), f.name); } } @@ -1283,8 +1298,8 @@ void Solver::save_derivs(BoutReal* dudata) { // Make sure 3D fields are at the correct cell location for (const auto& f : f3d) { if (f.var->getLocation() != (f.F_var)->getLocation()) { - throw BoutException(_("Time derivative at wrong location - Field is at {:s}, " - "derivative is at {:s} for field '{:s}'\n"), + throw BoutException(_f("Time derivative at wrong location - Field is at {:s}, " + "derivative is at {:s} for field '{:s}'\n"), toString(f.var->getLocation()), toString(f.F_var->getLocation()), f.name); } @@ -1488,7 +1503,7 @@ void Solver::post_rhs(BoutReal UNUSED(t)) { #if CHECK > 0 for (const auto& f : f3d) { if (!f.F_var->isAllocated()) { - throw BoutException(_("Time derivative for variable '{:s}' not set"), f.name); + throw BoutException(_f("Time derivative for variable '{:s}' not set"), f.name); } } #endif diff --git a/src/sys/expressionparser.cxx b/src/sys/expressionparser.cxx index 1c381b26df..5573846c4f 100644 --- a/src/sys/expressionparser.cxx +++ b/src/sys/expressionparser.cxx @@ -22,12 +22,29 @@ * **************************************************************************/ -#include - +#include "bout/sys/expressionparser.hxx" + +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include #include - -#include "bout/sys/gettext.hxx" -#include "bout/utils.hxx" +#include using std::list; using std::string; @@ -183,7 +200,7 @@ FieldGeneratorPtr FieldBinary::clone(const list args) { bool toBool(BoutReal rval) { int ival = ROUND(rval); if ((fabs(rval - static_cast(ival)) > 1e-3) or (ival < 0) or (ival > 1)) { - throw BoutException(_("Boolean operator argument {:e} is not a bool"), rval); + throw BoutException(_f("Boolean operator argument {:e} is not a bool"), rval); } return ival == 1; } @@ -302,11 +319,6 @@ ExpressionParser::fuzzyFind(const std::string& name, FieldGeneratorPtr ExpressionParser::parseIdentifierExpr(LexInfo& lex) const { // Make a nice error message if we couldn't find the identifier const auto generatorNotFoundErrorMessage = [&](const std::string& name) -> std::string { - const std::string message_template = _( - R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you -may need to change your input file. -{})"); - // Start position of the current identifier: by this point, we've either // moved one character past the token, or we're still at the start const auto start = @@ -326,16 +338,19 @@ may need to change your input file. [](const auto& match) -> bool { return match.distance == 0; }); // No matches, just point out the error + std::string error_message = fmt::format( + _f( + R"(Couldn't find generator '{}'. BOUT++ expressions are now case-sensitive, so you +may need to change your input file. +{})"), + name, problem_bit); if (possible_matches.empty()) { - return fmt::format(fmt::runtime(message_template), name, problem_bit); + return error_message; } // Give the first suggestion as a possible alternative - std::string error_message = - fmt::format(fmt::runtime(message_template), name, problem_bit); - error_message += - fmt::format(fmt::runtime(_("\n {1: ^{2}}{0}\n Did you mean '{0}'?")), - possible_matches.begin()->name, "", start); + error_message += fmt::format(_f("\n {1: ^{2}}{0}\n Did you mean '{0}'?"), + possible_matches.begin()->name, "", start); return error_message; }; diff --git a/src/sys/options.cxx b/src/sys/options.cxx index fb90868081..d409a2e1b1 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -107,10 +107,10 @@ Options::Options(InitializerList values, Options* parent_instance, Options& Options::operator[](const std::string& name) { if (isValue()) { - throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " - "value, not a section.\n" - "This is likely the result of clashing input options, and you " - "may have to rename one of them.\n"), + throw BoutException(_f("Trying to index Option '{0}' with '{1}', but '{0}' is a " + "value, not a section.\n" + "This is likely the result of clashing input options, and you " + "may have to rename one of them.\n"), full_name, name); } @@ -144,10 +144,10 @@ Options& Options::operator[](const std::string& name) { const Options& Options::operator[](const std::string& name) const { if (isValue()) { - throw BoutException(_("Trying to index Option '{0}' with '{1}', but '{0}' is a " - "value, not a section.\n" - "This is likely the result of clashing input options, and you " - "may have to rename one of them.\n"), + throw BoutException(_f("Trying to index Option '{0}' with '{1}', but '{0}' is a " + "value, not a section.\n" + "This is likely the result of clashing input options, and you " + "may have to rename one of them.\n"), full_name, name); } @@ -165,7 +165,7 @@ const Options& Options::operator[](const std::string& name) const { auto child = children.find(name); if (child == children.end()) { // Doesn't exist - throw BoutException(_("Option {:s}:{:s} does not exist"), full_name, name); + throw BoutException(_f("Option {:s}:{:s} does not exist"), full_name, name); } return child->second; @@ -369,7 +369,7 @@ double parseExpression(const Options::ValueType& value, const Options* options, return gen->generate({}); } catch (ParseException& error) { // Convert any exceptions to something a bit more useful - throw BoutException(_("Couldn't get {} from option {:s} = '{:s}': {}"), type, + throw BoutException(_f("Couldn't get {} from option {:s} = '{:s}': {}"), type, full_name, bout::utils::variantToString(value), error.what()); } } @@ -377,7 +377,7 @@ double parseExpression(const Options::ValueType& value, const Options* options, /// Helper function to print `key = value` with optional source template void printNameValueSourceLine(const Options& option, const T& value) { - output_info.write(_("\tOption {} = {}"), option.str(), value); + output_info.write(_f("\tOption {} = {}"), option.str(), value); if (option.hasAttribute("source")) { // Specify the source of the setting output_info.write(" ({})", @@ -390,7 +390,7 @@ void printNameValueSourceLine(const Options& option, const T& value) { template <> std::string Options::as(const std::string& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } // Mark this option as used @@ -406,7 +406,7 @@ std::string Options::as(const std::string& UNUSED(similar_to)) cons template <> int Options::as(const int& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } int result = 0; @@ -426,7 +426,7 @@ int Options::as(const int& UNUSED(similar_to)) const { } else { // Another type which can't be converted - throw BoutException(_("Value for option {:s} is not an integer"), full_name); + throw BoutException(_f("Value for option {:s} is not an integer"), full_name); } // Convert to int by rounding @@ -434,7 +434,7 @@ int Options::as(const int& UNUSED(similar_to)) const { // Check that the value is close to an integer if (fabs(rval - static_cast(result)) > 1e-3) { - throw BoutException(_("Value for option {:s} = {:e} is not an integer"), full_name, + throw BoutException(_f("Value for option {:s} = {:e} is not an integer"), full_name, rval); } } @@ -449,7 +449,7 @@ int Options::as(const int& UNUSED(similar_to)) const { template <> BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } BoutReal result = BoutNaN; @@ -464,7 +464,7 @@ BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { result = parseExpression(value, this, "BoutReal", full_name); } else { - throw BoutException(_("Value for option {:s} cannot be converted to a BoutReal"), + throw BoutException(_f("Value for option {:s} cannot be converted to a BoutReal"), full_name); } @@ -479,7 +479,7 @@ BoutReal Options::as(const BoutReal& UNUSED(similar_to)) const { template <> bool Options::as(const bool& UNUSED(similar_to)) const { if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); + throw BoutException(_f("Option {:s} has no value"), full_name); } bool result = false; @@ -494,12 +494,12 @@ bool Options::as(const bool& UNUSED(similar_to)) const { // Check that the result is either close to 1 (true) or close to 0 (false) const int ival = ROUND(rval); if ((fabs(rval - static_cast(ival)) > 1e-3) or (ival < 0) or (ival > 1)) { - throw BoutException(_("Value for option {:s} = {:e} is not a bool"), full_name, + throw BoutException(_f("Value for option {:s} = {:e} is not a bool"), full_name, rval); } result = ival == 1; } else { - throw BoutException(_("Value for option {:s} cannot be converted to a bool"), + throw BoutException(_f("Value for option {:s} cannot be converted to a bool"), full_name); } @@ -579,7 +579,7 @@ Field3D Options::as(const Field3D& similar_to) const { localmesh->LocalNz); } - throw BoutException(_("Value for option {:s} cannot be converted to a Field3D"), + throw BoutException(_f("Value for option {:s} cannot be converted to a Field3D"), full_name); } @@ -631,7 +631,7 @@ Field2D Options::as(const Field2D& similar_to) const { } } - throw BoutException(_("Value for option {:s} cannot be converted to a Field2D"), + throw BoutException(_f("Value for option {:s} cannot be converted to a Field2D"), full_name); } @@ -713,7 +713,7 @@ FieldPerp Options::as(const FieldPerp& similar_to) const { // to select a region from it using Mesh e.g. if this // is from the input grid file. } - throw BoutException(_("Value for option {:s} cannot be converted to a FieldPerp"), + throw BoutException(_f("Value for option {:s} cannot be converted to a FieldPerp"), full_name); } @@ -755,14 +755,13 @@ namespace { template T as_amt(const Options& self, const T& similar_to) { if (self.isSection()) { - throw BoutException(_("Option {:s} has no value"), self.str()); + throw BoutException(_f("Option {:s} has no value"), self.str()); } const T result = bout::utils::visit( ConvertContainer{ - fmt::format( - fmt::runtime(_("Value for option {:s} cannot be converted to an {}")), - self.str(), bout::utils::typeName()), + fmt::format(_f("Value for option {:s} cannot be converted to an {}"), + self.str(), bout::utils::typeName()), similar_to}, self.value); @@ -984,43 +983,6 @@ std::vector Options::getShape() const { return lazy_shape; } -fmt::format_parse_context::iterator -bout::details::OptionsFormatterBase::parse(fmt::format_parse_context& ctx) { - - const auto* closing_brace = std::find(ctx.begin(), ctx.end(), '}'); - std::for_each(ctx.begin(), closing_brace, [&](auto ctx_opt) { - switch (ctx_opt) { - case 'd': - docstrings = true; - break; - case 'i': - inline_section_names = true; - break; - case 'k': - key_only = true; - break; - case 's': - source = true; - break; - case 'u': - unused = true; - break; - default: - throw fmt::format_error("invalid format for 'Options'"); - } - }); - - // Keep a copy of the format string (without the last '}') so we can - // pass it down to the subsections. - const auto size = std::distance(ctx.begin(), closing_brace); - format_string.reserve(size + 3); - format_string.assign("{:"); - format_string.append(ctx.begin(), closing_brace); - format_string.push_back('}'); - - return closing_brace; -} - fmt::format_context::iterator bout::details::OptionsFormatterBase::format(const Options& options, fmt::format_context& ctx) const { @@ -1099,22 +1061,20 @@ bout::details::OptionsFormatterBase::format(const Options& options, // Get all the child values first for (const auto& child : children) { if (child.second.isValue()) { - fmt::format_to(ctx.out(), fmt::runtime(format_string), child.second); + format(child.second, ctx); fmt::format_to(ctx.out(), "\n"); } } // Now descend the tree, accumulating subsections for (const auto& subsection : options.subsections()) { - fmt::format_to(ctx.out(), fmt::runtime(format_string), *subsection.second); + format(*subsection.second, ctx); } return ctx.out(); } -std::string toString(const Options& value) { - return fmt::format(fmt::runtime("{}"), value); -} +std::string toString(const Options& value) { return fmt::format("{}", value); } namespace bout { void checkForUnusedOptions() { @@ -1160,7 +1120,7 @@ void checkForUnusedOptions(const Options& options, const std::string& data_dir, } possible_misspellings += fmt::format("\nUnused option '{}', did you mean:\n", key); for (const auto& match : fuzzy_matches) { - possible_misspellings += fmt::format(fmt::runtime("\t{:idk}\n"), match.match); + possible_misspellings += fmt::format("\t{:idk}\n", match.match); } } @@ -1170,10 +1130,7 @@ void checkForUnusedOptions(const Options& options, const std::string& data_dir, ? "" : fmt::format("Suggested alternatives:\n{}", possible_misspellings); - // Raw string to help with the formatting of the message, and a - // separate variable so clang-format doesn't barf on the - // exception - const std::string unused_message = _(R"( + throw BoutException(_f(R"( There were unused input options: ----- {:i} @@ -1196,9 +1153,8 @@ turn off this check for unused options. You can always set 'input:validate=true' to check inputs without running the full simulation. -{})"); - - throw BoutException(unused_message, unused, data_dir, option_file, +{})"), + unused, data_dir, option_file, unused.getChildren().begin()->first, additional_info); } } diff --git a/src/sys/options/options_ini.cxx b/src/sys/options/options_ini.cxx index b51af8fdcf..b5f813d168 100644 --- a/src/sys/options/options_ini.cxx +++ b/src/sys/options/options_ini.cxx @@ -52,8 +52,17 @@ #include "options_ini.hxx" #include +#include +#include #include +#include +#include +#include +#include + +#include "fmt/format.h" + using namespace std; /************************************************************************** @@ -65,7 +74,7 @@ void OptionINI::read(Options* options, const string& filename) { fin.open(filename.c_str()); if (!fin.good()) { - throw BoutException(_("\tOptions file '{:s}' not found\n"), filename); + throw BoutException(_f("\tOptions file '{:s}' not found\n"), filename); } Options* section = options; // Current section @@ -142,7 +151,7 @@ void OptionINI::read(Options* options, const string& filename) { // Add this to the current section section->set(key, value, filename); } // section test - } // buffer.empty + } // buffer.empty } while (!fin.eof()); fin.close(); @@ -153,11 +162,11 @@ void OptionINI::write(Options* options, const std::string& filename) { fout.open(filename, ios::out | ios::trunc); if (!fout.good()) { - throw BoutException(_("Could not open output file '{:s}'\n"), filename); + throw BoutException(_f("Could not open output file '{:s}'\n"), filename); } // Call recursive function to write to file - fout << fmt::format(fmt::runtime("{:uds}"), *options); + fout << fmt::format("{:uds}", *options); fout.close(); } @@ -193,10 +202,10 @@ void OptionINI::parse(const string& buffer, string& key, string& value) { value = trim(buffer.substr(startpos + 1), " \t\r\n\""); if (key.empty()) { - throw BoutException(_("\tEmpty key\n\tLine: {:s}"), buffer); + throw BoutException(_f("\tEmpty key\n\tLine: {:s}"), buffer); } if (key.find(':') != std::string::npos) { - throw BoutException(_("\tKey must not contain ':' character\n\tLine: {:s}"), buffer); + throw BoutException(_f("\tKey must not contain ':' character\n\tLine: {:s}"), buffer); } } diff --git a/src/sys/optionsreader.cxx b/src/sys/optionsreader.cxx index e16df31c97..3ce668a421 100644 --- a/src/sys/optionsreader.cxx +++ b/src/sys/optionsreader.cxx @@ -1,6 +1,9 @@ #include #include +#include #include +#include +#include #include // Interface for option file parsers @@ -9,7 +12,9 @@ // Individual parsers #include "options/options_ini.hxx" -#include +#include +#include +#include OptionsReader* OptionsReader::instance = nullptr; @@ -36,7 +41,7 @@ void OptionsReader::write(Options* options, const std::string& filename) { throw BoutException("OptionsReader::write passed empty filename\n"); } - output_info.write(_("Writing options to file {:s}\n"), filename); + output_info.write(_f("Writing options to file {:s}\n"), filename); OptionINI{}.write(options, filename); } @@ -105,7 +110,7 @@ void OptionsReader::parseCommandLine(Options* options, size_t endpos = buffer.find_last_of('='); if (startpos != endpos) { - throw BoutException(_("\tMultiple '=' in command-line argument '{:s}'\n"), + throw BoutException(_f("\tMultiple '=' in command-line argument '{:s}'\n"), buffer); } @@ -121,7 +126,7 @@ void OptionsReader::parseCommandLine(Options* options, } if (key.empty() || value.empty()) { - throw BoutException(_("\tEmpty key or value in command line '{:s}'\n"), buffer); + throw BoutException(_f("\tEmpty key or value in command line '{:s}'\n"), buffer); } options->set(key, value, _("Command line")); diff --git a/tests/unit/sys/test_options.cxx b/tests/unit/sys/test_options.cxx index f0256b6aa7..fb3e6a62c5 100644 --- a/tests/unit/sys/test_options.cxx +++ b/tests/unit/sys/test_options.cxx @@ -1112,7 +1112,7 @@ TEST_F(OptionsTest, FormatValue) { const std::string expected = "value1 = 4 # type: int, doc: This is a value, source: some test"; - EXPECT_EQ(expected, fmt::format(fmt::runtime("{:ds}"), options["value1"])); + EXPECT_EQ(expected, fmt::format("{:ds}", options["value1"])); } TEST_F(OptionsTest, FormatDefault) { @@ -1140,7 +1140,7 @@ value4 = 3.2 value6 = 12 )"; - EXPECT_EQ(fmt::format(fmt::runtime("{}"), option), expected); + EXPECT_EQ(fmt::format("{}", option), expected); } TEST_F(OptionsTest, FormatDocstrings) { @@ -1171,7 +1171,7 @@ value4 = 3.2 value6 = 12 )"; - EXPECT_EQ(fmt::format(fmt::runtime("{:d}"), option), expected); + EXPECT_EQ(fmt::format("{:d}", option), expected); } TEST_F(OptionsTest, FormatDocstringsAndInline) { @@ -1194,9 +1194,9 @@ section2:subsection1:value4 = 3.2 section3:subsection2:value6 = 12 )"; - EXPECT_EQ(fmt::format(fmt::runtime("{:di}"), option), expected); + EXPECT_EQ(fmt::format("{:di}", option), expected); // Order of format spec shouldn't matter - EXPECT_EQ(fmt::format(fmt::runtime("{:id}"), option), expected); + EXPECT_EQ(fmt::format("{:id}", option), expected); } TEST_F(OptionsTest, FormatDocstringsAndInlineKeysOnly) { @@ -1221,16 +1221,16 @@ section2:subsection1:value4 section3:subsection2:value6 # source: a test )"; - EXPECT_EQ(fmt::format(fmt::runtime("{:ksdi}"), option), expected); + EXPECT_EQ(fmt::format("{:ksdi}", option), expected); // Order of format spec shouldn't matter - EXPECT_EQ(fmt::format(fmt::runtime("{:idsk}"), option), expected); + EXPECT_EQ(fmt::format("{:idsk}", option), expected); } TEST_F(OptionsTest, FormatUnused) { Options option{{"section1", {{"value1", 42}}}}; std::string expected = "section1:value1\t\t# unused value (NOT marked conditionally used)\n"; - EXPECT_EQ(fmt::format(fmt::runtime("{:iku}"), option), expected); + EXPECT_EQ(fmt::format("{:iku}", option), expected); } TEST_F(OptionsTest, FormatConditionallyUsed) { @@ -1238,7 +1238,7 @@ TEST_F(OptionsTest, FormatConditionallyUsed) { option.setConditionallyUsed(); std::string expected = "section1:value1\t\t# unused value (marked conditionally used)\n"; - EXPECT_EQ(fmt::format(fmt::runtime("{:iku}"), option), expected); + EXPECT_EQ(fmt::format("{:iku}", option), expected); } TEST_F(OptionsTest, GetUnused) { diff --git a/tests/unit/sys/test_output.cxx b/tests/unit/sys/test_output.cxx index 2f13df9cb7..5beb12c0c2 100644 --- a/tests/unit/sys/test_output.cxx +++ b/tests/unit/sys/test_output.cxx @@ -332,7 +332,7 @@ TEST_F(OutputTest, FormatInd3DInvalid) { Ind3D ind(11, 2, 3); Output local_output; - EXPECT_THROW(local_output.write("{:b}", ind), fmt::format_error); + EXPECT_THROW(local_output.write(fmt::runtime("{:b}"), ind), fmt::format_error); } TEST_F(OutputTest, FormatInd2Ddefault) { diff --git a/tools/pylib/_boutpp_build/boutcpp.pxd.jinja b/tools/pylib/_boutpp_build/boutcpp.pxd.jinja index 8f838b864c..40aecb923e 100644 --- a/tools/pylib/_boutpp_build/boutcpp.pxd.jinja +++ b/tools/pylib/_boutpp_build/boutcpp.pxd.jinja @@ -162,7 +162,7 @@ cdef extern from "helper.h": cdef extern from "bout/output.hxx": cppclass ConditionalOutput: - void write(const char * str, const char * str) + void write(const char * str) ConditionalOutput output_info cdef extern from "bout/vecops.hxx": diff --git a/tools/pylib/_boutpp_build/boutpp.pyx.jinja b/tools/pylib/_boutpp_build/boutpp.pyx.jinja index d6f0601f1c..2710c9f21f 100644 --- a/tools/pylib/_boutpp_build/boutpp.pyx.jinja +++ b/tools/pylib/_boutpp_build/boutpp.pyx.jinja @@ -1709,7 +1709,7 @@ def print(*args, sep=" ", end="\n"): cdef _print(value): cdef c.string str_ = value.encode() - c.output_info.write("{:s}", str_.c_str()) + c.output_info.write(str_.c_str()) {% include "helper.py" %} From 5269a7d59be6af4892062312853386aa2231d71c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 27 Feb 2026 09:44:51 +0000 Subject: [PATCH 259/461] Add `operator+(Region&&, const Region&)` to make `clang-tidy` happy --- include/bout/region.hxx | 6 ++++++ tests/unit/fake_mesh.hxx | 5 +++-- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/include/bout/region.hxx b/include/bout/region.hxx index bb1cf82bf1..181582c7d6 100644 --- a/include/bout/region.hxx +++ b/include/bout/region.hxx @@ -972,6 +972,12 @@ Region operator+(const Region& lhs, const Region& rhs) { return Region(indices); } +template +Region operator+(Region&& lhs, const Region& rhs) { + lhs += rhs; + return std::move(lhs); +} + /// Returns a new region based on input but with indices offset by /// a constant template diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 7c5d0b3637..b79f5b0593 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -239,7 +240,7 @@ public: addRegion2D("RGN_BNDRY", std::accumulate(begin(boundary_names), end(boundary_names), Region{}, [&](Region a, const std::string& b) { - return a + getRegion2D(b); + return std::move(a) + getRegion2D(b); }) .unique()); @@ -247,7 +248,7 @@ public: std::accumulate(begin(boundary_names), end(boundary_names), Region{}, [this](Region a, const std::string& b) { - return a + getRegion3D(b); + return std::move(a) + getRegion3D(b); }) .unique()); addRegionPerp("RGN_BNDRY", From 570a76397481f17495a0735e34f406994638229e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:37:22 +0000 Subject: [PATCH 260/461] Bump ZedThree/clang-tidy-review from 0.23.0 to 0.23.1 Bumps [ZedThree/clang-tidy-review](https://github.com/zedthree/clang-tidy-review) from 0.23.0 to 0.23.1. - [Release notes](https://github.com/zedthree/clang-tidy-review/releases) - [Changelog](https://github.com/ZedThree/clang-tidy-review/blob/master/CHANGELOG.md) - [Commits](https://github.com/zedthree/clang-tidy-review/compare/v0.23.0...v0.23.1) --- updated-dependencies: - dependency-name: ZedThree/clang-tidy-review dependency-version: 0.23.1 dependency-type: direct:production update-type: version-update:semver-patch ... Signed-off-by: dependabot[bot] --- .github/workflows/clang-tidy-review.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 087c910987..360a162456 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -21,7 +21,7 @@ jobs: submodules: true - name: Run clang-tidy - uses: ZedThree/clang-tidy-review@v0.23.0 + uses: ZedThree/clang-tidy-review@v0.23.1 id: review with: build_dir: build @@ -46,4 +46,4 @@ jobs: -DBOUT_UPDATE_GIT_SUBMODULE=OFF - name: Upload clang-tidy fixes - uses: ZedThree/clang-tidy-review/upload@v0.23.0 + uses: ZedThree/clang-tidy-review/upload@v0.23.1 From 53f838c9713c33213cd90031d863d48531a0195e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:37:26 +0000 Subject: [PATCH 261/461] Bump actions/cache from 4 to 5 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/tests.yml | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index e45e082232..bd59fede37 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -168,7 +168,7 @@ jobs: python -m pip install -r requirements.txt - name: Cache Zenodo test data - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: build/tests/integrated/test-fci-mpi/grid.fci.nc # If we update the test, invalidate the cache @@ -205,7 +205,7 @@ jobs: submodules: true - name: Cache Zenodo test data - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: build/tests/integrated/test-fci-mpi/grid.fci.nc # If we update the test, invalidate the cache @@ -228,7 +228,7 @@ jobs: submodules: true - name: Cache Zenodo test data - uses: actions/cache@v4 + uses: actions/cache@v5 with: path: build/tests/integrated/test-fci-mpi/grid.fci.nc # If we update the test, invalidate the cache From 518c5a5478fbc8962dacea19df6ef02a42d66b94 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:37:30 +0000 Subject: [PATCH 262/461] Bump externalpackages/boutdata from `962d5a5` to `0e06267` Bumps [externalpackages/boutdata](https://github.com/boutproject/boutdata) from `962d5a5` to `0e06267`. - [Release notes](https://github.com/boutproject/boutdata/releases) - [Commits](https://github.com/boutproject/boutdata/compare/962d5a58b8e6133a2977c3e44f1d4751a11d2b1c...0e0626716f7fec01d318c427a96e596055540efe) --- updated-dependencies: - dependency-name: externalpackages/boutdata dependency-version: 0e0626716f7fec01d318c427a96e596055540efe dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- externalpackages/boutdata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externalpackages/boutdata b/externalpackages/boutdata index 962d5a58b8..0e0626716f 160000 --- a/externalpackages/boutdata +++ b/externalpackages/boutdata @@ -1 +1 @@ -Subproject commit 962d5a58b8e6133a2977c3e44f1d4751a11d2b1c +Subproject commit 0e0626716f7fec01d318c427a96e596055540efe From a34c4fa1821fac4076804c6e53cdf807cff8b4d7 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:37:33 +0000 Subject: [PATCH 263/461] Bump externalpackages/googletest from `9156d4c` to `73a63ea` Bumps [externalpackages/googletest](https://github.com/google/googletest) from `9156d4c` to `73a63ea`. - [Release notes](https://github.com/google/googletest/releases) - [Commits](https://github.com/google/googletest/compare/9156d4caac880b513264ecbe0aa4746a3fead3d7...73a63ea05dc8ca29ec1d2c1d66481dd0de1950f1) --- updated-dependencies: - dependency-name: externalpackages/googletest dependency-version: 73a63ea05dc8ca29ec1d2c1d66481dd0de1950f1 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- externalpackages/googletest | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externalpackages/googletest b/externalpackages/googletest index 9156d4caac..73a63ea05d 160000 --- a/externalpackages/googletest +++ b/externalpackages/googletest @@ -1 +1 @@ -Subproject commit 9156d4caac880b513264ecbe0aa4746a3fead3d7 +Subproject commit 73a63ea05dc8ca29ec1d2c1d66481dd0de1950f1 From 9f618b80cc39a42f4af190bfcf1be5d297363070 Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 27 Feb 2026 19:37:34 +0000 Subject: [PATCH 264/461] Bump actions/checkout from 5 to 6 Bumps [actions/checkout](https://github.com/actions/checkout) from 5 to 6. - [Release notes](https://github.com/actions/checkout/releases) - [Changelog](https://github.com/actions/checkout/blob/main/CHANGELOG.md) - [Commits](https://github.com/actions/checkout/compare/v5...v6) --- updated-dependencies: - dependency-name: actions/checkout dependency-version: '6' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/auto-formatting.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml index f54b012298..b7f1584515 100644 --- a/.github/workflows/auto-formatting.yml +++ b/.github/workflows/auto-formatting.yml @@ -10,7 +10,7 @@ jobs: runs-on: ubuntu-latest steps: # Checkout the pull request branch, also include all history - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 with: ref: ${{ github.head_ref }} fetch-depth: 0 From 96586162257f922db352a2fbef25b5065cb21f57 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 23 Feb 2023 13:52:03 +0100 Subject: [PATCH 265/461] enable openmp for sundials if it is enabled for BOUT++ --- cmake/SetupBOUTThirdParty.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/cmake/SetupBOUTThirdParty.cmake b/cmake/SetupBOUTThirdParty.cmake index 850809e856..9e6bdc6d2a 100644 --- a/cmake/SetupBOUTThirdParty.cmake +++ b/cmake/SetupBOUTThirdParty.cmake @@ -362,7 +362,7 @@ if(BOUT_USE_SUNDIALS) CACHE BOOL "" FORCE ) set(ENABLE_OPENMP - OFF + ${BOUT_USE_OPENMP} CACHE BOOL "" FORCE ) if(BUILD_SHARED_LIBS) From e96744a44066d45f0a740de343d05c3c09b87aac Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 18 Mar 2024 17:29:18 +0100 Subject: [PATCH 266/461] Inherit applyParallelBoundary functions --- include/bout/field3d.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index fad4815b92..d1be32dd84 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -496,6 +496,7 @@ public: /// Note: does not just copy values in boundary region. void setBoundaryTo(const Field3D& f3d); + using FieldData::applyParallelBoundary; void applyParallelBoundary() override; void applyParallelBoundary(BoutReal t) override; void applyParallelBoundary(const std::string& condition) override; From ab783f0fe4554db8f541763353bb7528c6777ae6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 6 Nov 2023 16:30:06 +0100 Subject: [PATCH 267/461] Make Field2d and Field3D more similar Useful for templates --- include/bout/field2d.hxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 92658f1bbf..3bafe3b116 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -133,8 +133,9 @@ public: return *this; } - /// Check if this field has yup and ydown fields + /// Dummy functions to increase portability bool hasParallelSlices() const { return true; } + void calcParallelSlices() const {} Field2D& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } const Field2D& yup(std::vector::size_type UNUSED(index) = 0) const { From fc2decea8723e1ac868796fd02c1a381754ac47b Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 27 Mar 2024 13:30:54 +0100 Subject: [PATCH 268/461] Add more dummy functions to field2d Allows to write code for Field3D, that also works for Field2D --- include/bout/field2d.hxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 3bafe3b116..6e82526bca 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -136,6 +136,8 @@ public: /// Dummy functions to increase portability bool hasParallelSlices() const { return true; } void calcParallelSlices() const {} + void clearParallelSlices() {} + int numberParallelSlices() { return 0; } Field2D& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } const Field2D& yup(std::vector::size_type UNUSED(index) = 0) const { @@ -275,7 +277,7 @@ public: friend void swap(Field2D& first, Field2D& second) noexcept; - int size() const override { return nx * ny; }; + int size() const override { return nx * ny; } private: /// Internal data array. Handles allocation/freeing of memory From 4d6873dba47411408f895117cba6d31ed34bb1c9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 27 Sep 2024 11:05:33 +0200 Subject: [PATCH 269/461] Add dummy functions for FieldPerp --- include/bout/fieldperp.hxx | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/include/bout/fieldperp.hxx b/include/bout/fieldperp.hxx index 6995308dbe..ad069f0d01 100644 --- a/include/bout/fieldperp.hxx +++ b/include/bout/fieldperp.hxx @@ -157,6 +157,25 @@ public: return *this; } + /// Dummy functions to increase portability + bool hasParallelSlices() const { return true; } + void calcParallelSlices() const {} + void clearParallelSlices() {} + int numberParallelSlices() { return 0; } + + FieldPerp& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } + const FieldPerp& yup(std::vector::size_type UNUSED(index) = 0) const { + return *this; + } + + FieldPerp& ydown(std::vector::size_type UNUSED(index) = 0) { return *this; } + const FieldPerp& ydown(std::vector::size_type UNUSED(index) = 0) const { + return *this; + } + + FieldPerp& ynext(int UNUSED(dir)) { return *this; } + const FieldPerp& ynext(int UNUSED(dir)) const { return *this; } + /*! * Ensure that data array is allocated and unique */ From 3156681fdcd7bee3384a55ec3a3b98e0f2323744 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 16 Oct 2024 10:27:20 +0200 Subject: [PATCH 270/461] add setRegion / getRegionID to all fields --- include/bout/field.hxx | 9 +++++++++ include/bout/field3d.hxx | 12 +++++++----- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 4e41aa3632..d6af2be3c2 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -30,6 +30,9 @@ class Field; #define FIELD_H #include +#include +#include +#include #include #include "bout/bout_types.hxx" @@ -125,6 +128,12 @@ public: swap(first.directions, second.directions); } + virtual void setRegion(size_t UNUSED(regionID)) {} + virtual void setRegion(std::optional UNUSED(regionID)) {} + virtual void setRegion(const std::string& UNUSED(region_name)) {} + virtual void resetRegion() {} + virtual std::optional getRegionID() const { return {}; } + private: /// Labels for the type of coordinate system this field is defined over DirectionTypes directions{YDirectionType::Standard, ZDirectionType::Standard}; diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index fad4815b92..607cbb37c4 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -327,11 +327,11 @@ public: const Region& getRegion(const std::string& region_name) const; /// Use region provided by the default, and if none is set, use the provided one const Region& getValidRegionWithDefault(const std::string& region_name) const; - void setRegion(const std::string& region_name); - void resetRegion() { regionID.reset(); }; - void setRegion(size_t id) { regionID = id; }; - void setRegion(std::optional id) { regionID = id; }; - std::optional getRegionID() const { return regionID; }; + void setRegion(const std::string& region_name) override; + void resetRegion() override { regionID.reset(); }; + void setRegion(size_t id) override { regionID = id; }; + void setRegion(std::optional id) override { regionID = id; }; + std::optional getRegionID() const override { return regionID; }; /// Return a Region reference to use to iterate over the x- and /// y-indices of this field @@ -510,6 +510,8 @@ public: std::weak_ptr getTracking() { return tracking; }; + bool allowCalcParallelSlices{true}; + private: /// Array sizes (from fieldmesh). These are valid only if fieldmesh is not null int nx{-1}, ny{-1}, nz{-1}; From 160adb41523556bc93ad00c16975f871e65c7006 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 14 Mar 2025 15:38:24 +0100 Subject: [PATCH 271/461] Add Field2D::splitParallelSlices() for writing FCI aware code --- include/bout/field2d.hxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 6e82526bca..fca0dd3c3f 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -136,8 +136,9 @@ public: /// Dummy functions to increase portability bool hasParallelSlices() const { return true; } void calcParallelSlices() const {} - void clearParallelSlices() {} - int numberParallelSlices() { return 0; } + void splitParallelSlices() const {} + void clearParallelSlices() const {} + int numberParallelSlices() const { return 0; } Field2D& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } const Field2D& yup(std::vector::size_type UNUSED(index) = 0) const { From 5502ce360b975ed9d47a3e49dae88b49bdc033ef Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 27 Sep 2024 11:05:58 +0200 Subject: [PATCH 272/461] Allow XZHermiteSpline also without y-offset --- src/mesh/interpolation/hermite_spline_xz.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 27a4f1d614..40dad9b7bd 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -352,8 +352,7 @@ Field3D XZHermiteSpline::interpolate(const Field3D& f, const std::string& region ASSERT1(f.getMesh() == localmesh); Field3D f_interp{emptyFrom(f)}; - const auto region2 = - y_offset == 0 ? "RGN_NOY" : fmt::format("RGN_YPAR_{:+d}", y_offset); + const auto region2 = y_offset != 0 ? fmt::format("RGN_YPAR_{:+d}", y_offset) : region; #if USE_NEW_WEIGHTS #ifdef HS_USE_PETSC From c74d33065293652ded5b64f130cb3c1e0891c4e3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 15 Jan 2025 13:20:10 +0100 Subject: [PATCH 273/461] Add communication routine for FCI operation For FCI we need to be able to access "random" data from the adjacent slices. If they are split in x-direction, this requires some tricky communication pattern. It can be used like this: ``` // Create object GlobalField3DAccess fci_comm(thismesh); // let it know what data points will be required: // where IndG3D is an index in the global field, which would be the // normal Ind3D if there would be only one proc. fci_comm.get(IndG3D(i, ny, nz)); // If all index have been added, the communication pattern will be // established. This has to be called by all processors in parallel fci_comm.setup() // Once the data for a given field is needed, it needs to be // communicated: GlobalField3DAccessInstance global_data = fci_comm.communicate(f3d); // and can be accessed like this BoutReal data = global_data[IndG3D(i, ny, nz)]; // ny and nz in the IndG3D are always optional. ``` --- CMakeLists.txt | 2 + include/bout/region.hxx | 3 +- src/mesh/parallel/fci_comm.cxx | 34 +++++ src/mesh/parallel/fci_comm.hxx | 241 +++++++++++++++++++++++++++++++++ 4 files changed, 279 insertions(+), 1 deletion(-) create mode 100644 src/mesh/parallel/fci_comm.cxx create mode 100644 src/mesh/parallel/fci_comm.hxx diff --git a/CMakeLists.txt b/CMakeLists.txt index e8c4657fb0..615a31d722 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -301,6 +301,8 @@ set(BOUT_SOURCES ./src/mesh/mesh.cxx ./src/mesh/parallel/fci.cxx ./src/mesh/parallel/fci.hxx + ./src/mesh/parallel/fci_comm.cxx + ./src/mesh/parallel/fci_comm.hxx ./src/mesh/parallel/identity.cxx ./src/mesh/parallel/shiftedmetric.cxx ./src/mesh/parallel/shiftedmetricinterp.cxx diff --git a/include/bout/region.hxx b/include/bout/region.hxx index bb1cf82bf1..f441b3edd7 100644 --- a/include/bout/region.hxx +++ b/include/bout/region.hxx @@ -139,7 +139,7 @@ class BoutMask; BOUT_FOR_OMP(index, (region), for schedule(BOUT_OPENMP_SCHEDULE) nowait) // NOLINTEND(cppcoreguidelines-macro-usage,bugprone-macro-parentheses) -enum class IND_TYPE { IND_3D = 0, IND_2D = 1, IND_PERP = 2 }; +enum class IND_TYPE { IND_3D = 0, IND_2D = 1, IND_PERP = 2, IND_GLOBAL_3D }; /// Indices base class for Fields -- Regions are dereferenced into these /// @@ -386,6 +386,7 @@ inline SpecificInd operator-(SpecificInd lhs, const SpecificInd& rhs) { using Ind3D = SpecificInd; using Ind2D = SpecificInd; using IndPerp = SpecificInd; +using IndG3D = SpecificInd; /// Get string representation of Ind3D inline std::string toString(const Ind3D& i) { diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx new file mode 100644 index 0000000000..c0d51d1eb9 --- /dev/null +++ b/src/mesh/parallel/fci_comm.cxx @@ -0,0 +1,34 @@ +/************************************************************************** + * Communication for Flux-coordinate Independent interpolation + * + ************************************************************************** + * Copyright 2025 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#include "fci_comm.hxx" + +#include + +const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { + auto it = gfa.mapping.find(ind.ind); + ASSERT2(it != gfa.mapping.end()); + return data[it->second]; +} diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx new file mode 100644 index 0000000000..aa3b5ecfb5 --- /dev/null +++ b/src/mesh/parallel/fci_comm.hxx @@ -0,0 +1,241 @@ +/************************************************************************** + * Communication for Flux-coordinate Independent interpolation + * + ************************************************************************** + * Copyright 2025 BOUT++ contributors + * + * Contact: Ben Dudson, dudson2@llnl.gov + * + * This file is part of BOUT++. + * + * BOUT++ is free software: you can redistribute it and/or modify + * it under the terms of the GNU Lesser General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * BOUT++ is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License + * along with BOUT++. If not, see . + * + **************************************************************************/ + +#pragma once + +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/boutcomm.hxx" +#include "bout/field3d.hxx" +#include "bout/mesh.hxx" +#include "bout/region.hxx" +#include +#include +#include +#include +#include +#include +#include +class GlobalField3DAccess; + +namespace fci_comm { +struct ProcLocal { + int proc; + int ind; +}; +struct globalToLocal1D { + const int mg; + const int npe; + const int localwith; + const int local; + const int global; + const int globalwith; + globalToLocal1D(int mg, int npe, int localwith) + : mg(mg), npe(npe), localwith(localwith), local(localwith - 2 * mg), + global(local * npe), globalwith(global + 2 * mg) {}; + ProcLocal convert(int id) const { + int idwo = id - mg; + int proc = idwo / local; + if (proc >= npe) { + proc = npe - 1; + } + ASSERT2(proc >= 0); + int loc = id - local * proc; + ASSERT2(0 <= loc); + ASSERT2(loc < (local + 2 * mg)); + return {proc, loc}; + } +}; +template +struct XYZ2Ind { + const int nx; + const int ny; + const int nz; + ind convert(const int x, const int y, const int z) const { + return {z + (y + x * ny) * nz, ny, nz}; + } + ind operator()(const int x, const int y, const int z) const { return convert(x, y, z); } + XYZ2Ind(const int nx, const int ny, const int nz) : nx(nx), ny(ny), nz(nz) {} +}; +} // namespace fci_comm + +class GlobalField3DAccessInstance { +public: + const BoutReal& operator[](IndG3D ind) const; + + GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, + const std::vector&& data) + : gfa(*gfa), data(std::move(data)) {}; + +private: + const GlobalField3DAccess& gfa; + const std::vector data; +}; + +class GlobalField3DAccess { +public: + friend class GlobalField3DAccessInstance; + GlobalField3DAccess(Mesh* mesh) + : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx), + g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy), + g2lz(mesh->zstart, 1, mesh->LocalNz), + xyzl(g2lx.localwith, g2ly.localwith, g2lz.localwith), + xyzg(g2lx.globalwith, g2ly.globalwith, g2lz.globalwith), comm(BoutComm::get()) {}; + void get(IndG3D ind) { ids.emplace(ind.ind); } + void operator[](IndG3D ind) { return get(ind); } + void setup() { + ASSERT2(is_setup == false); + toGet.resize(g2lx.npe * g2ly.npe * g2lz.npe); + for (const auto id : ids) { + IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; + const auto pix = g2lx.convert(gind.x()); + const auto piy = g2ly.convert(gind.y()); + const auto piz = g2lz.convert(gind.z()); + ASSERT3(piz.proc == 0); + toGet[piy.proc * g2lx.npe + pix.proc].push_back( + xyzl.convert(pix.ind, piy.ind, piz.ind).ind); + } + for (auto v : toGet) { + std::sort(v.begin(), v.end()); + } + commCommLists(); + { + int offset = 0; + for (auto get : toGet) { + offsets.push_back(offset); + offset += get.size(); + } + offsets.push_back(offset); + } + std::map mapping; + for (const auto id : ids) { + IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; + const auto pix = g2lx.convert(gind.x()); + const auto piy = g2ly.convert(gind.y()); + const auto piz = g2lz.convert(gind.z()); + ASSERT3(piz.proc == 0); + const auto proc = piy.proc * g2lx.npe + pix.proc; + const auto& vec = toGet[proc]; + auto it = + std::find(vec.begin(), vec.end(), xyzl.convert(pix.ind, piy.ind, piz.ind).ind); + ASSERT3(it != vec.end()); + mapping[id] = it - vec.begin() + offsets[proc]; + } + is_setup = true; + } + GlobalField3DAccessInstance communicate(const Field3D& f) { + return {this, communicate_data(f)}; + } + std::unique_ptr communicate_asPtr(const Field3D& f) { + return std::make_unique(this, communicate_data(f)); + } + +private: + void commCommLists() { + toSend.resize(toGet.size()); + std::vector toGetSizes(toGet.size()); + std::vector toSendSizes(toSend.size()); + //const int thisproc = mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex(); + std::vector reqs(toSend.size()); + for (size_t proc = 0; proc < toGet.size(); ++proc) { + auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, + 666 + proc, comm, &reqs[proc]); + ASSERT0(ret == MPI_SUCCESS); + } + for (size_t proc = 0; proc < toGet.size(); ++proc) { + toGetSizes[proc] = toGet[proc].size(); + sendBufferSize += toGetSizes[proc]; + auto ret = MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, + 666 + proc, comm); + ASSERT0(ret == MPI_SUCCESS); + } + for ([[maybe_unused]] auto dummy : reqs) { + int ind{0}; + auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + toSend[ind].resize(toSendSizes[ind]); + ret = MPI_Irecv(static_cast(&toSend[ind]), toSend[ind].size(), MPI_INT, ind, + 666 * 666 + ind, comm, &reqs[ind]); + ASSERT0(ret == MPI_SUCCESS); + } + for (size_t proc = 0; proc < toGet.size(); ++proc) { + const auto ret = MPI_Send(static_cast(&toGet[proc]), toGet[proc].size(), + MPI_INT, proc, 666 * 666 + proc, comm); + ASSERT0(ret == MPI_SUCCESS); + } + for ([[maybe_unused]] auto dummy : reqs) { + int ind{0}; + const auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + } + } + Mesh* mesh; + std::set ids; + std::map mapping; + bool is_setup{false}; + const fci_comm::globalToLocal1D g2lx; + const fci_comm::globalToLocal1D g2ly; + const fci_comm::globalToLocal1D g2lz; + +public: + const fci_comm::XYZ2Ind xyzl; + const fci_comm::XYZ2Ind xyzg; + +private: + std::vector> toGet; + std::vector> toSend; + std::vector offsets; + int sendBufferSize{0}; + MPI_Comm comm; + std::vector communicate_data(const Field3D& f) { + ASSERT2(f.getMesh() == mesh); + std::vector data(offsets.back()); + std::vector sendBuffer(sendBufferSize); + std::vector reqs(toSend.size()); + for (size_t proc = 0; proc < toGet.size(); ++proc) { + auto ret = MPI_Irecv(static_cast(&data[proc]), toGet[proc].size(), + MPI_DOUBLE, proc, 666 + proc, comm, &reqs[proc]); + ASSERT0(ret == MPI_SUCCESS); + } + int cnt = 0; + for (size_t proc = 0; proc < toGet.size(); ++proc) { + void* start = static_cast(&sendBuffer[cnt]); + for (auto i : toSend[proc]) { + sendBuffer[cnt++] = f[Ind3D(i)]; + } + auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666 + proc, comm); + ASSERT0(ret == MPI_SUCCESS); + } + for ([[maybe_unused]] auto dummy : reqs) { + int ind{0}; + auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + } + return data; + } +}; From afc68d971bb6c0c6c93fa66873d0a2ff397a211e Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 15 Jan 2025 13:26:37 +0100 Subject: [PATCH 274/461] Unify XZMonotonicHermiteSpline and XZMonotonicHermiteSpline If they are two instances of the same template, this allows to have an if in the inner loop that can be optimised out. --- CMakeLists.txt | 1 - include/bout/interpolation_xz.hxx | 84 +++++----------- src/mesh/interpolation/hermite_spline_xz.cxx | 74 +++++++++++--- .../monotonic_hermite_spline_xz.cxx | 98 ------------------- src/mesh/interpolation_xz.cxx | 1 + 5 files changed, 85 insertions(+), 173 deletions(-) delete mode 100644 src/mesh/interpolation/monotonic_hermite_spline_xz.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 615a31d722..11ff1df22e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -296,7 +296,6 @@ set(BOUT_SOURCES ./src/mesh/interpolation/hermite_spline_z.cxx ./src/mesh/interpolation/interpolation_z.cxx ./src/mesh/interpolation/lagrange_4pt_xz.cxx - ./src/mesh/interpolation/monotonic_hermite_spline_xz.cxx ./src/mesh/invert3x3.hxx ./src/mesh/mesh.cxx ./src/mesh/parallel/fci.cxx diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 4dd24259fd..2832899e8a 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -38,6 +38,7 @@ #endif class Options; +class GlobalField3DAccess; /// Interpolate a field onto a perturbed set of points const Field3D interpolate(const Field3D& f, const Field3D& delta_x, @@ -135,7 +136,16 @@ public: } }; -class XZHermiteSpline : public XZInterpolation { +/// Monotonic Hermite spline interpolator +/// +/// Similar to XZHermiteSpline, so uses most of the same code. +/// Forces the interpolated result to be in the range of the +/// neighbouring cell values. This prevents unphysical overshoots, +/// but also degrades accuracy near maxima and minima. +/// Perhaps should only impose near boundaries, since that is where +/// problems most obviously occur. +template +class XZHermiteSplineBase : public XZInterpolation { protected: /// This is protected rather than private so that it can be /// extended and used by HermiteSplineMonotonic @@ -143,6 +153,9 @@ protected: Tensor> i_corner; // index of bottom-left grid point Tensor k_corner; // z-index of bottom-left grid point + std::unique_ptr gf3daccess; + Tensor> g3dinds; + // Basis functions for cubic Hermite spline interpolation // see http://en.wikipedia.org/wiki/Cubic_Hermite_spline // The h00 and h01 basis functions are applied to the function itself @@ -168,14 +181,14 @@ protected: #endif public: - XZHermiteSpline(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) - : XZHermiteSpline(0, mesh) {} - XZHermiteSpline(int y_offset = 0, Mesh* mesh = nullptr); - XZHermiteSpline(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSpline(y_offset, mesh) { + XZHermiteSplineBase(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) + : XZHermiteSplineBase(0, mesh) {} + XZHermiteSplineBase(int y_offset = 0, Mesh* mesh = nullptr); + XZHermiteSplineBase(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) + : XZHermiteSplineBase(y_offset, mesh) { setRegion(regionFromMask(mask, localmesh)); } - ~XZHermiteSpline() { + ~XZHermiteSplineBase() { #if HS_USE_PETSC if (isInit) { MatDestroy(&petscWeights); @@ -205,61 +218,8 @@ public: getWeightsForYApproximation(int i, int j, int k, int yoffset) override; }; -/// Monotonic Hermite spline interpolator -/// -/// Similar to XZHermiteSpline, so uses most of the same code. -/// Forces the interpolated result to be in the range of the -/// neighbouring cell values. This prevents unphysical overshoots, -/// but also degrades accuracy near maxima and minima. -/// Perhaps should only impose near boundaries, since that is where -/// problems most obviously occur. -/// -/// You can control how tight the clipping to the range of the neighbouring cell -/// values through ``rtol`` and ``atol``: -/// -/// diff = (max_of_neighours - min_of_neighours) * rtol + atol -/// -/// and the interpolated value is instead clipped to the range -/// ``[min_of_neighours - diff, max_of_neighours + diff]`` -class XZMonotonicHermiteSpline : public XZHermiteSpline { - /// Absolute tolerance for clipping - BoutReal atol = 0.0; - /// Relative tolerance for clipping - BoutReal rtol = 1.0; - -public: - XZMonotonicHermiteSpline(Mesh* mesh = nullptr, Options* options = nullptr) - : XZHermiteSpline(0, mesh), - atol{(*options)["atol"] - .doc("Absolute tolerance for clipping overshoot") - .withDefault(0.0)}, - rtol{(*options)["rtol"] - .doc("Relative tolerance for clipping overshoot") - .withDefault(1.0)} { - if (localmesh->getNXPE() > 1) { - throw BoutException("Do not support MPI splitting in X"); - } - } - XZMonotonicHermiteSpline(int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSpline(y_offset, mesh) { - if (localmesh->getNXPE() > 1) { - throw BoutException("Do not support MPI splitting in X"); - } - } - XZMonotonicHermiteSpline(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSpline(mask, y_offset, mesh) { - if (localmesh->getNXPE() > 1) { - throw BoutException("Do not support MPI splitting in X"); - } - } - - using XZHermiteSpline::interpolate; - /// Interpolate using precalculated weights. - /// This function is called by the other interpolate functions - /// in the base class XZHermiteSpline. - Field3D interpolate(const Field3D& f, - const std::string& region = "RGN_NOBNDRY") const override; -}; +using XZMonotonicHermiteSpline = XZHermiteSplineBase; +using XZHermiteSpline = XZHermiteSplineBase; /// XZLagrange4pt interpolation class /// diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 40dad9b7bd..59c2f4724d 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -22,6 +22,7 @@ #include "../impls/bout/boutmesh.hxx" #include "bout/bout.hxx" +#include "../parallel/fci_comm.hxx" #include "bout/globals.hxx" #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" @@ -102,7 +103,8 @@ class IndConverter { } }; -XZHermiteSpline::XZHermiteSpline(int y_offset, Mesh* meshin) +template +XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin) : XZInterpolation(y_offset, meshin), h00_x(localmesh), h01_x(localmesh), h10_x(localmesh), h11_x(localmesh), h00_z(localmesh), h01_z(localmesh), h10_z(localmesh), h11_z(localmesh) { @@ -139,6 +141,10 @@ XZHermiteSpline::XZHermiteSpline(int y_offset, Mesh* meshin) MatCreateAIJ(BoutComm::get(), m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); #endif #endif + if constexpr (monotonic) { + gf3daccess = std::make_unique(localmesh); + g3dinds.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); + } #ifndef HS_USE_PETSC if (localmesh->getNXPE() > 1) { throw BoutException("Require PETSc for MPI splitting in X"); @@ -146,8 +152,10 @@ XZHermiteSpline::XZHermiteSpline(int y_offset, Mesh* meshin) #endif } -void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z, - const std::string& region) { +template +void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, + const Field3D& delta_z, + const std::string& region) { const int ny = localmesh->LocalNy; const int nz = localmesh->LocalNz; @@ -300,6 +308,14 @@ void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z } #endif #endif + if constexpr (monotonic) { + const auto gind = gf3daccess->xyzg(i_corn, y + y_offset, k_corner(x, y, z)); + gf3daccess->get(gind); + gf3daccess->get(gind.xp(1)); + gf3daccess->get(gind.zp(1)); + gf3daccess->get(gind.xp(1).zp(1)); + g3dinds[i] = {gind.ind, gind.xp(1).ind, gind.zp(1).ind, gind.xp(1).zp(1).ind}; + } } #ifdef HS_USE_PETSC MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); @@ -311,8 +327,11 @@ void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z #endif } -void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z, - const BoutMask& mask, const std::string& region) { +template +void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, + const Field3D& delta_z, + const BoutMask& mask, + const std::string& region) { setMask(mask); calcWeights(delta_x, delta_z, region); } @@ -333,8 +352,10 @@ void XZHermiteSpline::calcWeights(const Field3D& delta_x, const Field3D& delta_z * (i, j+1, k+1) h01_z + h10_z / 2 * (i, j+1, k+2) h11_z / 2 */ +template std::vector -XZHermiteSpline::getWeightsForYApproximation(int i, int j, int k, int yoffset) { +XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, + int yoffset) { const int nz = localmesh->LocalNz; const int k_mod = k_corner(i, j, k); const int k_mod_m1 = (k_mod > 0) ? (k_mod - 1) : (nz - 1); @@ -347,7 +368,9 @@ XZHermiteSpline::getWeightsForYApproximation(int i, int j, int k, int yoffset) { {i, j + yoffset, k_mod_p2, 0.5 * h11_z(i, j, k)}}; } -Field3D XZHermiteSpline::interpolate(const Field3D& f, const std::string& region) const { +template +Field3D XZHermiteSplineBase::interpolate(const Field3D& f, + const std::string& region) const { ASSERT1(f.getMesh() == localmesh); Field3D f_interp{emptyFrom(f)}; @@ -393,6 +416,11 @@ Field3D XZHermiteSpline::interpolate(const Field3D& f, const std::string& region Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", region2); Field3D fxz = bout::derivatives::index::DDZ(fx, CELL_DEFAULT, "DEFAULT", region2); + std::unique_ptr g3d; + if constexpr (monotonic) { + gf = gf3daccess.communicate_asPtr(f); + } + BOUT_FOR(i, getRegion(region)) { const auto iyp = i.yp(y_offset); @@ -421,6 +449,19 @@ Field3D XZHermiteSpline::interpolate(const Field3D& f, const std::string& region f_interp[iyp] = +f_z * h00_z[i] + f_zp1 * h01_z[i] + fz_z * h10_z[i] + fz_zp1 * h11_z[i]; + if constexpr (monotonic) { + const auto corners = {gf[g3dinds[i][0]], gf[g3dinds[i][1]], gf[g3dinds[i][2]], + gf[g3dinds[i][3]]}; + const auto minmax = std::minmax(corners); + if (f_interp[iyp] < minmax.first) { + f_interp[iyp] = minmax.first; + } else { + if (f_interp[iyp] > minmax.second) { + f_interp[iyp] = minmax.second; + } + } + } + ASSERT2(std::isfinite(f_interp[iyp]) || i.x() < localmesh->xstart || i.x() > localmesh->xend); } @@ -430,15 +471,24 @@ Field3D XZHermiteSpline::interpolate(const Field3D& f, const std::string& region return f_interp; } -Field3D XZHermiteSpline::interpolate(const Field3D& f, const Field3D& delta_x, - const Field3D& delta_z, const std::string& region) { +template +Field3D XZHermiteSplineBase::interpolate(const Field3D& f, + const Field3D& delta_x, + const Field3D& delta_z, + const std::string& region) { calcWeights(delta_x, delta_z, region); return interpolate(f, region); } -Field3D XZHermiteSpline::interpolate(const Field3D& f, const Field3D& delta_x, - const Field3D& delta_z, const BoutMask& mask, - const std::string& region) { +template +Field3D +XZHermiteSplineBase::interpolate(const Field3D& f, const Field3D& delta_x, + const Field3D& delta_z, const BoutMask& mask, + const std::string& region) { calcWeights(delta_x, delta_z, mask, region); return interpolate(f, region); } + +// ensure they are instantiated +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; diff --git a/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx b/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx deleted file mode 100644 index f206ed1e0f..0000000000 --- a/src/mesh/interpolation/monotonic_hermite_spline_xz.cxx +++ /dev/null @@ -1,98 +0,0 @@ -/************************************************************************** - * Copyright 2018 B.D.Dudson, P. Hill - * - * Contact: Ben Dudson, bd512@york.ac.uk - * - * This file is part of BOUT++. - * - * BOUT++ is free software: you can redistribute it and/or modify - * it under the terms of the GNU Lesser General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * BOUT++ is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public License - * along with BOUT++. If not, see . - * - **************************************************************************/ - -#include "bout/globals.hxx" -#include "bout/index_derivs_interface.hxx" -#include "bout/interpolation_xz.hxx" -#include "bout/mesh.hxx" - -#include - -Field3D XZMonotonicHermiteSpline::interpolate(const Field3D& f, - const std::string& region) const { - ASSERT1(f.getMesh() == localmesh); - Field3D f_interp(f.getMesh()); - f_interp.allocate(); - - // Derivatives are used for tension and need to be on dimensionless - // coordinates - Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT"); - Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", "RGN_ALL"); - localmesh->communicate_no_slices(fx, fz); - Field3D fxz = bout::derivatives::index::DDX(fz, CELL_DEFAULT, "DEFAULT"); - localmesh->communicate_no_slices(fxz); - - const auto curregion{getRegion(region)}; - BOUT_FOR(i, curregion) { - const auto iyp = i.yp(y_offset); - - const auto ic = i_corner[i]; - const auto iczp = ic.zp(); - const auto icxp = ic.xp(); - const auto icxpzp = iczp.xp(); - - // Interpolate f in X at Z - const BoutReal f_z = - f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; - - // Interpolate f in X at Z+1 - const BoutReal f_zp1 = f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + fx[iczp] * h10_x[i] - + fx[icxpzp] * h11_x[i]; - - // Interpolate fz in X at Z - const BoutReal fz_z = fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + fxz[ic] * h10_x[i] - + fxz[icxp] * h11_x[i]; - - // Interpolate fz in X at Z+1 - const BoutReal fz_zp1 = fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] - + fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; - - // Interpolate in Z - BoutReal result = - +f_z * h00_z[i] + f_zp1 * h01_z[i] + fz_z * h10_z[i] + fz_zp1 * h11_z[i]; - - ASSERT2(std::isfinite(result) || i.x() < localmesh->xstart - || i.x() > localmesh->xend); - - // Monotonicity - // Force the interpolated result to be in the range of the - // neighbouring cell values. This prevents unphysical overshoots, - // but also degrades accuracy near maxima and minima. - // Perhaps should only impose near boundaries, since that is where - // problems most obviously occur. - const BoutReal localmax = BOUTMAX(f[ic], f[icxp], f[iczp], f[icxpzp]); - const BoutReal localmin = BOUTMIN(f[ic], f[icxp], f[iczp], f[icxpzp]); - - ASSERT2(std::isfinite(localmax) || i.x() < localmesh->xstart - || i.x() > localmesh->xend); - ASSERT2(std::isfinite(localmin) || i.x() < localmesh->xstart - || i.x() > localmesh->xend); - - const auto diff = ((localmax - localmin) * rtol) + atol; - - result = std::min(result, localmax + diff); - result = std::max(result, localmin - diff); - - f_interp[iyp] = result; - } - return f_interp; -} diff --git a/src/mesh/interpolation_xz.cxx b/src/mesh/interpolation_xz.cxx index a58554dc82..f80a361bd1 100644 --- a/src/mesh/interpolation_xz.cxx +++ b/src/mesh/interpolation_xz.cxx @@ -23,6 +23,7 @@ * **************************************************************************/ +#include "parallel/fci_comm.hxx" #include #include #include From 84237c8a474ff6b8b4f2cf04dc76beb3aa4dac9d Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 15 Jan 2025 13:26:59 +0100 Subject: [PATCH 275/461] Use x-splitting for monotonichermitespline test --- tests/MMS/spatial/fci/runtest | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 34340e53f4..c76ad27833 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -17,7 +17,6 @@ import zoidberg as zb from boutdata.collect import collect from boututils.run_wrapper import build_and_log, launch_safe from numpy import arange, array, linspace, log, polyfit -from scipy.interpolate import RectBivariateSpline as RBS # Global parameters DIRECTORY = "data" @@ -32,15 +31,16 @@ NLIST = [8, 16, 32, 64] dx = 1.0 / array(NLIST) -def myRBS(a, b, c): - """RectBivariateSpline, but automatically tune spline degree for small arrays""" - mx, _ = c.shape - kx = max(mx - 1, 1) - kx = min(kx, 3) - return RBS(a, b, c, kx=kx) +success = True +error_2 = {} +error_inf = {} +method_orders = {} -zb.poloidal_grid.RectBivariateSpline = myRBS +# Run with periodic Y? +yperiodic = True + +build_and_log("FCI MMS test") def quiet_collect(name: str) -> float: From 06624497a52f1be434f34fb8b157bee68b8c96e1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 16 Jan 2025 09:07:51 +0100 Subject: [PATCH 276/461] Set region for lagrange4pt --- src/mesh/interpolation/lagrange_4pt_xz.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mesh/interpolation/lagrange_4pt_xz.cxx b/src/mesh/interpolation/lagrange_4pt_xz.cxx index e16b2699d1..7fb0fb502b 100644 --- a/src/mesh/interpolation/lagrange_4pt_xz.cxx +++ b/src/mesh/interpolation/lagrange_4pt_xz.cxx @@ -133,7 +133,11 @@ Field3D XZLagrange4pt::interpolate(const Field3D& f, const std::string& region) // Then in X f_interp(x, y_next, z) = lagrange_4pt(xvals, t_x(x, y, z)); + ASSERT2(std::isfinite(f_interp(x, y_next, z))); + } + const auto region2 = y_offset != 0 ? fmt::format("RGN_YPAR_{:+d}", y_offset) : region; + f_interp.setRegion(region2); return f_interp; } From f77849eb9fcc544e89c2839c6e220867753a702f Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 10:43:58 +0100 Subject: [PATCH 277/461] Add monotonic check also to other code branches --- src/mesh/interpolation/hermite_spline_xz.cxx | 27 +++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 59c2f4724d..28455f77e8 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -36,7 +36,7 @@ class IndConverter { xstart(mesh->xstart), ystart(mesh->ystart), zstart(0), lnx(mesh->LocalNx - 2 * xstart), lny(mesh->LocalNy - 2 * ystart), lnz(mesh->LocalNz - 2 * zstart) {} - // ix and iy are global indices + // ix and iz are global indices // iy is local int fromMeshToGlobal(int ix, int iy, int iz) { const int xstart = mesh->xstart; @@ -388,6 +388,19 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, VecGetArrayRead(result, &cptr); BOUT_FOR(i, f.getRegion(region2)) { f_interp[i] = cptr[int(i)]; + if constexpr (monotonic) { + const auto corners = {gf[g3dinds[i][0]], gf[g3dinds[i][1]], gf[g3dinds[i][2]], + gf[g3dinds[i][3]]}; + const auto minmax = std::minmax(corners); + if (f_interp[iyp] < minmax.first) { + f_interp[iyp] = minmax.first; + } else { + if (f_interp[iyp] > minmax.second) { + f_interp[iyp] = minmax.second; + } + } + } + ASSERT2(std::isfinite(cptr[int(i)])); } VecRestoreArrayRead(result, &cptr); @@ -403,6 +416,18 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, f_interp[iyp] += newWeights[w * 4 + 2][i] * f[ic.zp().xp(w - 1)]; f_interp[iyp] += newWeights[w * 4 + 3][i] * f[ic.zp(2).xp(w - 1)]; } + if constexpr (monotonic) { + const auto corners = {gf[g3dinds[i][0]], gf[g3dinds[i][1]], gf[g3dinds[i][2]], + gf[g3dinds[i][3]]}; + const auto minmax = std::minmax(corners); + if (f_interp[iyp] < minmax.first) { + f_interp[iyp] = minmax.first; + } else { + if (f_interp[iyp] > minmax.second) { + f_interp[iyp] = minmax.second; + } + } + } } #endif #else From 820f0a5cf2e5dfc9134f4ac760edac4b63cbc398 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 13:31:14 +0100 Subject: [PATCH 278/461] use lower_bound instead of find lower_bound takes into account the data is sorted --- src/mesh/parallel/fci_comm.hxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index aa3b5ecfb5..a847700548 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -138,10 +138,11 @@ public: ASSERT3(piz.proc == 0); const auto proc = piy.proc * g2lx.npe + pix.proc; const auto& vec = toGet[proc]; - auto it = - std::find(vec.begin(), vec.end(), xyzl.convert(pix.ind, piy.ind, piz.ind).ind); + const auto tofind = xyzl.convert(pix.ind, piy.ind, piz.ind).ind; + auto it = std::lower_bound(vec.begin(), vec.end(), tofind); ASSERT3(it != vec.end()); - mapping[id] = it - vec.begin() + offsets[proc]; + ASSERT3(*it == tofind); + mapping[id] = std::distance(vec.begin(), it) + offsets[proc]; } is_setup = true; } From 1178243fb42ba9afb595cf31c684504a83f09b63 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 13:31:27 +0100 Subject: [PATCH 279/461] Do not shadow mapping --- src/mesh/parallel/fci_comm.hxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index a847700548..5250fb99d3 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -129,7 +129,6 @@ public: } offsets.push_back(offset); } - std::map mapping; for (const auto id : ids) { IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; const auto pix = g2lx.convert(gind.x()); From 3d6f587ee8fc84418f4810bc537146dec4b3c2bb Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 13:32:01 +0100 Subject: [PATCH 280/461] Ensure setup has been called --- src/mesh/parallel/fci_comm.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 5250fb99d3..6c83d58f63 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -212,6 +212,7 @@ private: int sendBufferSize{0}; MPI_Comm comm; std::vector communicate_data(const Field3D& f) { + ASSERT2(is_setup); ASSERT2(f.getMesh() == mesh); std::vector data(offsets.back()); std::vector sendBuffer(sendBufferSize); From 265c8cd3b5abdf3dd7313a4c6ea4ed81048d1e11 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 13:32:12 +0100 Subject: [PATCH 281/461] Use pointer to data --- src/mesh/parallel/fci_comm.hxx | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 6c83d58f63..d44eea76ef 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -176,13 +176,14 @@ private: auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); + ASSERT2(static_cast(ind) < toSend.size()); toSend[ind].resize(toSendSizes[ind]); - ret = MPI_Irecv(static_cast(&toSend[ind]), toSend[ind].size(), MPI_INT, ind, - 666 * 666 + ind, comm, &reqs[ind]); + ret = MPI_Irecv(static_cast(&toSend[ind][0]), toSend[ind].size(), MPI_INT, + ind, 666 * 666 + ind, comm, &reqs[ind]); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { - const auto ret = MPI_Send(static_cast(&toGet[proc]), toGet[proc].size(), + const auto ret = MPI_Send(static_cast(&toGet[proc][0]), toGet[proc].size(), MPI_INT, proc, 666 * 666 + proc, comm); ASSERT0(ret == MPI_SUCCESS); } From 16a162e180baf86ef183e5f92e75fd88f072cff9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 13:32:36 +0100 Subject: [PATCH 282/461] Call setup before communicator is used --- src/mesh/interpolation/hermite_spline_xz.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 28455f77e8..46eda18a0a 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -317,6 +317,9 @@ void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, g3dinds[i] = {gind.ind, gind.xp(1).ind, gind.zp(1).ind, gind.xp(1).zp(1).ind}; } } + if constexpr (monotonic) { + gf3daccess->setup(); + } #ifdef HS_USE_PETSC MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); MatAssemblyEnd(petscWeights, MAT_FINAL_ASSEMBLY); From e5878c19b28e46c674d6e7c28a9e85a58a3b0bc6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 13:34:36 +0100 Subject: [PATCH 283/461] Deduplicate code Ensures we all ways check for monotonicity --- src/mesh/interpolation/hermite_spline_xz.cxx | 61 +++++++------------- 1 file changed, 21 insertions(+), 40 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 46eda18a0a..80a1336c02 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -380,8 +380,12 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, const auto region2 = y_offset != 0 ? fmt::format("RGN_YPAR_{:+d}", y_offset) : region; -#if USE_NEW_WEIGHTS -#ifdef HS_USE_PETSC + std::unique_ptr gf; + if constexpr (monotonic) { + gf = gf3daccess->communicate_asPtr(f); + } + +#if USE_NEW_WEIGHTS and defined(HS_USE_PETSC) BoutReal* ptr; const BoutReal* cptr; VecGetArray(rhs, &ptr); @@ -392,22 +396,9 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, BOUT_FOR(i, f.getRegion(region2)) { f_interp[i] = cptr[int(i)]; if constexpr (monotonic) { - const auto corners = {gf[g3dinds[i][0]], gf[g3dinds[i][1]], gf[g3dinds[i][2]], - gf[g3dinds[i][3]]}; - const auto minmax = std::minmax(corners); - if (f_interp[iyp] < minmax.first) { - f_interp[iyp] = minmax.first; - } else { - if (f_interp[iyp] > minmax.second) { - f_interp[iyp] = minmax.second; - } - } - } - - ASSERT2(std::isfinite(cptr[int(i)])); - } - VecRestoreArrayRead(result, &cptr); -#else + const auto iyp = i; + const auto i = iyp.ym(y_offset); +#elif USE_NEW_WEIGHTS // No Petsc BOUT_FOR(i, getRegion(region)) { auto ic = i_corner[i]; auto iyp = i.yp(y_offset); @@ -420,20 +411,7 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, f_interp[iyp] += newWeights[w * 4 + 3][i] * f[ic.zp(2).xp(w - 1)]; } if constexpr (monotonic) { - const auto corners = {gf[g3dinds[i][0]], gf[g3dinds[i][1]], gf[g3dinds[i][2]], - gf[g3dinds[i][3]]}; - const auto minmax = std::minmax(corners); - if (f_interp[iyp] < minmax.first) { - f_interp[iyp] = minmax.first; - } else { - if (f_interp[iyp] > minmax.second) { - f_interp[iyp] = minmax.second; - } - } - } - } -#endif -#else +#else // Legacy interpolation // Derivatives are used for tension and need to be on dimensionless // coordinates @@ -444,11 +422,6 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", region2); Field3D fxz = bout::derivatives::index::DDZ(fx, CELL_DEFAULT, "DEFAULT", region2); - std::unique_ptr g3d; - if constexpr (monotonic) { - gf = gf3daccess.communicate_asPtr(f); - } - BOUT_FOR(i, getRegion(region)) { const auto iyp = i.yp(y_offset); @@ -478,8 +451,9 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, +f_z * h00_z[i] + f_zp1 * h01_z[i] + fz_z * h10_z[i] + fz_zp1 * h11_z[i]; if constexpr (monotonic) { - const auto corners = {gf[g3dinds[i][0]], gf[g3dinds[i][1]], gf[g3dinds[i][2]], - gf[g3dinds[i][3]]}; +#endif + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; const auto minmax = std::minmax(corners); if (f_interp[iyp] < minmax.first) { f_interp[iyp] = minmax.first; @@ -489,7 +463,14 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, } } } - +#if USE_NEW_WEIGHTS and defined(HS_USE_PETSC) + ASSERT2(std::isfinite(cptr[int(i)])); + } + VecRestoreArrayRead(result, &cptr); +#elif USE_NEW_WEIGHTS + ASSERT2(std::isfinite(f_interp[iyp])); + } +#else ASSERT2(std::isfinite(f_interp[iyp]) || i.x() < localmesh->xstart || i.x() > localmesh->xend); } From aee27131b93e6ab89b8111064ee8deb79e138f8b Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 14:30:05 +0100 Subject: [PATCH 284/461] Fix tags for comm Tags were different for sender and receiver --- src/mesh/parallel/fci_comm.hxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index d44eea76ef..c0227f4773 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -161,14 +161,14 @@ private: std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, - 666 + proc, comm, &reqs[proc]); + 666, comm, &reqs[proc]); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { toGetSizes[proc] = toGet[proc].size(); sendBufferSize += toGetSizes[proc]; auto ret = MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, - 666 + proc, comm); + 666, comm); ASSERT0(ret == MPI_SUCCESS); } for ([[maybe_unused]] auto dummy : reqs) { @@ -179,12 +179,12 @@ private: ASSERT2(static_cast(ind) < toSend.size()); toSend[ind].resize(toSendSizes[ind]); ret = MPI_Irecv(static_cast(&toSend[ind][0]), toSend[ind].size(), MPI_INT, - ind, 666 * 666 + ind, comm, &reqs[ind]); + ind, 666 * 666, comm, &reqs[ind]); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { const auto ret = MPI_Send(static_cast(&toGet[proc][0]), toGet[proc].size(), - MPI_INT, proc, 666 * 666 + proc, comm); + MPI_INT, proc, 666 * 666, comm); ASSERT0(ret == MPI_SUCCESS); } for ([[maybe_unused]] auto dummy : reqs) { @@ -220,7 +220,7 @@ private: std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&data[proc]), toGet[proc].size(), - MPI_DOUBLE, proc, 666 + proc, comm, &reqs[proc]); + MPI_DOUBLE, proc, 666, comm, &reqs[proc]); ASSERT0(ret == MPI_SUCCESS); } int cnt = 0; @@ -229,7 +229,7 @@ private: for (auto i : toSend[proc]) { sendBuffer[cnt++] = f[Ind3D(i)]; } - auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666 + proc, comm); + auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666, comm); ASSERT0(ret == MPI_SUCCESS); } for ([[maybe_unused]] auto dummy : reqs) { From b4f7be51d9216d7b7f3416c63e3c609e3edda274 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 14:39:43 +0100 Subject: [PATCH 285/461] Use pointer instead of std::vector --- src/mesh/parallel/fci_comm.hxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index c0227f4773..257639bb32 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -216,7 +216,8 @@ private: ASSERT2(is_setup); ASSERT2(f.getMesh() == mesh); std::vector data(offsets.back()); - std::vector sendBuffer(sendBufferSize); + //std::vector sendBuffer(sendBufferSize); + BoutReal* sendBuffer = new BoutReal[sendBufferSize]; std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&data[proc]), toGet[proc].size(), @@ -238,6 +239,7 @@ private: ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); } + delete[] sendBuffer; return data; } }; From 578ee8d28551e1614ae0c62877a528ef45ab3b67 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 21 Jan 2025 14:43:12 +0100 Subject: [PATCH 286/461] Do not reuse requests if the array is still in use Otherwise mpi might wait for the wrong request. --- src/mesh/parallel/fci_comm.hxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 257639bb32..ec34d622a6 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -171,6 +171,7 @@ private: 666, comm); ASSERT0(ret == MPI_SUCCESS); } + std::vector reqs2(toSend.size()); for ([[maybe_unused]] auto dummy : reqs) { int ind{0}; auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); @@ -179,7 +180,7 @@ private: ASSERT2(static_cast(ind) < toSend.size()); toSend[ind].resize(toSendSizes[ind]); ret = MPI_Irecv(static_cast(&toSend[ind][0]), toSend[ind].size(), MPI_INT, - ind, 666 * 666, comm, &reqs[ind]); + ind, 666 * 666, comm, &reqs2[ind]); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { @@ -189,7 +190,7 @@ private: } for ([[maybe_unused]] auto dummy : reqs) { int ind{0}; - const auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); + const auto ret = MPI_Waitany(reqs.size(), &reqs2[0], &ind, MPI_STATUS_IGNORE); ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); } From bf9c9eb013fbb8fce3a8f4252f16dfe4dc287e8f Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:27:11 +0100 Subject: [PATCH 287/461] rename offset to getOffsets to avoid confusion whether the offsets are for sending or receiving --- src/mesh/parallel/fci_comm.hxx | 15 +++++++-------- 1 file changed, 7 insertions(+), 8 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index ec34d622a6..f079385dc1 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -124,10 +124,10 @@ public: { int offset = 0; for (auto get : toGet) { - offsets.push_back(offset); + getOffsets.push_back(offset); offset += get.size(); } - offsets.push_back(offset); + getOffsets.push_back(offset); } for (const auto id : ids) { IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; @@ -141,7 +141,7 @@ public: auto it = std::lower_bound(vec.begin(), vec.end(), tofind); ASSERT3(it != vec.end()); ASSERT3(*it == tofind); - mapping[id] = std::distance(vec.begin(), it) + offsets[proc]; + mapping[id] = std::distance(vec.begin(), it) + getOffsets[proc]; } is_setup = true; } @@ -155,9 +155,8 @@ public: private: void commCommLists() { toSend.resize(toGet.size()); - std::vector toGetSizes(toGet.size()); - std::vector toSendSizes(toSend.size()); - //const int thisproc = mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex(); + std::vector toGetSizes(toGet.size(), -1); + std::vector toSendSizes(toSend.size(), -1); std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, @@ -210,15 +209,15 @@ public: private: std::vector> toGet; std::vector> toSend; - std::vector offsets; + std::vector getOffsets; int sendBufferSize{0}; MPI_Comm comm; std::vector communicate_data(const Field3D& f) { ASSERT2(is_setup); ASSERT2(f.getMesh() == mesh); - std::vector data(offsets.back()); //std::vector sendBuffer(sendBufferSize); BoutReal* sendBuffer = new BoutReal[sendBufferSize]; + std::vector data(getOffsets.back()); std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&data[proc]), toGet[proc].size(), From 2155f068deda476df34d076b183b14c4255b8e92 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:28:04 +0100 Subject: [PATCH 288/461] Fix: mixup of sending / receiving size --- src/mesh/parallel/fci_comm.hxx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index f079385dc1..0926f5c415 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -165,7 +165,6 @@ private: } for (size_t proc = 0; proc < toGet.size(); ++proc) { toGetSizes[proc] = toGet[proc].size(); - sendBufferSize += toGetSizes[proc]; auto ret = MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, 666, comm); ASSERT0(ret == MPI_SUCCESS); @@ -177,6 +176,8 @@ private: ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); ASSERT2(static_cast(ind) < toSend.size()); + ASSERT3(toSendSizes[ind] >= 0); + sendBufferSize += toSendSizes[ind]; toSend[ind].resize(toSendSizes[ind]); ret = MPI_Irecv(static_cast(&toSend[ind][0]), toSend[ind].size(), MPI_INT, ind, 666 * 666, comm, &reqs2[ind]); @@ -215,9 +216,8 @@ private: std::vector communicate_data(const Field3D& f) { ASSERT2(is_setup); ASSERT2(f.getMesh() == mesh); - //std::vector sendBuffer(sendBufferSize); - BoutReal* sendBuffer = new BoutReal[sendBufferSize]; std::vector data(getOffsets.back()); + std::vector sendBuffer(sendBufferSize); std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&data[proc]), toGet[proc].size(), @@ -239,7 +239,6 @@ private: ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); } - delete[] sendBuffer; return data; } }; From 06622473f901863250a5e927cd5a060d068dcdac Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:28:43 +0100 Subject: [PATCH 289/461] Fix receive data offset --- src/mesh/parallel/fci_comm.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 0926f5c415..8db9d8ef14 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -220,8 +220,8 @@ private: std::vector sendBuffer(sendBufferSize); std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { - auto ret = MPI_Irecv(static_cast(&data[proc]), toGet[proc].size(), - MPI_DOUBLE, proc, 666, comm, &reqs[proc]); + auto ret = MPI_Irecv(static_cast(&data[getOffsets[proc]]), + toGet[proc].size(), MPI_DOUBLE, proc, 666, comm, &reqs[proc]); ASSERT0(ret == MPI_SUCCESS); } int cnt = 0; From 28d4e28dc7bbbb579913d974e5d4727b57a55eee Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:30:47 +0100 Subject: [PATCH 290/461] Add check to ensure the proc layout is as expected --- src/mesh/parallel/fci_comm.hxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 8db9d8ef14..30f7df76ee 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -157,6 +157,13 @@ private: toSend.resize(toGet.size()); std::vector toGetSizes(toGet.size(), -1); std::vector toSendSizes(toSend.size(), -1); +#if CHECK > 3 + { + int thisproc; + MPI_Comm_rank(comm, thisproc); + assert(thisproc == mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex()); + } +#endif std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, From e4507d953e51f75237202f72bba8d445586a2530 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:31:54 +0100 Subject: [PATCH 291/461] clang-format --- src/mesh/parallel/fci_comm.hxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 30f7df76ee..bd4b955f12 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -166,14 +166,14 @@ private: #endif std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { - auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, - 666, comm, &reqs[proc]); + auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, 666, + comm, &reqs[proc]); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { toGetSizes[proc] = toGet[proc].size(); - auto ret = MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, - 666, comm); + auto ret = + MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, 666, comm); ASSERT0(ret == MPI_SUCCESS); } std::vector reqs2(toSend.size()); From d7ba297dda1e35d60bc677856d21ccc472148b3e Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:40:35 +0100 Subject: [PATCH 292/461] Use BOUT++ assert --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index bd4b955f12..39348d2e10 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -161,7 +161,7 @@ private: { int thisproc; MPI_Comm_rank(comm, thisproc); - assert(thisproc == mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex()); + ASSERT0(thisproc == mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex()); } #endif std::vector reqs(toSend.size()); From f55f8e4c188806ac38191361d5d5e64356dba83f Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 22 Jan 2025 10:41:15 +0100 Subject: [PATCH 293/461] Fix check --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 39348d2e10..ec4aeaba49 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -160,7 +160,7 @@ private: #if CHECK > 3 { int thisproc; - MPI_Comm_rank(comm, thisproc); + MPI_Comm_rank(comm, &thisproc); ASSERT0(thisproc == mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex()); } #endif From 0e9f7d69ed72469c5e0dbf85deb9a692746bef15 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 28 Jan 2025 09:49:41 +0100 Subject: [PATCH 294/461] Take periodicity into account --- src/mesh/parallel/fci_comm.hxx | 36 ++++++++++++++++++++++++---------- 1 file changed, 26 insertions(+), 10 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index ec4aeaba49..a93b55244a 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -52,19 +52,35 @@ struct globalToLocal1D { const int local; const int global; const int globalwith; - globalToLocal1D(int mg, int npe, int localwith) + const bool periodic; + globalToLocal1D(int mg, int npe, int localwith, bool periodic) : mg(mg), npe(npe), localwith(localwith), local(localwith - 2 * mg), - global(local * npe), globalwith(global + 2 * mg) {}; + global(local * npe), globalwith(global + 2 * mg), periodic(periodic) {}; ProcLocal convert(int id) const { + if (periodic) { + while (id < mg) { + id += global; + } + while (id >= global + mg) { + id -= global; + } + } int idwo = id - mg; int proc = idwo / local; - if (proc >= npe) { - proc = npe - 1; + if (not periodic) { + if (proc >= npe) { + proc = npe - 1; + } } - ASSERT2(proc >= 0); int loc = id - local * proc; - ASSERT2(0 <= loc); - ASSERT2(loc < (local + 2 * mg)); +#if CHECK > 1 + if ((loc < 0 or loc > localwith or proc < 0 or proc > npe) + or (periodic and (loc < mg or loc >= local + mg))) { + printf("globalToLocal1D failure: %d %d, %d %d, %d %d %s\n", id, idwo, globalwith, + npe, proc, loc, periodic ? "periodic" : "non-periodic"); + ASSERT0(false); + } +#endif return {proc, loc}; } }; @@ -98,9 +114,9 @@ class GlobalField3DAccess { public: friend class GlobalField3DAccessInstance; GlobalField3DAccess(Mesh* mesh) - : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx), - g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy), - g2lz(mesh->zstart, 1, mesh->LocalNz), + : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, false), + g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), + g2lz(mesh->zstart, 1, mesh->LocalNz, true), xyzl(g2lx.localwith, g2ly.localwith, g2lz.localwith), xyzg(g2lx.globalwith, g2ly.globalwith, g2lz.globalwith), comm(BoutComm::get()) {}; void get(IndG3D ind) { ids.emplace(ind.ind); } From 944adfbf68b0bdf0440dfe0283634331ec6901a1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 28 Jan 2025 09:49:56 +0100 Subject: [PATCH 295/461] Sort a reference, not a copy --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index a93b55244a..9f296848b1 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -133,7 +133,7 @@ public: toGet[piy.proc * g2lx.npe + pix.proc].push_back( xyzl.convert(pix.ind, piy.ind, piz.ind).ind); } - for (auto v : toGet) { + for (auto& v : toGet) { std::sort(v.begin(), v.end()); } commCommLists(); From 6daec201722fd162574d831778babdf89f1b7742 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 28 Jan 2025 09:52:06 +0100 Subject: [PATCH 296/461] Only communicate non-empty vectors --- src/mesh/parallel/fci_comm.hxx | 44 ++++++++++++++++++++++++---------- 1 file changed, 31 insertions(+), 13 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 9f296848b1..1ca67f0ecc 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -193,6 +193,7 @@ private: ASSERT0(ret == MPI_SUCCESS); } std::vector reqs2(toSend.size()); + int cnt = 0; for ([[maybe_unused]] auto dummy : reqs) { int ind{0}; auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); @@ -200,20 +201,26 @@ private: ASSERT3(ind != MPI_UNDEFINED); ASSERT2(static_cast(ind) < toSend.size()); ASSERT3(toSendSizes[ind] >= 0); + if (toSendSizes[ind] == 0) { + continue; + } sendBufferSize += toSendSizes[ind]; - toSend[ind].resize(toSendSizes[ind]); - ret = MPI_Irecv(static_cast(&toSend[ind][0]), toSend[ind].size(), MPI_INT, - ind, 666 * 666, comm, &reqs2[ind]); + toSend[ind].resize(toSendSizes[ind], -1); + + ret = MPI_Irecv(static_cast(toSend[ind].data()), toSend[ind].size(), MPI_INT, + ind, 666 * 666, comm, reqs2.data() + cnt++); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { - const auto ret = MPI_Send(static_cast(&toGet[proc][0]), toGet[proc].size(), - MPI_INT, proc, 666 * 666, comm); - ASSERT0(ret == MPI_SUCCESS); + if (toGet.size() != 0) { + const auto ret = MPI_Send(static_cast(toGet[proc].data()), + toGet[proc].size(), MPI_INT, proc, 666 * 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } } - for ([[maybe_unused]] auto dummy : reqs) { + for (int c = 0; c < cnt; c++) { int ind{0}; - const auto ret = MPI_Waitany(reqs.size(), &reqs2[0], &ind, MPI_STATUS_IGNORE); + const auto ret = MPI_Waitany(cnt, reqs2.data(), &ind, MPI_STATUS_IGNORE); ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); } @@ -242,25 +249,36 @@ private: std::vector data(getOffsets.back()); std::vector sendBuffer(sendBufferSize); std::vector reqs(toSend.size()); + int cnt1 = 0; for (size_t proc = 0; proc < toGet.size(); ++proc) { - auto ret = MPI_Irecv(static_cast(&data[getOffsets[proc]]), - toGet[proc].size(), MPI_DOUBLE, proc, 666, comm, &reqs[proc]); + if (toGet[proc].size() == 0) { + continue; + } + auto ret = + MPI_Irecv(static_cast(data.data() + getOffsets[proc]), + toGet[proc].size(), MPI_DOUBLE, proc, 666, comm, reqs.data() + cnt1); ASSERT0(ret == MPI_SUCCESS); + cnt1++; } int cnt = 0; for (size_t proc = 0; proc < toGet.size(); ++proc) { - void* start = static_cast(&sendBuffer[cnt]); + if (toSend[proc].size() == 0) { + continue; + } + const void* start = static_cast(sendBuffer.data() + cnt); for (auto i : toSend[proc]) { sendBuffer[cnt++] = f[Ind3D(i)]; } auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666, comm); ASSERT0(ret == MPI_SUCCESS); } - for ([[maybe_unused]] auto dummy : reqs) { + for (int j = 0; j < cnt1; ++j) { int ind{0}; - auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); + auto ret = MPI_Waitany(cnt1, reqs.data(), &ind, MPI_STATUS_IGNORE); ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); + ASSERT3(ind >= 0); + ASSERT3(ind < cnt1); } return data; } From 3444dd0f7ee424ffa6206629f79063b301798516 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 31 Jan 2025 11:45:08 +0100 Subject: [PATCH 297/461] Make check stricter --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 1ca67f0ecc..df08a02dda 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -74,7 +74,7 @@ struct globalToLocal1D { } int loc = id - local * proc; #if CHECK > 1 - if ((loc < 0 or loc > localwith or proc < 0 or proc > npe) + if ((loc < 0 or loc > localwith or proc < 0 or proc >= npe) or (periodic and (loc < mg or loc >= local + mg))) { printf("globalToLocal1D failure: %d %d, %d %d, %d %d %s\n", id, idwo, globalwith, npe, proc, loc, periodic ? "periodic" : "non-periodic"); From 91bee6811566a04656fb8b05ca4493effcbf7a39 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 31 Jan 2025 11:49:47 +0100 Subject: [PATCH 298/461] Fix: include global offset in monotonic spline --- src/mesh/interpolation/hermite_spline_xz.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 80a1336c02..c2a97c1c05 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -164,6 +164,8 @@ void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, #ifdef HS_USE_PETSC IndConverter conv{localmesh}; #endif + [[maybe_unused]] const int y_global_offset = + localmesh->getYProcIndex() * (localmesh->yend - localmesh->ystart + 1); BOUT_FOR(i, getRegion(region)) { const int x = i.x(); const int y = i.y(); @@ -309,7 +311,8 @@ void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, #endif #endif if constexpr (monotonic) { - const auto gind = gf3daccess->xyzg(i_corn, y + y_offset, k_corner(x, y, z)); + const auto gind = + gf3daccess->xyzg(i_corn, y + y_offset + y_global_offset, k_corner(x, y, z)); gf3daccess->get(gind); gf3daccess->get(gind.xp(1)); gf3daccess->get(gind.zp(1)); From d7aeae490d6499a17ca7287fe291d48c630bb386 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 31 Jan 2025 14:51:11 +0100 Subject: [PATCH 299/461] make fci_comm openmp thread safe Using a local set for each thread ensures we do not need a mutex for adding data, at the cost of having to merge the different sets later. --- src/mesh/parallel/fci_comm.hxx | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index df08a02dda..40c1fe9f72 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -118,11 +118,30 @@ public: g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), g2lz(mesh->zstart, 1, mesh->LocalNz, true), xyzl(g2lx.localwith, g2ly.localwith, g2lz.localwith), - xyzg(g2lx.globalwith, g2ly.globalwith, g2lz.globalwith), comm(BoutComm::get()) {}; - void get(IndG3D ind) { ids.emplace(ind.ind); } + xyzg(g2lx.globalwith, g2ly.globalwith, g2lz.globalwith), comm(BoutComm::get()) { +#ifdef _OPENMP + o_ids.resize(omp_get_max_threads()); +#endif + }; + void get(IndG3D ind) { + ASSERT2(is_setup == false); +#ifdef _OPENMP + ASSERT2(o_ids.size() > static_cast(omp_get_thread_num())); + o_ids[omp_get_thread_num()].emplace(ind.ind); +#else + ids.emplace(ind.ind); +#endif + } + void operator[](IndG3D ind) { return get(ind); } void setup() { ASSERT2(is_setup == false); +#ifdef _OPENMP + for (auto& o_id : o_ids) { + ids.merge(o_id); + } + o_ids.clear(); +#endif toGet.resize(g2lx.npe * g2ly.npe * g2lz.npe); for (const auto id : ids) { IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; @@ -226,6 +245,9 @@ private: } } Mesh* mesh; +#ifdef _OPENMP + std::vector> o_ids; +#endif std::set ids; std::map mapping; bool is_setup{false}; From ac5cc7086d737ed0f41a1b5274e5815566b223f0 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 7 Feb 2025 13:50:10 +0100 Subject: [PATCH 300/461] Fix communication for fci We want to skip sending if there is no data for this process ... --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 40c1fe9f72..27dd111765 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -231,7 +231,7 @@ private: ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (toGet.size() != 0) { + if (toGet[proc].size() != 0) { const auto ret = MPI_Send(static_cast(toGet[proc].data()), toGet[proc].size(), MPI_INT, proc, 666 * 666, comm); ASSERT0(ret == MPI_SUCCESS); From 885b465cbb81d3e12345569337693b2c39e768dd Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 7 Feb 2025 15:58:07 +0100 Subject: [PATCH 301/461] Add check to reduce risk of bugs The result needs to be well understood, as the indices are a mix of local and global indices, as we need to access non-local data. --- src/mesh/interpolation/hermite_spline_xz.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index c2a97c1c05..036f5b2669 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -362,6 +362,9 @@ template std::vector XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, int yoffset) { + if (localmesh->getNXPE() > 1){ + throw BoutException("It is likely that the function calling this is not handling the result correctly."); + } const int nz = localmesh->LocalNz; const int k_mod = k_corner(i, j, k); const int k_mod_m1 = (k_mod > 0) ? (k_mod - 1) : (nz - 1); From a1c40336dc0bfbcedf7f622f2a2986e8cc9b8284 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Thu, 9 Oct 2025 17:23:45 +0100 Subject: [PATCH 302/461] tests: Increase nx for hermitespline interpolation boundary problem --- src/mesh/interpolation/hermite_spline_xz.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 036f5b2669..41ca857829 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -418,12 +418,14 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, } if constexpr (monotonic) { #else // Legacy interpolation + // TODO(peter): Should we apply dirichlet BCs to derivatives? // Derivatives are used for tension and need to be on dimensionless // coordinates // f has been communcated, and thus we can assume that the x-boundaries are // also valid in the y-boundary. Thus the differentiated field needs no // extra comms. + // TODO(dave) Add assert that we do not use z-splitting or z-guards. Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT", region2); Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", region2); Field3D fxz = bout::derivatives::index::DDZ(fx, CELL_DEFAULT, "DEFAULT", region2); From 1c25b0e3afc6b7505e35842256a08f545e0d0aef Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 13:02:00 +0100 Subject: [PATCH 303/461] region may be not used --- src/mesh/interpolation/hermite_spline_xz.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 41ca857829..bbd8c9abc0 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -155,7 +155,7 @@ XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin) template void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, const Field3D& delta_z, - const std::string& region) { + [[maybe_unused]] const std::string& region) { const int ny = localmesh->LocalNy; const int nz = localmesh->LocalNz; @@ -379,7 +379,7 @@ XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, template Field3D XZHermiteSplineBase::interpolate(const Field3D& f, - const std::string& region) const { + [[maybe_unused]] const std::string& region) const { ASSERT1(f.getMesh() == localmesh); Field3D f_interp{emptyFrom(f)}; From c7f8504324f1b8174c8f3317202a89b32f2fc735 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 13:52:00 +0100 Subject: [PATCH 304/461] Fix [[maybe_unused]] location --- include/bout/msg_stack.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/msg_stack.hxx b/include/bout/msg_stack.hxx index 1abb26d2c7..0fe22cd0e6 100644 --- a/include/bout/msg_stack.hxx +++ b/include/bout/msg_stack.hxx @@ -80,7 +80,7 @@ public: } void pop() {} - void pop(int [[maybe_unused]] id) {} + void pop([[maybe_unused]] int id) {} void clear() {} void dump() {} From 96aa2bdbae97c573580fac747de58a1eb79c167b Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Feb 2026 10:47:00 +0100 Subject: [PATCH 305/461] Add some wiggle room for monotonichermitespline --- include/bout/interpolation_xz.hxx | 13 ++++++++---- src/mesh/interpolation/hermite_spline_xz.cxx | 22 +++++++++++++------- 2 files changed, 23 insertions(+), 12 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 2832899e8a..fb06faacc2 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -180,12 +180,17 @@ protected: Vec rhs, result; #endif + /// Factors to allow for some wiggleroom + BoutReal abs_fac_monotonic{1e-2}; + BoutReal rel_fac_monotonic{1e-1}; + public: XZHermiteSplineBase(Mesh* mesh = nullptr, [[maybe_unused]] Options* options = nullptr) - : XZHermiteSplineBase(0, mesh) {} - XZHermiteSplineBase(int y_offset = 0, Mesh* mesh = nullptr); - XZHermiteSplineBase(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr) - : XZHermiteSplineBase(y_offset, mesh) { + : XZHermiteSplineBase(0, mesh, options) {} + XZHermiteSplineBase(int y_offset = 0, Mesh* mesh = nullptr, Options* options = nullptr); + XZHermiteSplineBase(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr, + Options* options = nullptr) + : XZHermiteSplineBase(y_offset, mesh, options) { setRegion(regionFromMask(mask, localmesh)); } ~XZHermiteSplineBase() { diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index bbd8c9abc0..8eac4037b7 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -104,11 +104,20 @@ class IndConverter { }; template -XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin) +XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin, + Options* options) : XZInterpolation(y_offset, meshin), h00_x(localmesh), h01_x(localmesh), h10_x(localmesh), h11_x(localmesh), h00_z(localmesh), h01_z(localmesh), h10_z(localmesh), h11_z(localmesh) { + if constexpr (monotonic) { + if (options == nullptr) { + options = &Options::root()["mesh:paralleltransform:xzinterpolation"]; + } + abs_fac_monotonic = (*options)["abs_tol"].withDefault(abs_fac_monotonic); + rel_fac_monotonic = (*options)["rel_tol"].withDefault(rel_fac_monotonic); + } + // Index arrays contain guard cells in order to get subscripts right i_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); k_corner.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); @@ -463,13 +472,10 @@ Field3D XZHermiteSplineBase::interpolate(const Field3D& f, const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; const auto minmax = std::minmax(corners); - if (f_interp[iyp] < minmax.first) { - f_interp[iyp] = minmax.first; - } else { - if (f_interp[iyp] > minmax.second) { - f_interp[iyp] = minmax.second; - } - } + const auto diff = + (minmax.second - minmax.first) * rel_fac_monotonic + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); } #if USE_NEW_WEIGHTS and defined(HS_USE_PETSC) ASSERT2(std::isfinite(cptr[int(i)])); From df70c89721279ca41935e322f1e5935eed14e9cf Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 24 Feb 2025 13:38:40 +0100 Subject: [PATCH 306/461] Allow to overwrite MYG with options. --- src/mesh/impls/bout/boutmesh.cxx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 4aaa760c04..08df213126 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -485,8 +485,18 @@ int BoutMesh::load() { } ASSERT0(MXG >= 0); - if (Mesh::get(MYG, "MYG") != 0) { - MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(2); + bool meshHasMyg = Mesh::get(MYG, "MYG") == 0; + int meshMyg; + if (!meshHasMyg) { + MYG = 2; + } else { + meshMyg = MYG; + } + MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(MYG); + if (meshHasMyg && MYG != meshMyg) { + output_warn.write(_("Options changed the number of y-guard cells. Grid has {} but " + "option specified {}! Continuing with {}"), + meshMyg, MYG, MYG); } ASSERT0(MYG >= 0); From 52708afb8ccc7f31729c27a201c925df1ffb9b3d Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 3 Mar 2025 13:40:58 +0100 Subject: [PATCH 307/461] Only read `MYG` if it set or mesh:MYG is not set This avoids errors in the MMS tests --- src/mesh/impls/bout/boutmesh.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 08df213126..acdcc5d0b0 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -492,7 +492,9 @@ int BoutMesh::load() { } else { meshMyg = MYG; } - MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(MYG); + if (options.isSet("MYG") or (!meshHasMyg)) { + MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(MYG); + } if (meshHasMyg && MYG != meshMyg) { output_warn.write(_("Options changed the number of y-guard cells. Grid has {} but " "option specified {}! Continuing with {}"), From 1e5f185d5753ca6171bc91b94a306adfeeaf9b80 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 3 Mar 2025 14:02:54 +0100 Subject: [PATCH 308/461] Do not set MYG/MXG if it is not needed --- tests/integrated/test-boutpp/mms-ddz/data/BOUT.inp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/integrated/test-boutpp/mms-ddz/data/BOUT.inp b/tests/integrated/test-boutpp/mms-ddz/data/BOUT.inp index d5ca4c4d71..519faa0403 100644 --- a/tests/integrated/test-boutpp/mms-ddz/data/BOUT.inp +++ b/tests/integrated/test-boutpp/mms-ddz/data/BOUT.inp @@ -1,7 +1,3 @@ - -MXG = 2 -MYG = 2 - [mesh] staggergrids = true n = 1 From 8267681fe80af7cc5ee0be12035ce5fb4a20dc91 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 3 Mar 2025 14:03:41 +0100 Subject: [PATCH 309/461] Do not set MYG/MXG if it is not needed --- tests/integrated/test-boutpp/collect/input/BOUT.inp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/tests/integrated/test-boutpp/collect/input/BOUT.inp b/tests/integrated/test-boutpp/collect/input/BOUT.inp index d5ca4c4d71..519faa0403 100644 --- a/tests/integrated/test-boutpp/collect/input/BOUT.inp +++ b/tests/integrated/test-boutpp/collect/input/BOUT.inp @@ -1,7 +1,3 @@ - -MXG = 2 -MYG = 2 - [mesh] staggergrids = true n = 1 From 9a45d250be9b317bdc16680171282aec57b403ab Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 2 Feb 2026 13:44:51 +0100 Subject: [PATCH 310/461] set target properties on exported library This allows dependent projects to check whether BOUT++ was compiled e.g. with 3D metrics. --- bout++Config.cmake.in | 29 ++++++++++++++++++++++++++++- 1 file changed, 28 insertions(+), 1 deletion(-) diff --git a/bout++Config.cmake.in b/bout++Config.cmake.in index a8cf041901..5cc68aefde 100644 --- a/bout++Config.cmake.in +++ b/bout++Config.cmake.in @@ -8,7 +8,7 @@ set(BOUT_USE_TRACK @BOUT_USE_TRACK@) set(BOUT_USE_SIGFPE @BOUT_USE_SIGFPE@) set(BOUT_USE_OPENMP @BOUT_USE_OPENMP@) set(BOUT_HAS_CUDA @BOUT_HAS_CUDA@) -set(BOUT_HAS_OUTPUT_DEBUG @BOUT_HAS_OUTPUT_DEBUG@) +set(BOUT_USE_OUTPUT_DEBUG @BOUT_USE_OUTPUT_DEBUG@) set(BOUT_CHECK_LEVEL @BOUT_CHECK_LEVEL@) set(BOUT_USE_METRIC_3D @BOUT_USE_METRIC_3D@) @@ -182,3 +182,30 @@ if (BOUT_HAS_ADIOS2) endif() include("${CMAKE_CURRENT_LIST_DIR}/bout++Targets.cmake") + +add_library(bout++ INTERFACE IMPORTED) +set_target_properties(bout++ + PROPERTIES BOUT_USE_SIGNAL @BOUT_USE_SIGNAL@ + BOUT_USE_COLOR @BOUT_USE_COLOR@ + BOUT_USE_TRACK @BOUT_USE_TRACK@ + BOUT_USE_SIGFPE @BOUT_USE_SIGFPE@ + BOUT_USE_OPENMP @BOUT_USE_OPENMP@ + BOUT_HAS_CUDA @BOUT_HAS_CUDA@ + BOUT_USE_OUTPUT_DEBUG @BOUT_USE_OUTPUT_DEBUG@ + BOUT_CHECK_LEVEL @BOUT_CHECK_LEVEL@ + BOUT_USE_METRIC_3D @BOUT_USE_METRIC_3D@ + BOUT_HAS_PVODE @BOUT_HAS_PVODE@ + BOUT_HAS_NETCDF @BOUT_HAS_NETCDF@ + BOUT_HAS_ADIOS2 @BOUT_HAS_ADIOS2@ + BOUT_HAS_FFTW @BOUT_HAS_FFTW@ + BOUT_HAS_LAPACK @BOUT_HAS_LAPACK@ + BOUT_HAS_PETSC @BOUT_HAS_PETSC@ + BOUT_HAS_SLEPC @BOUT_HAS_SLEPC@ + BOUT_HAS_SCOREP @BOUT_HAS_SCOREP@ + BOUT_USE_UUID_SYSTEM_GENERATOR @BOUT_USE_UUID_SYSTEM_GENERATOR@ + BOUT_HAS_SUNDIALS @BOUT_HAS_SUNDIALS@ + BOUT_HAS_HYPRE @BOUT_HAS_HYPRE@ + BOUT_HAS_GETTEXT @BOUT_HAS_GETTEXT@ + BOUT_HAS_UMPIRE @BOUT_HAS_UMPIRE@ + BOUT_HAS_RAJA @BOUT_HAS_RAJA@ +) From dce551b6fcc7b5099341385d480128eb385bdaf3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 11 Feb 2026 10:13:16 +0100 Subject: [PATCH 311/461] Do not build with openmp --- .github/workflows/docker.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index c028d17042..809f59cf9c 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -26,11 +26,11 @@ jobs: matrix: mpi: [mpich] metric3d: - - name: "With OpenMP" + - name: "With 3D Metrics" cmake: ON - base_prefix: "openmp-" + base_prefix: "" tag_prefix: "3d-" - - name: "Without OpenMP" + - name: "Without 3D Metrics" cmake: OFF base_prefix: "" tag_prefix: "" @@ -75,7 +75,7 @@ jobs: build-args: | BASE=${{ matrix.mpi }}-${{ matrix.metric3d.base_prefix }}${{ matrix.config.base_postfix }}-main MPI=${{ matrix.mpi }} - CMAKE_OPTIONS=${{ matrix.config.options }} -DBOUT_ENABLE_METRIC_3D=${{ matrix.metric3d.cmake }} -DBOUT_ENABLE_OPENMP=${{ matrix.metric3d.cmake }} + CMAKE_OPTIONS=${{ matrix.config.options }} -DBOUT_ENABLE_METRIC_3D=${{ matrix.metric3d.cmake }} COMMIT=${{ github.sha }} URL=${{ github.server_url }}/${{ github.repository }} context: .docker/fedora/ From e0ade7c1733c9a54e72105e4b67b0cb467faf3c9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 13 Feb 2026 14:43:11 +0100 Subject: [PATCH 312/461] be verbose in docker build --- .docker/fedora/Dockerfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index da4d2d9fac..ed04240c4c 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -31,7 +31,7 @@ RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ -DBOUT_ENABLE_PYTHON=ON \ -DBOUT_USE_SUNDIALS=ON -DSUNDIALS_ROOT=/usr/lib64/$MPI/ -DSUNDIALS_INCLUDE_DIR=/usr/include/$MPI-x86_64/sundials/ \ $CMAKE_OPTIONS || (cat /home/boutuser/BOUT-dev/build/CMakeFiles/CMake{Output,Error}.log ; exit 1); \ - make -C build -j 2; \ + make -C build -j 2 VERBOSE=1; \ sudo make -C build install; \ rm -rf build From e5cb1ebc179220551eba5a381eaac0d2f00b07b8 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 13 Feb 2026 14:49:22 +0100 Subject: [PATCH 313/461] [container] Set build type --- .docker/fedora/Dockerfile | 1 + 1 file changed, 1 insertion(+) diff --git a/.docker/fedora/Dockerfile b/.docker/fedora/Dockerfile index ed04240c4c..7d5c04418d 100644 --- a/.docker/fedora/Dockerfile +++ b/.docker/fedora/Dockerfile @@ -26,6 +26,7 @@ ENV HOME=/home/boutuser WORKDIR /home/boutuser/BOUT-dev RUN cmake -S . -B build -DCMAKE_INSTALL_PREFIX=/usr/local/ \ + -DCMAKE_BUILD_TYPE=RelWithDebInfo \ -DBOUT_GENERATE_FIELDOPS=OFF \ -DBOUT_USE_PETSC=ON -DPETSc_ROOT=/usr/local \ -DBOUT_ENABLE_PYTHON=ON \ From e4a054962ab784f5fa447b3146938a453aa4dc8f Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 13 Feb 2026 14:42:58 +0100 Subject: [PATCH 314/461] Change default to PETSc for 3D metrics --- include/bout/invert_laplace.hxx | 4 ++++ src/invert/laplace/impls/naulin/naulin_laplace.cxx | 3 ++- 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/include/bout/invert_laplace.hxx b/include/bout/invert_laplace.hxx index 258b4d2e80..73e58e8483 100644 --- a/include/bout/invert_laplace.hxx +++ b/include/bout/invert_laplace.hxx @@ -141,7 +141,11 @@ public: static constexpr auto type_name = "Laplacian"; static constexpr auto section_name = "laplace"; static constexpr auto option_name = "type"; +#if BOUT_USE_METRIC_3D + static constexpr auto default_type = LAPLACE_PETSC; +#else static constexpr auto default_type = LAPLACE_CYCLIC; +#endif ReturnType create(Options* options = nullptr, CELL_LOC loc = CELL_CENTRE, Mesh* mesh = nullptr, Solver* solver = nullptr) { diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.cxx b/src/invert/laplace/impls/naulin/naulin_laplace.cxx index 1c90976ce6..481f01ace0 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.cxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.cxx @@ -149,6 +149,7 @@ #include #include #include +#include #include "naulin_laplace.hxx" @@ -207,7 +208,7 @@ LaplaceNaulin::LaplaceNaulin(Options* opt, const CELL_LOC loc, Mesh* mesh_in, ASSERT0(underrelax_recovery >= 1.); delp2solver = create(opt->getSection("delp2solver"), location, localmesh); std::string delp2type; - opt->getSection("delp2solver")->get("type", delp2type, "cyclic"); + opt->getSection("delp2solver")->get("type", delp2type, LaplaceFactory::default_type); // Check delp2solver is using an FFT scheme, otherwise it will not exactly // invert Delp2 and we will not converge ASSERT0(delp2type == "cyclic" || delp2type == "spt" || delp2type == "tri"); From b043da35a828e689792ce7b9ab2c1f1eb13bc5c6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Mar 2026 13:50:15 +0100 Subject: [PATCH 315/461] Apply clang-tidy fixes --- include/bout/field.hxx | 2 +- include/bout/field3d.hxx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index d6af2be3c2..141e47ce04 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -24,6 +24,7 @@ * */ +#include "bout/unused.hxx" class Field; #ifndef FIELD_H @@ -31,7 +32,6 @@ class Field; #include #include -#include #include #include diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 607cbb37c4..b9e9fb66c6 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -20,6 +20,7 @@ * **************************************************************************/ +#include class Field3D; #pragma once From d37661b66bec5a50365537be4b92fcb59aab8649 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Mar 2026 13:57:51 +0100 Subject: [PATCH 316/461] Move functions to field.hxx and make them virtual --- include/bout/field.hxx | 5 +++++ include/bout/field2d.hxx | 7 ------- include/bout/field3d.hxx | 13 ++++++++----- include/bout/fieldperp.hxx | 6 ------ 4 files changed, 13 insertions(+), 18 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index 141e47ce04..bfae83cef0 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -128,11 +128,16 @@ public: swap(first.directions, second.directions); } + /// Dummy functions to increase portability virtual void setRegion(size_t UNUSED(regionID)) {} virtual void setRegion(std::optional UNUSED(regionID)) {} virtual void setRegion(const std::string& UNUSED(region_name)) {} virtual void resetRegion() {} virtual std::optional getRegionID() const { return {}; } + virtual bool hasParallelSlices() const { return true; } + virtual void calcParallelSlices() const {} + virtual void splitParallelSlices() const {} + virtual void clearParallelSlices() const {} private: /// Labels for the type of coordinate system this field is defined over diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index fca0dd3c3f..a13da6b92e 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -133,13 +133,6 @@ public: return *this; } - /// Dummy functions to increase portability - bool hasParallelSlices() const { return true; } - void calcParallelSlices() const {} - void splitParallelSlices() const {} - void clearParallelSlices() const {} - int numberParallelSlices() const { return 0; } - Field2D& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } const Field2D& yup(std::vector::size_type UNUSED(index) = 0) const { return *this; diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index b9e9fb66c6..9f46d25198 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -239,15 +239,15 @@ public: * Ensure that this field has separate fields * for yup and ydown. */ - void splitParallelSlices(); + void splitParallelSlices() override; /*! * Clear the parallel slices, yup and ydown */ - void clearParallelSlices(); + void clearParallelSlices() override; /// Check if this field has yup and ydown fields - bool hasParallelSlices() const { + bool hasParallelSlices() const override { #if CHECK > 2 if (yup_fields.size() != ydown_fields.size()) { throw BoutException( @@ -482,7 +482,7 @@ public: friend class Vector3D; friend class Vector2D; - Field3D& calcParallelSlices(); + void calcParallelSlices() override; void applyBoundary(bool init = false) override; void applyBoundary(BoutReal t); @@ -511,7 +511,8 @@ public: std::weak_ptr getTracking() { return tracking; }; - bool allowCalcParallelSlices{true}; + bool allowCalcParallelSlices() const { return _allowCalcParallelSlices; }; + void disallowCalcParallelSlices() { _allowCalcParallelSlices = false; }; private: /// Array sizes (from fieldmesh). These are valid only if fieldmesh is not null @@ -532,6 +533,8 @@ private: /// counter for tracking, to assign unique names to the variable names int tracking_state{0}; std::weak_ptr tracking; + + bool _allowCalcParallelSlices{true}; // name is changed if we assign to the variable, while selfname is a // non-changing copy that is used for the variable names in the dump files std::string selfname; diff --git a/include/bout/fieldperp.hxx b/include/bout/fieldperp.hxx index ad069f0d01..dcfb89f54c 100644 --- a/include/bout/fieldperp.hxx +++ b/include/bout/fieldperp.hxx @@ -157,12 +157,6 @@ public: return *this; } - /// Dummy functions to increase portability - bool hasParallelSlices() const { return true; } - void calcParallelSlices() const {} - void clearParallelSlices() {} - int numberParallelSlices() { return 0; } - FieldPerp& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } const FieldPerp& yup(std::vector::size_type UNUSED(index) = 0) const { return *this; From 48e78ec6d80d378b38d5ec15a7725447d3ed836c Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Mar 2026 13:58:06 +0100 Subject: [PATCH 317/461] Apply fixes from clang-tidy --- include/bout/fieldperp.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/include/bout/fieldperp.hxx b/include/bout/fieldperp.hxx index dcfb89f54c..1f70089990 100644 --- a/include/bout/fieldperp.hxx +++ b/include/bout/fieldperp.hxx @@ -23,6 +23,7 @@ * **************************************************************************/ +#include class FieldPerp; #ifndef BOUT_FIELDPERP_H From b6b39d9d4a6f11e4fc8dbc70d48484aee3b2ada0 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 3 Mar 2026 14:06:52 +0100 Subject: [PATCH 318/461] The overrides are non-const --- include/bout/field.hxx | 6 +++--- src/field/field3d.cxx | 3 +-- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index bfae83cef0..a5b9d1390b 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -135,9 +135,9 @@ public: virtual void resetRegion() {} virtual std::optional getRegionID() const { return {}; } virtual bool hasParallelSlices() const { return true; } - virtual void calcParallelSlices() const {} - virtual void splitParallelSlices() const {} - virtual void clearParallelSlices() const {} + virtual void calcParallelSlices() {} + virtual void splitParallelSlices() {} + virtual void clearParallelSlices() {} private: /// Labels for the type of coordinate system this field is defined over diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 2cd55b0957..fe9fdc9263 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -341,9 +341,8 @@ Field3D& Field3D::operator=(const BoutReal val) { return *this; } -Field3D& Field3D::calcParallelSlices() { +void Field3D::calcParallelSlices() { getCoordinates()->getParallelTransform().calcParallelSlices(*this); - return *this; } ///////////////////// BOUNDARY CONDITIONS ////////////////// From 65880e4af16cb95438437bf057c4b9fdb427b142 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:09:09 +0000 Subject: [PATCH 319/461] [bot] Apply format changes --- include/bout/interpolation_xz.hxx | 2 +- src/mesh/interpolation/hermite_spline_xz.cxx | 17 +++++++++-------- src/mesh/interpolation/lagrange_4pt_xz.cxx | 1 - src/mesh/parallel/fci_comm.hxx | 4 ++-- 4 files changed, 12 insertions(+), 12 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index fb06faacc2..578627997f 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -189,7 +189,7 @@ public: : XZHermiteSplineBase(0, mesh, options) {} XZHermiteSplineBase(int y_offset = 0, Mesh* mesh = nullptr, Options* options = nullptr); XZHermiteSplineBase(const BoutMask& mask, int y_offset = 0, Mesh* mesh = nullptr, - Options* options = nullptr) + Options* options = nullptr) : XZHermiteSplineBase(y_offset, mesh, options) { setRegion(regionFromMask(mask, localmesh)); } diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 8eac4037b7..6b9441f539 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -21,8 +21,8 @@ **************************************************************************/ #include "../impls/bout/boutmesh.hxx" -#include "bout/bout.hxx" #include "../parallel/fci_comm.hxx" +#include "bout/bout.hxx" #include "bout/globals.hxx" #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" @@ -162,9 +162,9 @@ XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin, } template -void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, - const Field3D& delta_z, - [[maybe_unused]] const std::string& region) { +void XZHermiteSplineBase::calcWeights( + const Field3D& delta_x, const Field3D& delta_z, + [[maybe_unused]] const std::string& region) { const int ny = localmesh->LocalNy; const int nz = localmesh->LocalNz; @@ -371,8 +371,9 @@ template std::vector XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, int yoffset) { - if (localmesh->getNXPE() > 1){ - throw BoutException("It is likely that the function calling this is not handling the result correctly."); + if (localmesh->getNXPE() > 1) { + throw BoutException("It is likely that the function calling this is not handling the " + "result correctly."); } const int nz = localmesh->LocalNz; const int k_mod = k_corner(i, j, k); @@ -387,8 +388,8 @@ XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, } template -Field3D XZHermiteSplineBase::interpolate(const Field3D& f, - [[maybe_unused]] const std::string& region) const { +Field3D XZHermiteSplineBase::interpolate( + const Field3D& f, [[maybe_unused]] const std::string& region) const { ASSERT1(f.getMesh() == localmesh); Field3D f_interp{emptyFrom(f)}; diff --git a/src/mesh/interpolation/lagrange_4pt_xz.cxx b/src/mesh/interpolation/lagrange_4pt_xz.cxx index 7fb0fb502b..d5e862fc0b 100644 --- a/src/mesh/interpolation/lagrange_4pt_xz.cxx +++ b/src/mesh/interpolation/lagrange_4pt_xz.cxx @@ -134,7 +134,6 @@ Field3D XZLagrange4pt::interpolate(const Field3D& f, const std::string& region) // Then in X f_interp(x, y_next, z) = lagrange_4pt(xvals, t_x(x, y, z)); ASSERT2(std::isfinite(f_interp(x, y_next, z))); - } const auto region2 = y_offset != 0 ? fmt::format("RGN_YPAR_{:+d}", y_offset) : region; f_interp.setRegion(region2); diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 27dd111765..3514e4ba17 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -55,7 +55,7 @@ struct globalToLocal1D { const bool periodic; globalToLocal1D(int mg, int npe, int localwith, bool periodic) : mg(mg), npe(npe), localwith(localwith), local(localwith - 2 * mg), - global(local * npe), globalwith(global + 2 * mg), periodic(periodic) {}; + global(local * npe), globalwith(global + 2 * mg), periodic(periodic){}; ProcLocal convert(int id) const { if (periodic) { while (id < mg) { @@ -103,7 +103,7 @@ public: GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, const std::vector&& data) - : gfa(*gfa), data(std::move(data)) {}; + : gfa(*gfa), data(std::move(data)){}; private: const GlobalField3DAccess& gfa; From a91d3e75b31ba43951792f5ede617af602b4261b Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:09:10 +0000 Subject: [PATCH 320/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index abd11c7762..9ec546dec7 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -11,3 +11,4 @@ a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 2c2402ed59c91164eaff46dee0f79386b7347e9e 05b7c571544c3bcb153fce67d12b9ac48947fc2d c8f38049359170a34c915e209276238ea66b9a1e +65880e4af16cb95438437bf057c4b9fdb427b142 From 8d5cb39e03c2644715a50684f8cd0318b4e82676 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:33:29 +0000 Subject: [PATCH 321/461] [bot] Apply format changes --- src/mesh/impls/bout/boutmesh.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index acdcc5d0b0..7f76d6efe0 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -497,8 +497,8 @@ int BoutMesh::load() { } if (meshHasMyg && MYG != meshMyg) { output_warn.write(_("Options changed the number of y-guard cells. Grid has {} but " - "option specified {}! Continuing with {}"), - meshMyg, MYG, MYG); + "option specified {}! Continuing with {}"), + meshMyg, MYG, MYG); } ASSERT0(MYG >= 0); From 0ba91f61cd2781bec1da5f29e04c830d5e95a4fc Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Tue, 3 Mar 2026 13:33:30 +0000 Subject: [PATCH 322/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index abd11c7762..4e7777ec58 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -11,3 +11,4 @@ a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 2c2402ed59c91164eaff46dee0f79386b7347e9e 05b7c571544c3bcb153fce67d12b9ac48947fc2d c8f38049359170a34c915e209276238ea66b9a1e +8d5cb39e03c2644715a50684f8cd0318b4e82676 From f473a6bfd972421769c61fda4cef9edc03535ca0 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 10:16:51 +0100 Subject: [PATCH 323/461] Apply fixes from clang-format --- src/mesh/interpolation/hermite_spline_xz.cxx | 2 ++ src/mesh/parallel/fci_comm.cxx | 3 +++ src/mesh/parallel/fci_comm.hxx | 3 +++ 3 files changed, 8 insertions(+) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 6b9441f539..577b4cb9db 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -27,6 +27,8 @@ #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" +#include +#include #include class IndConverter { diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index c0d51d1eb9..95bfa6958e 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -24,6 +24,9 @@ **************************************************************************/ #include "fci_comm.hxx" +#include "bout/assert.hxx" +#include "bout/bout_types.hxx" +#include "bout/region.hxx" #include diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 3514e4ba17..cbe5af3a7f 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -32,6 +32,9 @@ #include "bout/mesh.hxx" #include "bout/region.hxx" #include +#include +#include +#include #include #include #include From a8c5ea82e28ee4cd903c15297792b1bdc939f62c Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 10:07:25 +0100 Subject: [PATCH 324/461] Cleanup code --- src/mesh/impls/bout/boutmesh.cxx | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 7f76d6efe0..d0bdcf33d8 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -485,13 +485,12 @@ int BoutMesh::load() { } ASSERT0(MXG >= 0); - bool meshHasMyg = Mesh::get(MYG, "MYG") == 0; - int meshMyg; + const bool meshHasMyg = Mesh::get(MYG, "MYG") == 0; if (!meshHasMyg) { MYG = 2; - } else { - meshMyg = MYG; } + int meshMyg = MYG; + if (options.isSet("MYG") or (!meshHasMyg)) { MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(MYG); } From 6a104ba0c61ab5908f1aa70401567cf5960ffeec Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 19 Mar 2024 15:41:00 +0100 Subject: [PATCH 325/461] Add maskFromRegion --- include/bout/mask.hxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/include/bout/mask.hxx b/include/bout/mask.hxx index fc5f814676..378ac835bc 100644 --- a/include/bout/mask.hxx +++ b/include/bout/mask.hxx @@ -80,4 +80,12 @@ inline std::unique_ptr> regionFromMask(const BoutMask& mask, } return std::make_unique>(indices); } + +inline BoutMask maskFromRegion(const Region& region, const Mesh* mesh) { + BoutMask mask{mesh, false}; + + BOUT_FOR(i, region) { mask[i] = true; } + return mask; +} + #endif //BOUT_MASK_H From ea2ea211079e43c547f828a016d1d398a88d3376 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 09:59:36 +0100 Subject: [PATCH 326/461] Switch to [[maybe_unused]] --- include/bout/field.hxx | 7 +++---- include/bout/field2d.hxx | 29 ++++++++++++++--------------- include/bout/field3d.hxx | 8 ++++---- 3 files changed, 21 insertions(+), 23 deletions(-) diff --git a/include/bout/field.hxx b/include/bout/field.hxx index a5b9d1390b..3f7f591528 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -24,7 +24,6 @@ * */ -#include "bout/unused.hxx" class Field; #ifndef FIELD_H @@ -129,9 +128,9 @@ public: } /// Dummy functions to increase portability - virtual void setRegion(size_t UNUSED(regionID)) {} - virtual void setRegion(std::optional UNUSED(regionID)) {} - virtual void setRegion(const std::string& UNUSED(region_name)) {} + virtual void setRegion([[maybe_unused]] size_t regionID) {} + virtual void setRegion([[maybe_unused]] std::optional regionID) {} + virtual void setRegion([[maybe_unused]] const std::string& region_name) {} virtual void resetRegion() {} virtual std::optional getRegionID() const { return {}; } virtual bool hasParallelSlices() const { return true; } diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index a13da6b92e..97c4be2716 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -36,7 +36,6 @@ class Field2D; #include "bout/field_data.hxx" #include "bout/fieldperp.hxx" #include "bout/region.hxx" -#include "bout/unused.hxx" #if BOUT_HAS_RAJA #include "RAJA/RAJA.hpp" // using RAJA lib @@ -133,18 +132,18 @@ public: return *this; } - Field2D& yup(std::vector::size_type UNUSED(index) = 0) { return *this; } - const Field2D& yup(std::vector::size_type UNUSED(index) = 0) const { + Field2D& yup([[maybe_unused]] std::vector::size_type index = 0) { return *this; } + const Field2D& yup([[maybe_unused]] std::vector::size_type index = 0) const { return *this; } - Field2D& ydown(std::vector::size_type UNUSED(index) = 0) { return *this; } - const Field2D& ydown(std::vector::size_type UNUSED(index) = 0) const { + Field2D& ydown([[maybe_unused]] std::vector::size_type index = 0) { return *this; } + const Field2D& ydown([[maybe_unused]] std::vector::size_type index) = 0 const { return *this; } - Field2D& ynext(int UNUSED(dir)) { return *this; } - const Field2D& ynext(int UNUSED(dir)) const { return *this; } + Field2D& ynext([[maybe_unused]] int dir) { return *this; } + const Field2D& ynext([[maybe_unused]] int dir) const { return *this; } // Operators @@ -227,8 +226,8 @@ public: * DIrect access to underlying array. This version is for compatibility * with Field3D objects */ - BoutReal& operator()(int jx, int jy, int UNUSED(jz)) { return operator()(jx, jy); } - const BoutReal& operator()(int jx, int jy, int UNUSED(jz)) const { + BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) { return operator()(jx, jy); } + const BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) const { return operator()(jx, jy); } @@ -315,12 +314,12 @@ Field2D operator-(const Field2D& f); // Non-member functions inline Field2D toFieldAligned(const Field2D& f, - const std::string& UNUSED(region) = "RGN_ALL") { + [[maybe_unused]] const std::string& region = "RGN_ALL") { return f; } inline Field2D fromFieldAligned(const Field2D& f, - const std::string& UNUSED(region) = "RGN_ALL") { + [[maybe_unused]] const std::string& region = "RGN_ALL") { return f; } @@ -331,15 +330,15 @@ inline Field2D fromFieldAligned(const Field2D& f, /// default (can be changed using the \p rgn argument void checkData(const Field2D& f, const std::string& region = "RGN_NOBNDRY"); #else -inline void checkData(const Field2D& UNUSED(f), - std::string UNUSED(region) = "RGN_NOBNDRY") {} +inline void checkData([[maybe_unused]] const Field2D& f, + [[maybe_unused]] std::string region) = "RGN_NOBNDRY" {} #endif /// Force guard cells of passed field \p var to NaN #if CHECK > 2 void invalidateGuards(Field2D& var); #else -inline void invalidateGuards(Field2D& UNUSED(var)) {} +inline void invalidateGuards([[maybe_unused]] Field2D& var) {} #endif /// Average in the Z direction @@ -355,7 +354,7 @@ inline Field2D& ddt(Field2D& f) { return *(f.timeDeriv()); } /// toString template specialisation /// Defined in utils.hxx template <> -inline std::string toString<>(const Field2D& UNUSED(val)) { +inline std::string toString<>([[maybe_unused]] const Field2D& val) { return ""; } diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 9f46d25198..8546a430ed 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -605,8 +605,8 @@ void checkData(const Field3D& f, const std::string& region = "RGN_NOBNDRY"); #else /// Ignored with disabled CHECK; Throw an exception if \p f is not /// allocated or if any elements are non-finite (for CHECK > 2) -inline void checkData(const Field3D& UNUSED(f), - const std::string& UNUSED(region) = "RGN_NOBNDRY"){}; +inline void checkData([[maybe_unused]] const Field3D& f, + [[maybe_unused]] const std::string& region = "RGN_NOBNDRY"){}; #endif /// Fourier filtering, removes all except one mode @@ -667,7 +667,7 @@ Field2D DC(const Field3D& f, const std::string& rgn = "RGN_ALL"); #if CHECK > 2 void invalidateGuards(Field3D& var); #else -inline void invalidateGuards(Field3D& UNUSED(var)) {} +inline void invalidateGuards([[maybe_unused]] Field3D& var) {} #endif /// Returns a reference to the time-derivative of a field \p f @@ -678,7 +678,7 @@ inline Field3D& ddt(Field3D& f) { return *(f.timeDeriv()); } /// toString template specialisation /// Defined in utils.hxx template <> -inline std::string toString<>(const Field3D& UNUSED(val)) { +inline std::string toString<>([[maybe_unused]] const Field3D& val) { return ""; } From f2bc0488a298f136294c523bc5ab4086d090059b Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:56:43 +0000 Subject: [PATCH 327/461] [bot] Apply format changes --- src/invert/laplace/impls/naulin/naulin_laplace.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/invert/laplace/impls/naulin/naulin_laplace.cxx b/src/invert/laplace/impls/naulin/naulin_laplace.cxx index 481f01ace0..bf8711245e 100644 --- a/src/invert/laplace/impls/naulin/naulin_laplace.cxx +++ b/src/invert/laplace/impls/naulin/naulin_laplace.cxx @@ -147,9 +147,9 @@ #include #include #include +#include #include #include -#include #include "naulin_laplace.hxx" From e0dd2049d748059f7fa52eb2a1eeabe2c45647ee Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Wed, 4 Mar 2026 09:56:44 +0000 Subject: [PATCH 328/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index abd11c7762..742c020e17 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -11,3 +11,4 @@ a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 2c2402ed59c91164eaff46dee0f79386b7347e9e 05b7c571544c3bcb153fce67d12b9ac48947fc2d c8f38049359170a34c915e209276238ea66b9a1e +f2bc0488a298f136294c523bc5ab4086d090059b From 8a3f82debba0a6751a51f9c38d434265c2de92c5 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 10:02:08 +0100 Subject: [PATCH 329/461] Prefer size_t --- include/bout/field2d.hxx | 8 ++++---- include/bout/field3d.hxx | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 97c4be2716..001201b5e1 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -132,13 +132,13 @@ public: return *this; } - Field2D& yup([[maybe_unused]] std::vector::size_type index = 0) { return *this; } - const Field2D& yup([[maybe_unused]] std::vector::size_type index = 0) const { + Field2D& yup([[maybe_unused]] size_t index = 0) { return *this; } + const Field2D& yup([[maybe_unused]] size_t index = 0) const { return *this; } - Field2D& ydown([[maybe_unused]] std::vector::size_type index = 0) { return *this; } - const Field2D& ydown([[maybe_unused]] std::vector::size_type index) = 0 const { + Field2D& ydown([[maybe_unused]] size_t index = 0) { return *this; } + const Field2D& ydown([[maybe_unused]] size_t index = 0) const { return *this; } diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 8546a430ed..42fee60a7c 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -264,24 +264,24 @@ public: /// Check if this field has yup and ydown fields /// Return reference to yup field - Field3D& yup(std::vector::size_type index = 0) { + Field3D& yup(size_t index = 0) { ASSERT2(index < yup_fields.size()); return yup_fields[index]; } /// Return const reference to yup field - const Field3D& yup(std::vector::size_type index = 0) const { + const Field3D& yup(size_t index = 0) const { ASSERT2(index < yup_fields.size()); return yup_fields[index]; } /// Return reference to ydown field - Field3D& ydown(std::vector::size_type index = 0) { + Field3D& ydown(size_t index = 0) { ASSERT2(index < ydown_fields.size()); return ydown_fields[index]; } /// Return const reference to ydown field - const Field3D& ydown(std::vector::size_type index = 0) const { + const Field3D& ydown(size_t index = 0) const { ASSERT2(index < ydown_fields.size()); return ydown_fields[index]; } From f341037b5fbb26184898190112932004985b010e Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:20:14 +0100 Subject: [PATCH 330/461] Apply clang-tidy changes --- include/bout/interpolation_xz.hxx | 3 +- include/bout/region.hxx | 2 +- src/mesh/interpolation/hermite_spline_xz.cxx | 4 ++- src/mesh/interpolation/lagrange_4pt_xz.cxx | 1 + src/mesh/parallel/fci_comm.hxx | 38 ++++++++++---------- 5 files changed, 26 insertions(+), 22 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 578627997f..6e4f12b1b0 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -27,6 +27,7 @@ #include #include #include +#include #define USE_NEW_WEIGHTS 1 #if BOUT_HAS_PETSC @@ -193,7 +194,7 @@ public: : XZHermiteSplineBase(y_offset, mesh, options) { setRegion(regionFromMask(mask, localmesh)); } - ~XZHermiteSplineBase() { + ~XZHermiteSplineBase() override { #if HS_USE_PETSC if (isInit) { MatDestroy(&petscWeights); diff --git a/include/bout/region.hxx b/include/bout/region.hxx index f441b3edd7..17ded65987 100644 --- a/include/bout/region.hxx +++ b/include/bout/region.hxx @@ -139,7 +139,7 @@ class BoutMask; BOUT_FOR_OMP(index, (region), for schedule(BOUT_OPENMP_SCHEDULE) nowait) // NOLINTEND(cppcoreguidelines-macro-usage,bugprone-macro-parentheses) -enum class IND_TYPE { IND_3D = 0, IND_2D = 1, IND_PERP = 2, IND_GLOBAL_3D }; +enum class IND_TYPE { IND_3D = 0, IND_2D = 1, IND_PERP = 2, IND_GLOBAL_3D = 3 }; /// Indices base class for Fields -- Regions are dereferenced into these /// diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 577b4cb9db..2a4f32070a 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -26,8 +26,10 @@ #include "bout/globals.hxx" #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" +#include "bout/mask.hxx" #include +#include #include #include @@ -476,7 +478,7 @@ Field3D XZHermiteSplineBase::interpolate( (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; const auto minmax = std::minmax(corners); const auto diff = - (minmax.second - minmax.first) * rel_fac_monotonic + abs_fac_monotonic; + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); } diff --git a/src/mesh/interpolation/lagrange_4pt_xz.cxx b/src/mesh/interpolation/lagrange_4pt_xz.cxx index d5e862fc0b..c82c9fa36e 100644 --- a/src/mesh/interpolation/lagrange_4pt_xz.cxx +++ b/src/mesh/interpolation/lagrange_4pt_xz.cxx @@ -20,6 +20,7 @@ * **************************************************************************/ +#include "fmt/format.h" #include "bout/globals.hxx" #include "bout/interpolation_xz.hxx" #include "bout/mesh.hxx" diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index cbe5af3a7f..17b30a9c25 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -57,8 +57,8 @@ struct globalToLocal1D { const int globalwith; const bool periodic; globalToLocal1D(int mg, int npe, int localwith, bool periodic) - : mg(mg), npe(npe), localwith(localwith), local(localwith - 2 * mg), - global(local * npe), globalwith(global + 2 * mg), periodic(periodic){}; + : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), + global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; ProcLocal convert(int id) const { if (periodic) { while (id < mg) { @@ -68,14 +68,14 @@ struct globalToLocal1D { id -= global; } } - int idwo = id - mg; + const int idwo = id - mg; int proc = idwo / local; if (not periodic) { if (proc >= npe) { proc = npe - 1; } } - int loc = id - local * proc; + const int loc = id - (local * proc); #if CHECK > 1 if ((loc < 0 or loc > localwith or proc < 0 or proc >= npe) or (periodic and (loc < mg or loc >= local + mg))) { @@ -93,7 +93,7 @@ struct XYZ2Ind { const int ny; const int nz; ind convert(const int x, const int y, const int z) const { - return {z + (y + x * ny) * nz, ny, nz}; + return {z + ((y + x * ny) * nz), ny, nz}; } ind operator()(const int x, const int y, const int z) const { return convert(x, y, z); } XYZ2Ind(const int nx, const int ny, const int nz) : nx(nx), ny(ny), nz(nz) {} @@ -105,8 +105,8 @@ public: const BoutReal& operator[](IndG3D ind) const; GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, - const std::vector&& data) - : gfa(*gfa), data(std::move(data)){}; + std::vector&& data) + : gfa(*gfa), data(std::move(data)) {}; private: const GlobalField3DAccess& gfa; @@ -136,7 +136,7 @@ public: #endif } - void operator[](IndG3D ind) { return get(ind); } + void operator[](IndG3D ind) { get(ind); } void setup() { ASSERT2(is_setup == false); #ifdef _OPENMP @@ -145,14 +145,14 @@ public: } o_ids.clear(); #endif - toGet.resize(g2lx.npe * g2ly.npe * g2lz.npe); + toGet.resize(static_cast(g2lx.npe * g2ly.npe * g2lz.npe)); for (const auto id : ids) { - IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; + const IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; const auto pix = g2lx.convert(gind.x()); const auto piy = g2ly.convert(gind.y()); const auto piz = g2lz.convert(gind.z()); ASSERT3(piz.proc == 0); - toGet[piy.proc * g2lx.npe + pix.proc].push_back( + toGet[(piy.proc * g2lx.npe) + pix.proc].push_back( xyzl.convert(pix.ind, piy.ind, piz.ind).ind); } for (auto& v : toGet) { @@ -161,19 +161,19 @@ public: commCommLists(); { int offset = 0; - for (auto get : toGet) { + for (const auto& get : toGet) { getOffsets.push_back(offset); offset += get.size(); } getOffsets.push_back(offset); } for (const auto id : ids) { - IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; + const IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; const auto pix = g2lx.convert(gind.x()); const auto piy = g2ly.convert(gind.y()); const auto piz = g2lz.convert(gind.z()); ASSERT3(piz.proc == 0); - const auto proc = piy.proc * g2lx.npe + pix.proc; + const auto proc = (piy.proc * g2lx.npe) + pix.proc; const auto& vec = toGet[proc]; const auto tofind = xyzl.convert(pix.ind, piy.ind, piz.ind).ind; auto it = std::lower_bound(vec.begin(), vec.end(), tofind); @@ -216,9 +216,9 @@ private: } std::vector reqs2(toSend.size()); int cnt = 0; - for ([[maybe_unused]] auto dummy : reqs) { + for ([[maybe_unused]] auto* dummy : reqs) { int ind{0}; - auto ret = MPI_Waitany(reqs.size(), &reqs[0], &ind, MPI_STATUS_IGNORE); + auto ret = MPI_Waitany(reqs.size(), reqs.data(), &ind, MPI_STATUS_IGNORE); ASSERT0(ret == MPI_SUCCESS); ASSERT3(ind != MPI_UNDEFINED); ASSERT2(static_cast(ind) < toSend.size()); @@ -234,7 +234,7 @@ private: ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (toGet[proc].size() != 0) { + if (!toGet[proc].empty()) { const auto ret = MPI_Send(static_cast(toGet[proc].data()), toGet[proc].size(), MPI_INT, proc, 666 * 666, comm); ASSERT0(ret == MPI_SUCCESS); @@ -276,7 +276,7 @@ private: std::vector reqs(toSend.size()); int cnt1 = 0; for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (toGet[proc].size() == 0) { + if (toGet[proc].empty()) { continue; } auto ret = @@ -287,7 +287,7 @@ private: } int cnt = 0; for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (toSend[proc].size() == 0) { + if (toSend[proc].empty()) { continue; } const void* start = static_cast(sendBuffer.data() + cnt); From 999d4ca69e32e47ce7b46c60c6d2818593a6dd9f Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:31:37 +0100 Subject: [PATCH 331/461] Apply clang-tidy improvements --- src/mesh/parallel/fci_comm.cxx | 4 ++-- src/mesh/parallel/fci_comm.hxx | 22 +++++++++++----------- 2 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 95bfa6958e..65a8731740 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -31,7 +31,7 @@ #include const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { - auto it = gfa.mapping.find(ind.ind); - ASSERT2(it != gfa.mapping.end()); + auto it = gfa->mapping.find(ind.ind); + ASSERT2(it != gfa->mapping.end()); return data[it->second]; } diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 17b30a9c25..6d80a2e326 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -49,13 +49,13 @@ struct ProcLocal { int ind; }; struct globalToLocal1D { - const int mg; - const int npe; - const int localwith; - const int local; - const int global; - const int globalwith; - const bool periodic; + int mg; + int npe; + int localwith; + int local; + int global; + int globalwith; + bool periodic; globalToLocal1D(int mg, int npe, int localwith, bool periodic) : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; @@ -106,11 +106,11 @@ public: GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, std::vector&& data) - : gfa(*gfa), data(std::move(data)) {}; + : gfa(gfa), data(std::move(data)) {}; private: - const GlobalField3DAccess& gfa; - const std::vector data; + const GlobalField3DAccess* gfa; + std::vector data; }; class GlobalField3DAccess { @@ -145,7 +145,7 @@ public: } o_ids.clear(); #endif - toGet.resize(static_cast(g2lx.npe * g2ly.npe * g2lz.npe)); + toGet.resize(static_cast(g2lx.npe * g2ly.npe * g2lz.npe)); for (const auto id : ids) { const IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; const auto pix = g2lx.convert(gind.x()); From 1f7623c86455de9d75fe6a97f7f33fd80a491df2 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:35:42 +0100 Subject: [PATCH 332/461] fixup --- include/bout/field2d.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 001201b5e1..d1585522f0 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -331,7 +331,7 @@ inline Field2D fromFieldAligned(const Field2D& f, void checkData(const Field2D& f, const std::string& region = "RGN_NOBNDRY"); #else inline void checkData([[maybe_unused]] const Field2D& f, - [[maybe_unused]] std::string region) = "RGN_NOBNDRY" {} + [[maybe_unused]] std::string region = "RGN_NOBNDRY") {} #endif /// Force guard cells of passed field \p var to NaN From 88abeb55fd7c89388f501a2918e9933169bad301 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:13:27 +0100 Subject: [PATCH 333/461] Add missing header --- include/bout/field2d.hxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index d1585522f0..899c741991 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -37,6 +37,8 @@ class Field2D; #include "bout/fieldperp.hxx" #include "bout/region.hxx" +#include + #if BOUT_HAS_RAJA #include "RAJA/RAJA.hpp" // using RAJA lib #endif From 5ac876b2b20b964e1c418aeb22548a3fd36f4fe3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:50:49 +0100 Subject: [PATCH 334/461] Add missing header --- include/bout/mask.hxx | 1 + 1 file changed, 1 insertion(+) diff --git a/include/bout/mask.hxx b/include/bout/mask.hxx index 378ac835bc..9033080d44 100644 --- a/include/bout/mask.hxx +++ b/include/bout/mask.hxx @@ -26,6 +26,7 @@ #include "bout/globals.hxx" #include "bout/mesh.hxx" +#include "bout/region.hxx" /** * 3D array of bools to mask Field3Ds From 614a727ca68ff8ca0fd3f330d4d48dcf127989a6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:52:57 +0100 Subject: [PATCH 335/461] MPI_Request may not be a pointer --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 6d80a2e326..cf0ddb01a2 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -216,7 +216,7 @@ private: } std::vector reqs2(toSend.size()); int cnt = 0; - for ([[maybe_unused]] auto* dummy : reqs) { + for ([[maybe_unused]] auto dummy : reqs) { int ind{0}; auto ret = MPI_Waitany(reqs.size(), reqs.data(), &ind, MPI_STATUS_IGNORE); ASSERT0(ret == MPI_SUCCESS); From ec69e8838be2dde140a915e50506f8e1ce3cb534 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:55:59 +0000 Subject: [PATCH 336/461] [bot] Apply format changes --- include/bout/field2d.hxx | 12 +++++------- include/bout/field3d.hxx | 2 +- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 899c741991..757880ed70 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -135,14 +135,10 @@ public: } Field2D& yup([[maybe_unused]] size_t index = 0) { return *this; } - const Field2D& yup([[maybe_unused]] size_t index = 0) const { - return *this; - } + const Field2D& yup([[maybe_unused]] size_t index = 0) const { return *this; } Field2D& ydown([[maybe_unused]] size_t index = 0) { return *this; } - const Field2D& ydown([[maybe_unused]] size_t index = 0) const { - return *this; - } + const Field2D& ydown([[maybe_unused]] size_t index = 0) const { return *this; } Field2D& ynext([[maybe_unused]] int dir) { return *this; } const Field2D& ynext([[maybe_unused]] int dir) const { return *this; } @@ -228,7 +224,9 @@ public: * DIrect access to underlying array. This version is for compatibility * with Field3D objects */ - BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) { return operator()(jx, jy); } + BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) { + return operator()(jx, jy); + } const BoutReal& operator()(int jx, int jy, [[maybe_unused]] int jz) const { return operator()(jx, jy); } diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 42fee60a7c..df2a807706 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -606,7 +606,7 @@ void checkData(const Field3D& f, const std::string& region = "RGN_NOBNDRY"); /// Ignored with disabled CHECK; Throw an exception if \p f is not /// allocated or if any elements are non-finite (for CHECK > 2) inline void checkData([[maybe_unused]] const Field3D& f, - [[maybe_unused]] const std::string& region = "RGN_NOBNDRY"){}; + [[maybe_unused]] const std::string& region = "RGN_NOBNDRY") {}; #endif /// Fourier filtering, removes all except one mode From 013a8478ab374abb3022e7f86efa138143d21bbb Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Wed, 4 Mar 2026 10:56:00 +0000 Subject: [PATCH 337/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index abd11c7762..8d488ade81 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -11,3 +11,4 @@ a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 2c2402ed59c91164eaff46dee0f79386b7347e9e 05b7c571544c3bcb153fce67d12b9ac48947fc2d c8f38049359170a34c915e209276238ea66b9a1e +ec69e8838be2dde140a915e50506f8e1ce3cb534 From d1cfb8abd6aa5c76e6c1a4d7ab20929c65f8afc2 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:19:18 +0000 Subject: [PATCH 338/461] [bot] Apply format changes --- src/mesh/parallel/fci_comm.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index cf0ddb01a2..8c34885981 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -58,7 +58,7 @@ struct globalToLocal1D { bool periodic; globalToLocal1D(int mg, int npe, int localwith, bool periodic) : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), - global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; + global(local * npe), globalwith(global + (2 * mg)), periodic(periodic){}; ProcLocal convert(int id) const { if (periodic) { while (id < mg) { @@ -106,7 +106,7 @@ public: GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, std::vector&& data) - : gfa(gfa), data(std::move(data)) {}; + : gfa(gfa), data(std::move(data)){}; private: const GlobalField3DAccess* gfa; From 5c0d41d8b126a59ae261b2448f6895a2954a50b4 Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Wed, 4 Mar 2026 11:19:19 +0000 Subject: [PATCH 339/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 9ec546dec7..7fbaa021ce 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -12,3 +12,4 @@ a71cad2dd6ace5741a754e2ca7daacd4bb094e0e 05b7c571544c3bcb153fce67d12b9ac48947fc2d c8f38049359170a34c915e209276238ea66b9a1e 65880e4af16cb95438437bf057c4b9fdb427b142 +d1cfb8abd6aa5c76e6c1a4d7ab20929c65f8afc2 From 640d27d5b299f1e95ee8ad451bde3162e015d317 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 5 Mar 2026 14:12:10 +0100 Subject: [PATCH 340/461] Add missing headers --- include/bout/field2d.hxx | 1 + include/bout/field3d.hxx | 1 + 2 files changed, 2 insertions(+) diff --git a/include/bout/field2d.hxx b/include/bout/field2d.hxx index 757880ed70..906af38a1d 100644 --- a/include/bout/field2d.hxx +++ b/include/bout/field2d.hxx @@ -24,6 +24,7 @@ * along with BOUT++. If not, see . * */ +#include "bout/utils.hxx" class Field2D; #pragma once diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index 021c45c426..b0318aa9d2 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -20,6 +20,7 @@ * **************************************************************************/ +#include "bout/utils.hxx" #include class Field3D; From 9bb5bca06baa60e4c8be32e08970f04b2f7f7bf1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 6 Mar 2026 13:31:56 +0100 Subject: [PATCH 341/461] Make clear it is a setter --- include/bout/field3d.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index b0318aa9d2..c6e1d1191d 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -513,7 +513,7 @@ public: std::weak_ptr getTracking() { return tracking; }; - bool allowCalcParallelSlices() const { return _allowCalcParallelSlices; }; + bool areCalcParallelSlicesAllowed() const { return _allowCalcParallelSlices; }; void disallowCalcParallelSlices() { _allowCalcParallelSlices = false; }; private: From c95668c1a5e07a29401c52f506d6076f8e01de5d Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Fri, 6 Mar 2026 19:33:25 +0000 Subject: [PATCH 342/461] Bump externalpackages/boutdata from `0e06267` to `11f5d0a` Bumps [externalpackages/boutdata](https://github.com/boutproject/boutdata) from `0e06267` to `11f5d0a`. - [Release notes](https://github.com/boutproject/boutdata/releases) - [Commits](https://github.com/boutproject/boutdata/compare/0e0626716f7fec01d318c427a96e596055540efe...11f5d0acb3d92a30eb0f97866c206a238c487076) --- updated-dependencies: - dependency-name: externalpackages/boutdata dependency-version: 11f5d0acb3d92a30eb0f97866c206a238c487076 dependency-type: direct:production ... Signed-off-by: dependabot[bot] --- externalpackages/boutdata | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/externalpackages/boutdata b/externalpackages/boutdata index 0e0626716f..11f5d0acb3 160000 --- a/externalpackages/boutdata +++ b/externalpackages/boutdata @@ -1 +1 @@ -Subproject commit 0e0626716f7fec01d318c427a96e596055540efe +Subproject commit 11f5d0acb3d92a30eb0f97866c206a238c487076 From 0c3cd4b00966ca98671eb5c7c617ccc3342cc993 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 1 Sep 2025 12:09:51 +0200 Subject: [PATCH 343/461] Allow more parallel boundary regions Also allow to set boundaries for bndry_par_xin/xout and bndry_par_yup/ydown --- src/mesh/boundary_factory.cxx | 98 ++++++++++++++++++++--------------- 1 file changed, 57 insertions(+), 41 deletions(-) diff --git a/src/mesh/boundary_factory.cxx b/src/mesh/boundary_factory.cxx index d112a216ad..5934daff31 100644 --- a/src/mesh/boundary_factory.cxx +++ b/src/mesh/boundary_factory.cxx @@ -228,46 +228,82 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string& varname, string prefix("bndry_"); - string side; + std::array sides; + sides[0] = region->label; switch (region->location) { case BNDRY_XIN: { - side = "xin"; + sides[1] = "xin"; break; } case BNDRY_XOUT: { - side = "xout"; + sides[1] = "xout"; break; } case BNDRY_YDOWN: { - side = "ydown"; + sides[1] = "ydown"; break; } case BNDRY_YUP: { - side = "yup"; + sides[1] = "yup"; break; } case BNDRY_PAR_FWD_XIN: { - side = "par_yup_xin"; + sides[1] = "par_yup_xin"; break; } case BNDRY_PAR_FWD_XOUT: { - side = "par_yup_xout"; + sides[1] = "par_yup_xout"; break; } case BNDRY_PAR_BKWD_XIN: { - side = "par_ydown_xin"; + sides[1] = "par_ydown_xin"; break; } case BNDRY_PAR_BKWD_XOUT: { - side = "par_ydown_xout"; + sides[1] = "par_ydown_xout"; break; } default: { - side = "all"; + sides[1] = "all"; break; } } + switch (region->location) { + case BNDRY_PAR_FWD_XIN: + case BNDRY_PAR_BKWD_XIN: { + sides[2] = "par_xin"; + break; + } + case BNDRY_PAR_BKWD_XOUT: + case BNDRY_PAR_FWD_XOUT: { + sides[2] = "par_xout"; + break; + } + default: { + sides[2] = "all"; + break; + } + } + switch (region->location) { + case BNDRY_PAR_FWD_XIN: + case BNDRY_PAR_FWD_XOUT: { + sides[3] = "par_yup"; + break; + } + case BNDRY_PAR_BKWD_XIN: + case BNDRY_PAR_BKWD_XOUT: { + sides[3] = "par_ydown"; + break; + } + default: { + sides[3] = "all"; + break; + } + } + + sides[4] = region->isParallel ? "par_all" : "all"; + // Get options Options* options = Options::getRoot(); @@ -275,27 +311,10 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string& varname, Options* varOpts = options->getSection(varname); string set; - /// First try looking for (var, region) - if (varOpts->isSet(prefix + region->label)) { - varOpts->get(prefix + region->label, set, ""); - return create(set, region); - } - - /// Then (var, side) - if (varOpts->isSet(prefix + side)) { - varOpts->get(prefix + side, set, ""); - return create(set, region); - } - - /// Then (var, all) - if (region->isParallel) { - if (varOpts->isSet(prefix + "par_all")) { - varOpts->get(prefix + "par_all", set, ""); - return create(set, region); - } - } else { - if (varOpts->isSet(prefix + "all")) { - varOpts->get(prefix + "all", set, ""); + /// First try looking for (var, ...) + for (const auto& side : sides) { + if (varOpts->isSet(prefix + side)) { + varOpts->get(prefix + side, set, ""); return create(set, region); } } @@ -303,16 +322,13 @@ BoundaryOpBase* BoundaryFactory::createFromOptions(const string& varname, // Get the "all" options varOpts = options->getSection("all"); - /// Then (all, region) - if (varOpts->isSet(prefix + region->label)) { - varOpts->get(prefix + region->label, set, ""); - return create(set, region); - } - - /// Then (all, side) - if (varOpts->isSet(prefix + side)) { - varOpts->get(prefix + side, set, ""); - return create(set, region); + /// First try looking for (all, ...) + for (const auto& side : sides) { + if (varOpts->isSet(prefix + side)) { + varOpts->get(prefix + side, set, + region->isParallel ? "parallel_dirichlet_o2" : "dirichlet"); + return create(set, region); + } } /// Then (all, all) From 4cd217b833710c2f401c2185dcc8ce927c48e664 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 10:41:41 +0100 Subject: [PATCH 344/461] Add missing header --- src/mesh/boundary_factory.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/mesh/boundary_factory.cxx b/src/mesh/boundary_factory.cxx index 5934daff31..fd5bbdc70d 100644 --- a/src/mesh/boundary_factory.cxx +++ b/src/mesh/boundary_factory.cxx @@ -6,6 +6,7 @@ #include #include +#include #include #include #include From 92d03c1c1dd6c49350ef6cbee42b931a34cfec47 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Feb 2026 13:53:10 +0100 Subject: [PATCH 345/461] Do not rely on valid region being set --- src/sys/options.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 0a68bab124..c17abb3541 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -364,8 +364,8 @@ void saveParallel(Options& opt, const std::string& name, const Field3D& tosave) Field3D tmp; tmp.allocate(); const auto& fpar = tosave.ynext(i); - for (auto j : fpar.getValidRegionWithDefault("RGN_NO_BOUNDARY")) { - tmp[j.yp(-i)] = fpar[j]; + for (auto j : tmp.getRegion("RGN_NOY")) { + tmp[j] = fpar[j.yp(i)]; } opt[fmt::format("{}_y{:+d}", name, i)] = tmp; } From 201a969ff45a6503affbd8b6d3b2a7cbd21d1a7f Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Feb 2026 14:00:48 +0100 Subject: [PATCH 346/461] Ignore if field is not allocated --- src/sys/options.cxx | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/src/sys/options.cxx b/src/sys/options.cxx index c17abb3541..e864e0cbed 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -364,10 +364,18 @@ void saveParallel(Options& opt, const std::string& name, const Field3D& tosave) Field3D tmp; tmp.allocate(); const auto& fpar = tosave.ynext(i); - for (auto j : tmp.getRegion("RGN_NOY")) { - tmp[j] = fpar[j.yp(i)]; + if (fpar.isAllocated()) { + for (auto j : tmp.getRegion("RGN_NOY")) { + tmp[j] = fpar[j.yp(i)]; + } + opt[fmt::format("{}_y{:+d}", name, i)] = tmp; + } else { + if (tosave.isFci()) { // likely an error + throw BoutException( + "Tried to save parallel fields - but parallel field {i} is not allocated", + i); + } } - opt[fmt::format("{}_y{:+d}", name, i)] = tmp; } } } From a1647993b69c280b6e554ab1af801a80a3c46378 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 9 Mar 2026 11:54:50 +0100 Subject: [PATCH 347/461] Use fmt syntax, not python f-string --- src/sys/options.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/options.cxx b/src/sys/options.cxx index e864e0cbed..7c6f36f85a 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -372,7 +372,7 @@ void saveParallel(Options& opt, const std::string& name, const Field3D& tosave) } else { if (tosave.isFci()) { // likely an error throw BoutException( - "Tried to save parallel fields - but parallel field {i} is not allocated", + "Tried to save parallel fields - but parallel field {} is not allocated", i); } } From cc4fd33681603f024c45eaa46f0c70c4e282a59f Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 9 Aug 2024 13:46:44 +0200 Subject: [PATCH 348/461] Set parallel region by default --- src/field/field3d.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index fe9fdc9263..bd8b831606 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -143,7 +143,9 @@ void Field3D::splitParallelSlices() { // Note the fields constructed here will be fully overwritten by the // ParallelTransform, so we don't need a full constructor yup_fields.emplace_back(fieldmesh); + yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); ydown_fields.emplace_back(fieldmesh); + yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); } } From e4c7d986be427fd3e40a713b6994eee9ce8aacd2 Mon Sep 17 00:00:00 2001 From: David Bold Date: Thu, 26 Sep 2024 09:05:41 +0200 Subject: [PATCH 349/461] Only set region of parallel fields for FCI --- src/field/field3d.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index bd8b831606..01c2dcc2e7 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -143,9 +143,11 @@ void Field3D::splitParallelSlices() { // Note the fields constructed here will be fully overwritten by the // ParallelTransform, so we don't need a full constructor yup_fields.emplace_back(fieldmesh); - yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); ydown_fields.emplace_back(fieldmesh); - yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); + if (isFci()) { + yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); + yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); + } } } From 0cdf3e2b98d14407a03a88c73f5a6159b2abe88a Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 20 Jan 2025 16:28:15 +0100 Subject: [PATCH 350/461] Fix split parallel slices --- src/field/field3d.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 01c2dcc2e7..0ad6d51ffa 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -146,7 +146,7 @@ void Field3D::splitParallelSlices() { ydown_fields.emplace_back(fieldmesh); if (isFci()) { yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); - yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); + ydown_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); } } } From 343fe342c7bd0d945eb0c3107e0deb51a4af0e02 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 30 Sep 2025 14:56:53 +0200 Subject: [PATCH 351/461] add resetRegionParallel to set default regions for parallel slices --- include/bout/field3d.hxx | 1 + src/field/field3d.cxx | 10 ++++++++++ 2 files changed, 11 insertions(+) diff --git a/include/bout/field3d.hxx b/include/bout/field3d.hxx index c6e1d1191d..54d9fb85c9 100644 --- a/include/bout/field3d.hxx +++ b/include/bout/field3d.hxx @@ -331,6 +331,7 @@ public: const Region& getValidRegionWithDefault(const std::string& region_name) const; void setRegion(const std::string& region_name) override; void resetRegion() override { regionID.reset(); }; + void resetRegionParallel(); void setRegion(size_t id) override { regionID = id; }; void setRegion(std::optional id) override { regionID = id; }; std::optional getRegionID() const override { return regionID; }; diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 0ad6d51ffa..1cf54a9900 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -845,6 +845,16 @@ void Field3D::setRegion(const std::string& region_name) { regionID = fieldmesh->getRegionID(region_name); } +void Field3D::resetRegion() { regionID.reset(); }; +void Field3D::resetRegionParallel() { + if (isFci()) { + for (int i = 0; i < fieldmesh->ystart; ++i) { + yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); + ydown_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); + } + } +} + Field3D& Field3D::enableTracking(const std::string& name, std::weak_ptr _tracking) { tracking = std::move(_tracking); From e7592a848f8540b8189dea8ac69447c2d4676bcf Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 10 Nov 2025 12:43:45 +0100 Subject: [PATCH 352/461] Move parallel region creation to defaultRegions --- src/mesh/mesh.cxx | 6 ++++++ src/mesh/parallel/fci.cxx | 10 ---------- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/src/mesh/mesh.cxx b/src/mesh/mesh.cxx index 8964a6b797..010e799b2b 100644 --- a/src/mesh/mesh.cxx +++ b/src/mesh/mesh.cxx @@ -654,6 +654,12 @@ void Mesh::createDefaultRegions() { + getRegion3D("RGN_YGUARDS") + getRegion3D("RGN_ZGUARDS")) .unique()); + for (int offset_ = -ystart; offset_ <= ystart; ++offset_) { + const auto region = fmt::format("RGN_YPAR_{:+d}", offset_); + addRegion3D(region, Region(xstart, xend, ystart + offset_, yend + offset_, 0, + LocalNz - 1, LocalNy, LocalNz)); + } + //2D regions addRegion2D("RGN_ALL", Region(0, LocalNx - 1, 0, LocalNy - 1, 0, 0, LocalNy, 1, maxregionblocksize)); diff --git a/src/mesh/parallel/fci.cxx b/src/mesh/parallel/fci.cxx index 39654519af..152ffaed2d 100644 --- a/src/mesh/parallel/fci.cxx +++ b/src/mesh/parallel/fci.cxx @@ -261,16 +261,6 @@ FCIMap::FCIMap(Mesh& mesh, [[maybe_unused]] const Coordinates::FieldMetric& dy, region_no_boundary = region_no_boundary.mask(to_remove); interp->setRegion(region_no_boundary); - - const auto region = fmt::format("RGN_YPAR_{:+d}", offset_); - if (not map_mesh->hasRegion3D(region)) { - // The valid region for this slice - map_mesh->addRegion3D(region, Region(map_mesh->xstart, map_mesh->xend, - map_mesh->ystart + offset_, - map_mesh->yend + offset_, 0, - map_mesh->LocalNz - 1, map_mesh->LocalNy, - map_mesh->LocalNz)); - } } Field3D FCIMap::integrate(Field3D& f) const { From 051387583cfc5efa6719d505fd620cd6b65d1c4d Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 4 Mar 2026 11:04:50 +0100 Subject: [PATCH 353/461] Remove duplicate definition of resetRegion --- src/field/field3d.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 1cf54a9900..66a5dbfc18 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -845,7 +845,6 @@ void Field3D::setRegion(const std::string& region_name) { regionID = fieldmesh->getRegionID(region_name); } -void Field3D::resetRegion() { regionID.reset(); }; void Field3D::resetRegionParallel() { if (isFci()) { for (int i = 0; i < fieldmesh->ystart; ++i) { From b8bfd3b2f9bcc1802431b6443be86792bb9b843e Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 08:49:11 +0100 Subject: [PATCH 354/461] Rename to GlobalToLocal1D --- src/mesh/parallel/fci_comm.hxx | 28 +++++++++++++++++----------- 1 file changed, 17 insertions(+), 11 deletions(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 8c34885981..836ef9b89d 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -48,7 +48,7 @@ struct ProcLocal { int proc; int ind; }; -struct globalToLocal1D { +struct GlobalToLocal1D { int mg; int npe; int localwith; @@ -56,9 +56,9 @@ struct globalToLocal1D { int global; int globalwith; bool periodic; - globalToLocal1D(int mg, int npe, int localwith, bool periodic) + GlobalToLocal1D(int mg, int npe, int localwith, bool periodic) : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), - global(local * npe), globalwith(global + (2 * mg)), periodic(periodic){}; + global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; ProcLocal convert(int id) const { if (periodic) { while (id < mg) { @@ -77,11 +77,17 @@ struct globalToLocal1D { } const int loc = id - (local * proc); #if CHECK > 1 - if ((loc < 0 or loc > localwith or proc < 0 or proc >= npe) - or (periodic and (loc < mg or loc >= local + mg))) { - printf("globalToLocal1D failure: %d %d, %d %d, %d %d %s\n", id, idwo, globalwith, - npe, proc, loc, periodic ? "periodic" : "non-periodic"); - ASSERT0(false); + if (loc < 0) { + throw BoutException("GlobalToLocal1D failure: loc is {} and thus smaller then 0\n", + loc); + } + ASSERT1(loc <= localwith); + ASSERT1(proc >= 0); + ASSERT1(proc < npe); + if (periodic and (loc < mg or loc >= local + mg)) { + throw BoutException( + "GlobalToLocal1D failure - expected {} < {} < {} because we are periodic\n", mg, + loc, local + mg); } #endif return {proc, loc}; @@ -254,9 +260,9 @@ private: std::set ids; std::map mapping; bool is_setup{false}; - const fci_comm::globalToLocal1D g2lx; - const fci_comm::globalToLocal1D g2ly; - const fci_comm::globalToLocal1D g2lz; + const fci_comm::GlobalToLocal1D g2lx; + const fci_comm::GlobalToLocal1D g2ly; + const fci_comm::GlobalToLocal1D g2lz; public: const fci_comm::XYZ2Ind xyzl; From 52373e7f27a479009f355a0724281d30c40152b4 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 09:01:36 +0100 Subject: [PATCH 355/461] Rename to request --- src/mesh/interpolation/hermite_spline_xz.cxx | 8 ++++---- src/mesh/parallel/fci_comm.hxx | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 2a4f32070a..40f2bb12dc 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -326,10 +326,10 @@ void XZHermiteSplineBase::calcWeights( if constexpr (monotonic) { const auto gind = gf3daccess->xyzg(i_corn, y + y_offset + y_global_offset, k_corner(x, y, z)); - gf3daccess->get(gind); - gf3daccess->get(gind.xp(1)); - gf3daccess->get(gind.zp(1)); - gf3daccess->get(gind.xp(1).zp(1)); + gf3daccess->request(gind); + gf3daccess->request(gind.xp(1)); + gf3daccess->request(gind.zp(1)); + gf3daccess->request(gind.xp(1).zp(1)); g3dinds[i] = {gind.ind, gind.xp(1).ind, gind.zp(1).ind, gind.xp(1).zp(1).ind}; } } diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 836ef9b89d..fbbe1a53ea 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -132,7 +132,7 @@ public: o_ids.resize(omp_get_max_threads()); #endif }; - void get(IndG3D ind) { + void request(IndG3D ind) { ASSERT2(is_setup == false); #ifdef _OPENMP ASSERT2(o_ids.size() > static_cast(omp_get_thread_num())); From c65088198a92368cccbcf126d3398f9b3c8acadc Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 09:01:46 +0100 Subject: [PATCH 356/461] Add some docs --- src/mesh/parallel/fci_comm.hxx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index fbbe1a53ea..8b6c4ecf61 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -41,6 +41,23 @@ #include #include #include + +/// GlobalField3DAccess is a class to set up the communication +/// patterns, to request abitrary data form the global +/// field. GlobalField3DAccessInstance is an instance. after +/// GlobalField3DAccess has communicated the pre-requested data. Only +/// data that has been pre-requested can be requested from +/// GlobalField3DAccessInstance after communication. +/// +/// The usage looks a bit like this: +/// +/// GlobalField3DAccess gfa; +/// // Request an abitrary number of global indices ``gi``: +/// gfa.request(gi) +/// // Communicate data +/// const auto data = gfa.communicate(f3d); +/// // Now data can be accesssed for all previously requested ``gi``s: +/// data[gi] class GlobalField3DAccess; namespace fci_comm { From 3a62dc4bbe2cd51db6974bfeab486122da401ac3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 09:58:51 +0100 Subject: [PATCH 357/461] Use templates over pre-processing Now all implementation can be compiled at once, and switching is a runtime choise. --- include/bout/interpolation_xz.hxx | 32 +- src/mesh/interpolation/hermite_spline_xz.cxx | 450 ++++++++++--------- src/mesh/interpolation_xz.cxx | 11 + src/mesh/parallel/fci_comm.hxx | 1 - 4 files changed, 270 insertions(+), 224 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 6e4f12b1b0..9639a0a80e 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -29,15 +29,14 @@ #include #include -#define USE_NEW_WEIGHTS 1 #if BOUT_HAS_PETSC -#define HS_USE_PETSC 1 -#endif - -#ifdef HS_USE_PETSC #include "bout/petsclib.hxx" #endif +namespace { +enum class implementation_type { new_weights, petsc, legacy }; +} + class Options; class GlobalField3DAccess; @@ -145,7 +144,7 @@ public: /// but also degrades accuracy near maxima and minima. /// Perhaps should only impose near boundaries, since that is where /// problems most obviously occur. -template +template class XZHermiteSplineBase : public XZInterpolation { protected: /// This is protected rather than private so that it can be @@ -174,7 +173,7 @@ protected: std::vector newWeights; -#if HS_USE_PETSC +#if BOUT_HAS_PETSC PetscLib* petsclib; bool isInit{false}; Mat petscWeights; @@ -195,7 +194,7 @@ public: setRegion(regionFromMask(mask, localmesh)); } ~XZHermiteSplineBase() override { -#if HS_USE_PETSC +#if BOUT_HAS_PETSC if (isInit) { MatDestroy(&petscWeights); VecDestroy(&rhs); @@ -224,8 +223,21 @@ public: getWeightsForYApproximation(int i, int j, int k, int yoffset) override; }; -using XZMonotonicHermiteSpline = XZHermiteSplineBase; -using XZHermiteSpline = XZHermiteSplineBase; +using XZMonotonicHermiteSplineSerial = + XZHermiteSplineBase; +using XZHermiteSplineSerial = + XZHermiteSplineBase; +using XZMonotonicHermiteSplineLegacy = + XZHermiteSplineBase; +using XZHermiteSplineLegacy = XZHermiteSplineBase; +#if BOUT_HAS_PETSC +using XZMonotonicHermiteSpline = XZHermiteSplineBase; +using XZHermiteSpline = XZHermiteSplineBase; +#else +using XZMonotonicHermiteSpline = + XZHermiteSplineBase; +using XZHermiteSpline = XZHermiteSplineBase; +#endif /// XZLagrange4pt interpolation class /// diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 40f2bb12dc..3aaa3624d4 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -107,9 +107,9 @@ class IndConverter { } }; -template -XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin, - Options* options) +template +XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin, + Options* options) : XZInterpolation(y_offset, meshin), h00_x(localmesh), h01_x(localmesh), h10_x(localmesh), h11_x(localmesh), h00_z(localmesh), h01_z(localmesh), h10_z(localmesh), h11_z(localmesh) { @@ -140,33 +140,35 @@ XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh* meshin, h10_z.allocate(); h11_z.allocate(); -#if USE_NEW_WEIGHTS - newWeights.reserve(16); - for (int w = 0; w < 16; ++w) { - newWeights.emplace_back(localmesh); - newWeights[w].allocate(); + if constexpr (imp_type == implementation_type::new_weights + || imp_type == implementation_type::petsc) { + newWeights.reserve(16); + for (int w = 0; w < 16; ++w) { + newWeights.emplace_back(localmesh); + newWeights[w].allocate(); + } + } + if constexpr (imp_type == implementation_type::petsc) { + petsclib = new PetscLib( + &Options::root()["mesh:paralleltransform:xzinterpolation:hermitespline"]); + const int m = localmesh->LocalNx * localmesh->LocalNy * localmesh->LocalNz; + const int M = m * localmesh->getNXPE() * localmesh->getNYPE() * localmesh->getNZPE(); + MatCreateAIJ(BoutComm::get(), m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); } -#ifdef HS_USE_PETSC - petsclib = new PetscLib( - &Options::root()["mesh:paralleltransform:xzinterpolation:hermitespline"]); - const int m = localmesh->LocalNx * localmesh->LocalNy * localmesh->LocalNz; - const int M = m * localmesh->getNXPE() * localmesh->getNYPE(); - MatCreateAIJ(BoutComm::get(), m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); -#endif -#endif if constexpr (monotonic) { gf3daccess = std::make_unique(localmesh); g3dinds.reallocate(localmesh->LocalNx, localmesh->LocalNy, localmesh->LocalNz); } -#ifndef HS_USE_PETSC - if (localmesh->getNXPE() > 1) { - throw BoutException("Require PETSc for MPI splitting in X"); + if constexpr (imp_type == implementation_type::new_weights + || imp_type == implementation_type::legacy) { + if (localmesh->getNXPE() > 1) { + throw BoutException("Require PETSc for MPI splitting in X"); + } } -#endif } -template -void XZHermiteSplineBase::calcWeights( +template +void XZHermiteSplineBase::calcWeights( const Field3D& delta_x, const Field3D& delta_z, [[maybe_unused]] const std::string& region) { @@ -174,9 +176,8 @@ void XZHermiteSplineBase::calcWeights( const int nz = localmesh->LocalNz; const int xend = (localmesh->xend - localmesh->xstart + 1) * localmesh->getNXPE() + localmesh->xstart - 1; -#ifdef HS_USE_PETSC - IndConverter conv{localmesh}; -#endif + [[maybe_unused]] IndConverter conv{localmesh}; + [[maybe_unused]] const int y_global_offset = localmesh->getYProcIndex() * (localmesh->yend - localmesh->ystart + 1); BOUT_FOR(i, getRegion(region)) { @@ -246,83 +247,83 @@ void XZHermiteSplineBase::calcWeights( h11_x[i] = (t_x * t_x * t_x) - (t_x * t_x); h11_z[i] = (t_z * t_z * t_z) - (t_z * t_z); -#if USE_NEW_WEIGHTS - - for (int w = 0; w < 16; ++w) { - newWeights[w][i] = 0; - } - // The distribution of our weights: - // 0 4 8 12 - // 1 5 9 13 - // 2 6 10 14 - // 3 7 11 15 - // e.g. 1 == ic.xm(); 4 == ic.zm(); 5 == ic; 7 == ic.zp(2); - - // f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; - newWeights[5][i] += h00_x[i] * h00_z[i]; - newWeights[9][i] += h01_x[i] * h00_z[i]; - newWeights[9][i] += h10_x[i] * h00_z[i] / 2; - newWeights[1][i] -= h10_x[i] * h00_z[i] / 2; - newWeights[13][i] += h11_x[i] * h00_z[i] / 2; - newWeights[5][i] -= h11_x[i] * h00_z[i] / 2; - - // f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + - // fx[iczp] * h10_x[i] + fx[icxpzp] * h11_x[i]; - newWeights[6][i] += h00_x[i] * h01_z[i]; - newWeights[10][i] += h01_x[i] * h01_z[i]; - newWeights[10][i] += h10_x[i] * h01_z[i] / 2; - newWeights[2][i] -= h10_x[i] * h01_z[i] / 2; - newWeights[14][i] += h11_x[i] * h01_z[i] / 2; - newWeights[6][i] -= h11_x[i] * h01_z[i] / 2; - - // fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + - // fxz[ic] * h10_x[i]+ fxz[icxp] * h11_x[i]; - newWeights[6][i] += h00_x[i] * h10_z[i] / 2; - newWeights[4][i] -= h00_x[i] * h10_z[i] / 2; - newWeights[10][i] += h01_x[i] * h10_z[i] / 2; - newWeights[8][i] -= h01_x[i] * h10_z[i] / 2; - newWeights[10][i] += h10_x[i] * h10_z[i] / 4; - newWeights[8][i] -= h10_x[i] * h10_z[i] / 4; - newWeights[2][i] -= h10_x[i] * h10_z[i] / 4; - newWeights[0][i] += h10_x[i] * h10_z[i] / 4; - newWeights[14][i] += h11_x[i] * h10_z[i] / 4; - newWeights[12][i] -= h11_x[i] * h10_z[i] / 4; - newWeights[6][i] -= h11_x[i] * h10_z[i] / 4; - newWeights[4][i] += h11_x[i] * h10_z[i] / 4; - - // fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] + - // fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; - newWeights[7][i] += h00_x[i] * h11_z[i] / 2; - newWeights[5][i] -= h00_x[i] * h11_z[i] / 2; - newWeights[11][i] += h01_x[i] * h11_z[i] / 2; - newWeights[9][i] -= h01_x[i] * h11_z[i] / 2; - newWeights[11][i] += h10_x[i] * h11_z[i] / 4; - newWeights[9][i] -= h10_x[i] * h11_z[i] / 4; - newWeights[3][i] -= h10_x[i] * h11_z[i] / 4; - newWeights[1][i] += h10_x[i] * h11_z[i] / 4; - newWeights[15][i] += h11_x[i] * h11_z[i] / 4; - newWeights[13][i] -= h11_x[i] * h11_z[i] / 4; - newWeights[7][i] -= h11_x[i] * h11_z[i] / 4; - newWeights[5][i] += h11_x[i] * h11_z[i] / 4; -#ifdef HS_USE_PETSC - PetscInt idxn[1] = {conv.fromLocalToGlobal(x, y + y_offset, z)}; - // output.write("debug: {:d} -> {:d}: {:d}:{:d} -> {:d}:{:d}\n", - // conv.fromLocalToGlobal(x, y + y_offset, z), - // conv.fromMeshToGlobal(i_corn, y + y_offset, k_corner(x, y, z)), - // x, z, i_corn, k_corner(x, y, z)); - // ixstep = mesh->LocalNx * mesh->LocalNz; - for (int j = 0; j < 4; ++j) { - PetscInt idxm[4]; - PetscScalar vals[4]; - for (int k = 0; k < 4; ++k) { - idxm[k] = conv.fromMeshToGlobal(i_corn - 1 + j, y + y_offset, - k_corner(x, y, z) - 1 + k); - vals[k] = newWeights[j * 4 + k][i]; + if constexpr (imp_type == implementation_type::new_weights + || imp_type == implementation_type::petsc) { + for (int w = 0; w < 16; ++w) { + newWeights[w][i] = 0; + } + // The distribution of our weights: + // 0 4 8 12 + // 1 5 9 13 + // 2 6 10 14 + // 3 7 11 15 + // e.g. 1 == ic.xm(); 4 == ic.zm(); 5 == ic; 7 == ic.zp(2); + + // f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; + newWeights[5][i] += h00_x[i] * h00_z[i]; + newWeights[9][i] += h01_x[i] * h00_z[i]; + newWeights[9][i] += h10_x[i] * h00_z[i] / 2; + newWeights[1][i] -= h10_x[i] * h00_z[i] / 2; + newWeights[13][i] += h11_x[i] * h00_z[i] / 2; + newWeights[5][i] -= h11_x[i] * h00_z[i] / 2; + + // f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + + // fx[iczp] * h10_x[i] + fx[icxpzp] * h11_x[i]; + newWeights[6][i] += h00_x[i] * h01_z[i]; + newWeights[10][i] += h01_x[i] * h01_z[i]; + newWeights[10][i] += h10_x[i] * h01_z[i] / 2; + newWeights[2][i] -= h10_x[i] * h01_z[i] / 2; + newWeights[14][i] += h11_x[i] * h01_z[i] / 2; + newWeights[6][i] -= h11_x[i] * h01_z[i] / 2; + + // fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + + // fxz[ic] * h10_x[i]+ fxz[icxp] * h11_x[i]; + newWeights[6][i] += h00_x[i] * h10_z[i] / 2; + newWeights[4][i] -= h00_x[i] * h10_z[i] / 2; + newWeights[10][i] += h01_x[i] * h10_z[i] / 2; + newWeights[8][i] -= h01_x[i] * h10_z[i] / 2; + newWeights[10][i] += h10_x[i] * h10_z[i] / 4; + newWeights[8][i] -= h10_x[i] * h10_z[i] / 4; + newWeights[2][i] -= h10_x[i] * h10_z[i] / 4; + newWeights[0][i] += h10_x[i] * h10_z[i] / 4; + newWeights[14][i] += h11_x[i] * h10_z[i] / 4; + newWeights[12][i] -= h11_x[i] * h10_z[i] / 4; + newWeights[6][i] -= h11_x[i] * h10_z[i] / 4; + newWeights[4][i] += h11_x[i] * h10_z[i] / 4; + + // fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] + + // fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; + newWeights[7][i] += h00_x[i] * h11_z[i] / 2; + newWeights[5][i] -= h00_x[i] * h11_z[i] / 2; + newWeights[11][i] += h01_x[i] * h11_z[i] / 2; + newWeights[9][i] -= h01_x[i] * h11_z[i] / 2; + newWeights[11][i] += h10_x[i] * h11_z[i] / 4; + newWeights[9][i] -= h10_x[i] * h11_z[i] / 4; + newWeights[3][i] -= h10_x[i] * h11_z[i] / 4; + newWeights[1][i] += h10_x[i] * h11_z[i] / 4; + newWeights[15][i] += h11_x[i] * h11_z[i] / 4; + newWeights[13][i] -= h11_x[i] * h11_z[i] / 4; + newWeights[7][i] -= h11_x[i] * h11_z[i] / 4; + newWeights[5][i] += h11_x[i] * h11_z[i] / 4; + if (imp_type == implementation_type::petsc) { + PetscInt idxn[1] = {conv.fromLocalToGlobal(x, y + y_offset, z)}; + // output.write("debug: {:d} -> {:d}: {:d}:{:d} -> {:d}:{:d}\n", + // conv.fromLocalToGlobal(x, y + y_offset, z), + // conv.fromMeshToGlobal(i_corn, y + y_offset, k_corner(x, y, z)), + // x, z, i_corn, k_corner(x, y, z)); + // ixstep = mesh->LocalNx * mesh->LocalNz; + for (int j = 0; j < 4; ++j) { + PetscInt idxm[4]; + PetscScalar vals[4]; + for (int k = 0; k < 4; ++k) { + idxm[k] = conv.fromMeshToGlobal(i_corn - 1 + j, y + y_offset, + k_corner(x, y, z) - 1 + k); + vals[k] = newWeights[j * 4 + k][i]; + } + MatSetValues(petscWeights, 1, idxn, 4, idxm, vals, INSERT_VALUES); + } } - MatSetValues(petscWeights, 1, idxn, 4, idxm, vals, INSERT_VALUES); } -#endif -#endif if constexpr (monotonic) { const auto gind = gf3daccess->xyzg(i_corn, y + y_offset + y_global_offset, k_corner(x, y, z)); @@ -336,21 +337,21 @@ void XZHermiteSplineBase::calcWeights( if constexpr (monotonic) { gf3daccess->setup(); } -#ifdef HS_USE_PETSC - MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(petscWeights, MAT_FINAL_ASSEMBLY); - if (!isInit) { - MatCreateVecs(petscWeights, &rhs, &result); + if constexpr (imp_type == implementation_type::petsc) { + MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); + MatAssemblyEnd(petscWeights, MAT_FINAL_ASSEMBLY); + if (!isInit) { + MatCreateVecs(petscWeights, &rhs, &result); + } + isInit = true; } - isInit = true; -#endif } -template -void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, - const Field3D& delta_z, - const BoutMask& mask, - const std::string& region) { +template +void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, + const Field3D& delta_z, + const BoutMask& mask, + const std::string& region) { setMask(mask); calcWeights(delta_x, delta_z, region); } @@ -371,10 +372,10 @@ void XZHermiteSplineBase::calcWeights(const Field3D& delta_x, * (i, j+1, k+1) h01_z + h10_z / 2 * (i, j+1, k+2) h11_z / 2 */ -template +template std::vector -XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, - int yoffset) { +XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, + int yoffset) { if (localmesh->getNXPE() > 1) { throw BoutException("It is likely that the function calling this is not handling the " "result correctly."); @@ -391,8 +392,8 @@ XZHermiteSplineBase::getWeightsForYApproximation(int i, int j, int k, {i, j + yoffset, k_mod_p2, 0.5 * h11_z(i, j, k)}}; } -template -Field3D XZHermiteSplineBase::interpolate( +template +Field3D XZHermiteSplineBase::interpolate( const Field3D& f, [[maybe_unused]] const std::string& region) const { ASSERT1(f.getMesh() == localmesh); @@ -405,118 +406,141 @@ Field3D XZHermiteSplineBase::interpolate( gf = gf3daccess->communicate_asPtr(f); } -#if USE_NEW_WEIGHTS and defined(HS_USE_PETSC) - BoutReal* ptr; - const BoutReal* cptr; - VecGetArray(rhs, &ptr); - BOUT_FOR(i, f.getRegion("RGN_NOY")) { ptr[int(i)] = f[i]; } - VecRestoreArray(rhs, &ptr); - MatMult(petscWeights, rhs, result); - VecGetArrayRead(result, &cptr); - BOUT_FOR(i, f.getRegion(region2)) { - f_interp[i] = cptr[int(i)]; - if constexpr (monotonic) { - const auto iyp = i; - const auto i = iyp.ym(y_offset); -#elif USE_NEW_WEIGHTS // No Petsc - BOUT_FOR(i, getRegion(region)) { - auto ic = i_corner[i]; - auto iyp = i.yp(y_offset); - - f_interp[iyp] = 0; - for (int w = 0; w < 4; ++w) { - f_interp[iyp] += newWeights[w * 4 + 0][i] * f[ic.zm().xp(w - 1)]; - f_interp[iyp] += newWeights[w * 4 + 1][i] * f[ic.xp(w - 1)]; - f_interp[iyp] += newWeights[w * 4 + 2][i] * f[ic.zp().xp(w - 1)]; - f_interp[iyp] += newWeights[w * 4 + 3][i] * f[ic.zp(2).xp(w - 1)]; + if constexpr (imp_type == implementation_type::petsc) { + BoutReal* ptr; + const BoutReal* cptr; + VecGetArray(rhs, &ptr); + BOUT_FOR(i, f.getRegion("RGN_NOY")) { ptr[int(i)] = f[i]; } + VecRestoreArray(rhs, &ptr); + MatMult(petscWeights, rhs, result); + VecGetArrayRead(result, &cptr); + BOUT_FOR(i, f.getRegion(region2)) { + f_interp[i] = cptr[int(i)]; + if constexpr (monotonic) { + const auto iyp = i; + const auto i = iyp.ym(y_offset); + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; + const auto minmax = std::minmax(corners); + const auto diff = + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + } + ASSERT2(std::isfinite(cptr[int(i)])); } - if constexpr (monotonic) { -#else // Legacy interpolation - // TODO(peter): Should we apply dirichlet BCs to derivatives? - // Derivatives are used for tension and need to be on dimensionless - // coordinates - - // f has been communcated, and thus we can assume that the x-boundaries are - // also valid in the y-boundary. Thus the differentiated field needs no - // extra comms. - // TODO(dave) Add assert that we do not use z-splitting or z-guards. - Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT", region2); - Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", region2); - Field3D fxz = bout::derivatives::index::DDZ(fx, CELL_DEFAULT, "DEFAULT", region2); - - BOUT_FOR(i, getRegion(region)) { - const auto iyp = i.yp(y_offset); - - const auto ic = i_corner[i]; - const auto iczp = ic.zp(); - const auto icxp = ic.xp(); - const auto icxpzp = iczp.xp(); - - // Interpolate f in X at Z - const BoutReal f_z = - f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; - - // Interpolate f in X at Z+1 - const BoutReal f_zp1 = f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + fx[iczp] * h10_x[i] - + fx[icxpzp] * h11_x[i]; - - // Interpolate fz in X at Z - const BoutReal fz_z = fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + fxz[ic] * h10_x[i] - + fxz[icxp] * h11_x[i]; - - // Interpolate fz in X at Z+1 - const BoutReal fz_zp1 = fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] - + fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; - - // Interpolate in Z - f_interp[iyp] = - +f_z * h00_z[i] + f_zp1 * h01_z[i] + fz_z * h10_z[i] + fz_zp1 * h11_z[i]; + VecRestoreArrayRead(result, &cptr); + } - if constexpr (monotonic) { -#endif - const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], - (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; - const auto minmax = std::minmax(corners); - const auto diff = - ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; - f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); - f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + if constexpr (imp_type == implementation_type::new_weights) { + BOUT_FOR(i, getRegion(region)) { + auto ic = i_corner[i]; + auto iyp = i.yp(y_offset); + + f_interp[iyp] = 0; + for (int w = 0; w < 4; ++w) { + f_interp[iyp] += newWeights[w * 4 + 0][i] * f[ic.zm().xp(w - 1)]; + f_interp[iyp] += newWeights[w * 4 + 1][i] * f[ic.xp(w - 1)]; + f_interp[iyp] += newWeights[w * 4 + 2][i] * f[ic.zp().xp(w - 1)]; + f_interp[iyp] += newWeights[w * 4 + 3][i] * f[ic.zp(2).xp(w - 1)]; + } + if constexpr (monotonic) { + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; + const auto minmax = std::minmax(corners); + const auto diff = + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + } + ASSERT2(std::isfinite(f_interp[iyp])); } -#if USE_NEW_WEIGHTS and defined(HS_USE_PETSC) - ASSERT2(std::isfinite(cptr[int(i)])); } - VecRestoreArrayRead(result, &cptr); -#elif USE_NEW_WEIGHTS - ASSERT2(std::isfinite(f_interp[iyp])); - } -#else - ASSERT2(std::isfinite(f_interp[iyp]) || i.x() < localmesh->xstart - || i.x() > localmesh->xend); + if constexpr (imp_type == implementation_type::legacy) { + // Legacy interpolation + // TODO(peter): Should we apply dirichlet BCs to derivatives? + // Derivatives are used for tension and need to be on dimensionless + // coordinates + + // f has been communcated, and thus we can assume that the x-boundaries are + // also valid in the y-boundary. Thus the differentiated field needs no + // extra comms. + // TODO(dave) Add assert that we do not use z-splitting or z-guards. + Field3D fx = bout::derivatives::index::DDX(f, CELL_DEFAULT, "DEFAULT", region2); + Field3D fz = bout::derivatives::index::DDZ(f, CELL_DEFAULT, "DEFAULT", region2); + Field3D fxz = bout::derivatives::index::DDZ(fx, CELL_DEFAULT, "DEFAULT", region2); + + BOUT_FOR(i, getRegion(region)) { + const auto iyp = i.yp(y_offset); + + const auto ic = i_corner[i]; + const auto iczp = ic.zp(); + const auto icxp = ic.xp(); + const auto icxpzp = iczp.xp(); + + // Interpolate f in X at Z + const BoutReal f_z = + f[ic] * h00_x[i] + f[icxp] * h01_x[i] + fx[ic] * h10_x[i] + fx[icxp] * h11_x[i]; + + // Interpolate f in X at Z+1 + const BoutReal f_zp1 = f[iczp] * h00_x[i] + f[icxpzp] * h01_x[i] + + fx[iczp] * h10_x[i] + fx[icxpzp] * h11_x[i]; + + // Interpolate fz in X at Z + const BoutReal fz_z = fz[ic] * h00_x[i] + fz[icxp] * h01_x[i] + fxz[ic] * h10_x[i] + + fxz[icxp] * h11_x[i]; + + // Interpolate fz in X at Z+1 + const BoutReal fz_zp1 = fz[iczp] * h00_x[i] + fz[icxpzp] * h01_x[i] + + fxz[iczp] * h10_x[i] + fxz[icxpzp] * h11_x[i]; + + // Interpolate in Z + f_interp[iyp] = + +f_z * h00_z[i] + f_zp1 * h01_z[i] + fz_z * h10_z[i] + fz_zp1 * h11_z[i]; + + if constexpr (monotonic) { + const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], + (*gf)[IndG3D(g3dinds[i][2])], (*gf)[IndG3D(g3dinds[i][3])]}; + const auto minmax = std::minmax(corners); + const auto diff = + ((minmax.second - minmax.first) * rel_fac_monotonic) + abs_fac_monotonic; + f_interp[iyp] = std::max(f_interp[iyp], minmax.first - diff); + f_interp[iyp] = std::min(f_interp[iyp], minmax.second + diff); + } + ASSERT2(std::isfinite(f_interp[iyp]) || i.x() < localmesh->xstart + || i.x() > localmesh->xend); + } } -#endif f_interp.setRegion(region2); ASSERT2(f_interp.getRegionID()); return f_interp; } -template -Field3D XZHermiteSplineBase::interpolate(const Field3D& f, - const Field3D& delta_x, - const Field3D& delta_z, - const std::string& region) { +template +Field3D XZHermiteSplineBase::interpolate(const Field3D& f, + const Field3D& delta_x, + const Field3D& delta_z, + const std::string& region) { calcWeights(delta_x, delta_z, region); return interpolate(f, region); } -template -Field3D -XZHermiteSplineBase::interpolate(const Field3D& f, const Field3D& delta_x, - const Field3D& delta_z, const BoutMask& mask, - const std::string& region) { +template +Field3D XZHermiteSplineBase::interpolate(const Field3D& f, + const Field3D& delta_x, + const Field3D& delta_z, + const BoutMask& mask, + const std::string& region) { calcWeights(delta_x, delta_z, mask, region); return interpolate(f, region); } // ensure they are instantiated -template class XZHermiteSplineBase; -template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +#if BOUT_HAS_PETSC +template class XZHermiteSplineBase; +template class XZHermiteSplineBase; +#endif diff --git a/src/mesh/interpolation_xz.cxx b/src/mesh/interpolation_xz.cxx index f80a361bd1..82234cf840 100644 --- a/src/mesh/interpolation_xz.cxx +++ b/src/mesh/interpolation_xz.cxx @@ -85,6 +85,17 @@ void XZInterpolationFactory::ensureRegistered() {} namespace { RegisterXZInterpolation registerinterphermitespline{"hermitespline"}; +RegisterXZInterpolation registerinterpmonotonichermitespline{ + "monotonichermitespline"}; +RegisterXZInterpolation registerinterphermitesplines{ + "hermitesplineserial"}; +RegisterXZInterpolation + registerinterpmonotonichermitesplines{"monotonichermitesplinelegacy"}; +RegisterXZInterpolation registerinterphermitesplinel{ + "hermitesplineserial"}; +RegisterXZInterpolation + registerinterpmonotonichermitesplinel{"monotonichermitesplinelegacy"}; +RegisterXZInterpolation registerinterphermitespline{"hermitespline"}; RegisterXZInterpolation registerinterpmonotonichermitespline{ "monotonichermitespline"}; RegisterXZInterpolation registerinterplagrange4pt{"lagrange4pt"}; diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 8b6c4ecf61..d34b97aa53 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -159,7 +159,6 @@ public: #endif } - void operator[](IndG3D ind) { get(ind); } void setup() { ASSERT2(is_setup == false); #ifdef _OPENMP From cb84b760595d74e1f31b2b7492b21061e42119c6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 09:59:06 +0100 Subject: [PATCH 358/461] Test serveral implementations --- tests/integrated/test-fci-mpi/runtest | 51 ++++++++++++----------- tests/integrated/test-interpolate/runtest | 2 + 2 files changed, 28 insertions(+), 25 deletions(-) diff --git a/tests/integrated/test-fci-mpi/runtest b/tests/integrated/test-fci-mpi/runtest index c18ab0391d..9962567217 100755 --- a/tests/integrated/test-fci-mpi/runtest +++ b/tests/integrated/test-fci-mpi/runtest @@ -1,6 +1,6 @@ #!/usr/bin/env python3 # -# Python script to run and analyse MMS test +# Python script to run and analyse MPI test from boututils.run_wrapper import build_and_log, launch_safe from boutdata.collect import collect @@ -14,13 +14,13 @@ NLIST = [1, 2, 4] MAXCORES = 8 NSLICES = [1] -build_and_log("FCI MMS test") +build_and_log("FCI MPI test") COLLECT_KW = dict(info=False, xguards=False, yguards=False, path="data") def run_case(nxpe: int, nype: int, mthread: int): - cmd = f"./fci_mpi NXPE={nxpe} NYPE={nype}" + cmd = f"./fci_mpi NXPE={nxpe} NYPE={nype} mesh:paralleltransform:xzinterpolation:type={implementation}" print(f"Running command: {cmd}") _, out = launch_safe(cmd, nproc=nxpe * nype, mthread=mthread, pipe=True) @@ -46,28 +46,29 @@ def test_case(nxpe: int, nype: int, mthread: int, ref: dict) -> bool: failures = [] -for nslice in NSLICES: - # reference data! - run_case(1, 1, MAXCORES) - - ref = {} - for i in range(4): - for yp in range(1, nslice + 1): - for y in [-yp, yp]: - name = f"output_{i}_{y:+d}" - ref[name] = collect(name, **COLLECT_KW) - - for nxpe, nype in itertools.product(NLIST, NLIST): - if (nxpe, nype) == (1, 1): - # reference case, done above - continue - - if nxpe * nype > MAXCORES: - continue - - mthread = MAXCORES // (nxpe * nype) - failures_ = test_case(nxpe, nype, mthread, ref) - failures.extend(failures_) +for implementation in ["hermitespline", "monotonichermitespline"]: + for nslice in NSLICES: + # reference data! + run_case(1, 1, MAXCORES) + + ref = {} + for i in range(4): + for yp in range(1, nslice + 1): + for y in [-yp, yp]: + name = f"output_{i}_{y:+d}" + ref[name] = collect(name, **COLLECT_KW) + + for nxpe, nype in itertools.product(NLIST, NLIST): + if (nxpe, nype) == (1, 1): + # reference case, done above + continue + + if nxpe * nype > MAXCORES: + continue + + mthread = MAXCORES // (nxpe * nype) + failures_ = test_case(nxpe, nype, mthread, ref) + failures.extend(failures_) success = len(failures) == 0 diff --git a/tests/integrated/test-interpolate/runtest b/tests/integrated/test-interpolate/runtest index f5460aff2a..91503f5f9e 100755 --- a/tests/integrated/test-interpolate/runtest +++ b/tests/integrated/test-interpolate/runtest @@ -23,6 +23,8 @@ labels = [r"$" + var + r"$" for var in varlist] methods = { "hermitespline": 3, + "hermitesplinelegacy": 3, + "hermitesplineserial": 3, "lagrange4pt": 3, "bilinear": 2, } From a81d3eb279df51495b5290c8a54fe698aa22d470 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 11:47:05 +0100 Subject: [PATCH 359/461] Cleanup interface and move more functions to cxx file --- src/mesh/interpolation/hermite_spline_xz.cxx | 3 - src/mesh/interpolation_xz.cxx | 7 +- src/mesh/parallel/fci_comm.cxx | 179 +++++++++++++- src/mesh/parallel/fci_comm.hxx | 235 ++++--------------- 4 files changed, 219 insertions(+), 205 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 3aaa3624d4..f622ff572e 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -334,9 +334,6 @@ void XZHermiteSplineBase::calcWeights( g3dinds[i] = {gind.ind, gind.xp(1).ind, gind.zp(1).ind, gind.xp(1).zp(1).ind}; } } - if constexpr (monotonic) { - gf3daccess->setup(); - } if constexpr (imp_type == implementation_type::petsc) { MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); MatAssemblyEnd(petscWeights, MAT_FINAL_ASSEMBLY); diff --git a/src/mesh/interpolation_xz.cxx b/src/mesh/interpolation_xz.cxx index 82234cf840..ec8bcc0502 100644 --- a/src/mesh/interpolation_xz.cxx +++ b/src/mesh/interpolation_xz.cxx @@ -90,14 +90,11 @@ RegisterXZInterpolation registerinterpmonotonichermite RegisterXZInterpolation registerinterphermitesplines{ "hermitesplineserial"}; RegisterXZInterpolation - registerinterpmonotonichermitesplines{"monotonichermitesplinelegacy"}; + registerinterpmonotonichermitesplines{"monotonichermitesplineserial"}; RegisterXZInterpolation registerinterphermitesplinel{ - "hermitesplineserial"}; + "hermitesplinelegacy"}; RegisterXZInterpolation registerinterpmonotonichermitesplinel{"monotonichermitesplinelegacy"}; -RegisterXZInterpolation registerinterphermitespline{"hermitespline"}; -RegisterXZInterpolation registerinterpmonotonichermitespline{ - "monotonichermitespline"}; RegisterXZInterpolation registerinterplagrange4pt{"lagrange4pt"}; RegisterXZInterpolation registerinterpbilinear{"bilinear"}; } // namespace diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 65a8731740..5ff16a980e 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -30,8 +30,179 @@ #include -const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { - auto it = gfa->mapping.find(ind.ind); - ASSERT2(it != gfa->mapping.end()); - return data[it->second]; +fci_comm::ProcLocal fci_comm::GlobalToLocal1D::convert(int id) const { + if (periodic) { + while (id < mg) { + id += global; + } + while (id >= global + mg) { + id -= global; + } + } + const int idwo = id - mg; + int proc = idwo / local; + if (not periodic) { + if (proc >= npe) { + proc = npe - 1; + } + } + const int loc = id - (local * proc); +#if CHECK > 1 + ASSERT1(loc >= 0); + ASSERT1(loc <= localwith); + ASSERT1(proc >= 0); + ASSERT1(proc < npe); + if (periodic and (loc < mg or loc >= local + mg)) { + throw BoutException( + "GlobalToLocal1D failure - expected {} < {} < {} because we are periodic\n", mg, + loc, local + mg); + } +#endif + return {proc, loc}; +} + +void GlobalField3DAccess::setup() { + ASSERT2(is_setup == false); +#ifdef _OPENMP + for (auto& o_id : o_ids) { + ids.merge(o_id); + } + o_ids.clear(); +#endif + toGet.resize(static_cast(g2lx.getNPE() * g2ly.getNPE() * g2lz.getNPE())); + for (const auto id : ids) { + const IndG3D gind{id, g2ly.getGlobalWith(), g2lz.getGlobalWith()}; + const auto pix = g2lx.convert(gind.x()); + const auto piy = g2ly.convert(gind.y()); + const auto piz = g2lz.convert(gind.z()); + ASSERT3(piz.proc == 0); + toGet[(piy.proc * g2lx.getNPE()) + pix.proc].push_back( + xyzl.convert(pix.ind, piy.ind, piz.ind).ind); + } + for (auto& v : toGet) { + std::sort(v.begin(), v.end()); + } + commCommLists(); + { + int offset = 0; + for (const auto& get : toGet) { + getOffsets.push_back(offset); + offset += get.size(); + } + getOffsets.push_back(offset); + } + for (const auto id : ids) { + const IndG3D gind{id, g2ly.getGlobalWith(), g2lz.getGlobalWith()}; + const auto pix = g2lx.convert(gind.x()); + const auto piy = g2ly.convert(gind.y()); + const auto piz = g2lz.convert(gind.z()); + ASSERT3(piz.proc == 0); + const auto proc = (piy.proc * g2lx.getNPE()) + pix.proc; + const auto& vec = toGet[proc]; + const auto tofind = xyzl.convert(pix.ind, piy.ind, piz.ind).ind; + auto it = std::lower_bound(vec.begin(), vec.end(), tofind); + ASSERT3(it != vec.end()); + ASSERT3(*it == tofind); + mapping[id] = std::distance(vec.begin(), it) + getOffsets[proc]; + } + is_setup = true; +} + +void GlobalField3DAccess::commCommLists() { + toSend.resize(toGet.size()); + std::vector toGetSizes(toGet.size(), -1); + std::vector toSendSizes(toSend.size(), -1); +#if CHECK > 3 + { + int thisproc; + MPI_Comm_rank(comm, &thisproc); + ASSERT0(thisproc == mesh->getYProcIndex() * g2lx.getNPE() + mesh->getXProcIndex()); + } +#endif + std::vector reqs(toSend.size()); + for (size_t proc = 0; proc < toGet.size(); ++proc) { + auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, 666, + comm, &reqs[proc]); + ASSERT0(ret == MPI_SUCCESS); + } + for (size_t proc = 0; proc < toGet.size(); ++proc) { + toGetSizes[proc] = toGet[proc].size(); + auto ret = + MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } + std::vector reqs2(toSend.size()); + int cnt = 0; + for ([[maybe_unused]] auto dummy : reqs) { + int ind{0}; + auto ret = MPI_Waitany(reqs.size(), reqs.data(), &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + ASSERT2(static_cast(ind) < toSend.size()); + ASSERT3(toSendSizes[ind] >= 0); + if (toSendSizes[ind] == 0) { + continue; + } + sendBufferSize += toSendSizes[ind]; + toSend[ind].resize(toSendSizes[ind], -1); + + ret = MPI_Irecv(static_cast(toSend[ind].data()), toSend[ind].size(), MPI_INT, + ind, 666 * 666, comm, reqs2.data() + cnt++); + ASSERT0(ret == MPI_SUCCESS); + } + for (size_t proc = 0; proc < toGet.size(); ++proc) { + if (!toGet[proc].empty()) { + const auto ret = MPI_Send(static_cast(toGet[proc].data()), + toGet[proc].size(), MPI_INT, proc, 666 * 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } + } + for (int c = 0; c < cnt; c++) { + int ind{0}; + const auto ret = MPI_Waitany(cnt, reqs2.data(), &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + } +} + +std::vector GlobalField3DAccess::communicate_data(const Field3D& f) { + if (not is_setup) { + setup(); + } + ASSERT2(f.getMesh() == mesh); + std::vector data(getOffsets.back()); + std::vector sendBuffer(sendBufferSize); + std::vector reqs(toSend.size()); + int cnt1 = 0; + for (size_t proc = 0; proc < toGet.size(); ++proc) { + if (toGet[proc].empty()) { + continue; + } + auto ret = + MPI_Irecv(static_cast(data.data() + getOffsets[proc]), toGet[proc].size(), + MPI_DOUBLE, proc, 666, comm, reqs.data() + cnt1); + ASSERT0(ret == MPI_SUCCESS); + cnt1++; + } + int cnt = 0; + for (size_t proc = 0; proc < toGet.size(); ++proc) { + if (toSend[proc].empty()) { + continue; + } + const void* start = static_cast(sendBuffer.data() + cnt); + for (auto i : toSend[proc]) { + sendBuffer[cnt++] = f[Ind3D(i)]; + } + auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666, comm); + ASSERT0(ret == MPI_SUCCESS); + } + for (int j = 0; j < cnt1; ++j) { + int ind{0}; + auto ret = MPI_Waitany(cnt1, reqs.data(), &ind, MPI_STATUS_IGNORE); + ASSERT0(ret == MPI_SUCCESS); + ASSERT3(ind != MPI_UNDEFINED); + ASSERT3(ind >= 0); + ASSERT3(ind < cnt1); + } + return data; } diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index d34b97aa53..af294989fb 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -65,7 +65,19 @@ struct ProcLocal { int proc; int ind; }; + +/// Class to convert global to local indices for 1D +/// given the global index, it returns the local index and the processor. struct GlobalToLocal1D { + GlobalToLocal1D(int mg, int npe, int localwith, bool periodic) + : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), + global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; + ProcLocal convert(int id) const; + int getLocalWith() const { return localwith; } + int getGlobalWith() const { return globalwith; } + int getNPE() const { return npe; } + +private: int mg; int npe; int localwith; @@ -73,60 +85,27 @@ struct GlobalToLocal1D { int global; int globalwith; bool periodic; - GlobalToLocal1D(int mg, int npe, int localwith, bool periodic) - : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), - global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; - ProcLocal convert(int id) const { - if (periodic) { - while (id < mg) { - id += global; - } - while (id >= global + mg) { - id -= global; - } - } - const int idwo = id - mg; - int proc = idwo / local; - if (not periodic) { - if (proc >= npe) { - proc = npe - 1; - } - } - const int loc = id - (local * proc); -#if CHECK > 1 - if (loc < 0) { - throw BoutException("GlobalToLocal1D failure: loc is {} and thus smaller then 0\n", - loc); - } - ASSERT1(loc <= localwith); - ASSERT1(proc >= 0); - ASSERT1(proc < npe); - if (periodic and (loc < mg or loc >= local + mg)) { - throw BoutException( - "GlobalToLocal1D failure - expected {} < {} < {} because we are periodic\n", mg, - loc, local + mg); - } -#endif - return {proc, loc}; - } }; + +/// Convert an x-y-z tupple to an Ind template struct XYZ2Ind { - const int nx; - const int ny; - const int nz; + XYZ2Ind(const int nx, const int ny, const int nz) : nx(nx), ny(ny), nz(nz) {} ind convert(const int x, const int y, const int z) const { return {z + ((y + x * ny) * nz), ny, nz}; } ind operator()(const int x, const int y, const int z) const { return convert(x, y, z); } - XYZ2Ind(const int nx, const int ny, const int nz) : nx(nx), ny(ny), nz(nz) {} + +private: + int nx; + int ny; + int nz; }; } // namespace fci_comm class GlobalField3DAccessInstance { public: const BoutReal& operator[](IndG3D ind) const; - GlobalField3DAccessInstance(const GlobalField3DAccess* gfa, std::vector&& data) : gfa(gfa), data(std::move(data)){}; @@ -143,68 +122,23 @@ public: : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, false), g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), g2lz(mesh->zstart, 1, mesh->LocalNz, true), - xyzl(g2lx.localwith, g2ly.localwith, g2lz.localwith), - xyzg(g2lx.globalwith, g2ly.globalwith, g2lz.globalwith), comm(BoutComm::get()) { + xyzl(g2lx.getLocalWith(), g2ly.getLocalWith(), g2lz.getLocalWith()), + xyzg(g2lx.getGlobalWith(), g2ly.getGlobalWith(), g2lz.getGlobalWith()), + comm(BoutComm::get()) { #ifdef _OPENMP - o_ids.resize(omp_get_max_threads()); + openmp_ids.resize(omp_get_max_threads()); #endif }; void request(IndG3D ind) { ASSERT2(is_setup == false); #ifdef _OPENMP - ASSERT2(o_ids.size() > static_cast(omp_get_thread_num())); - o_ids[omp_get_thread_num()].emplace(ind.ind); + ASSERT2(openmp_ids.size() > static_cast(omp_get_thread_num())); + openmp_ids[omp_get_thread_num()].emplace(ind.ind); #else ids.emplace(ind.ind); #endif } - void setup() { - ASSERT2(is_setup == false); -#ifdef _OPENMP - for (auto& o_id : o_ids) { - ids.merge(o_id); - } - o_ids.clear(); -#endif - toGet.resize(static_cast(g2lx.npe * g2ly.npe * g2lz.npe)); - for (const auto id : ids) { - const IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; - const auto pix = g2lx.convert(gind.x()); - const auto piy = g2ly.convert(gind.y()); - const auto piz = g2lz.convert(gind.z()); - ASSERT3(piz.proc == 0); - toGet[(piy.proc * g2lx.npe) + pix.proc].push_back( - xyzl.convert(pix.ind, piy.ind, piz.ind).ind); - } - for (auto& v : toGet) { - std::sort(v.begin(), v.end()); - } - commCommLists(); - { - int offset = 0; - for (const auto& get : toGet) { - getOffsets.push_back(offset); - offset += get.size(); - } - getOffsets.push_back(offset); - } - for (const auto id : ids) { - const IndG3D gind{id, g2ly.globalwith, g2lz.globalwith}; - const auto pix = g2lx.convert(gind.x()); - const auto piy = g2ly.convert(gind.y()); - const auto piz = g2lz.convert(gind.z()); - ASSERT3(piz.proc == 0); - const auto proc = (piy.proc * g2lx.npe) + pix.proc; - const auto& vec = toGet[proc]; - const auto tofind = xyzl.convert(pix.ind, piy.ind, piz.ind).ind; - auto it = std::lower_bound(vec.begin(), vec.end(), tofind); - ASSERT3(it != vec.end()); - ASSERT3(*it == tofind); - mapping[id] = std::distance(vec.begin(), it) + getOffsets[proc]; - } - is_setup = true; - } GlobalField3DAccessInstance communicate(const Field3D& f) { return {this, communicate_data(f)}; } @@ -213,76 +147,23 @@ public: } private: - void commCommLists() { - toSend.resize(toGet.size()); - std::vector toGetSizes(toGet.size(), -1); - std::vector toSendSizes(toSend.size(), -1); -#if CHECK > 3 - { - int thisproc; - MPI_Comm_rank(comm, &thisproc); - ASSERT0(thisproc == mesh->getYProcIndex() * g2lx.npe + mesh->getXProcIndex()); - } -#endif - std::vector reqs(toSend.size()); - for (size_t proc = 0; proc < toGet.size(); ++proc) { - auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, 666, - comm, &reqs[proc]); - ASSERT0(ret == MPI_SUCCESS); - } - for (size_t proc = 0; proc < toGet.size(); ++proc) { - toGetSizes[proc] = toGet[proc].size(); - auto ret = - MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, 666, comm); - ASSERT0(ret == MPI_SUCCESS); - } - std::vector reqs2(toSend.size()); - int cnt = 0; - for ([[maybe_unused]] auto dummy : reqs) { - int ind{0}; - auto ret = MPI_Waitany(reqs.size(), reqs.data(), &ind, MPI_STATUS_IGNORE); - ASSERT0(ret == MPI_SUCCESS); - ASSERT3(ind != MPI_UNDEFINED); - ASSERT2(static_cast(ind) < toSend.size()); - ASSERT3(toSendSizes[ind] >= 0); - if (toSendSizes[ind] == 0) { - continue; - } - sendBufferSize += toSendSizes[ind]; - toSend[ind].resize(toSendSizes[ind], -1); - - ret = MPI_Irecv(static_cast(toSend[ind].data()), toSend[ind].size(), MPI_INT, - ind, 666 * 666, comm, reqs2.data() + cnt++); - ASSERT0(ret == MPI_SUCCESS); - } - for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (!toGet[proc].empty()) { - const auto ret = MPI_Send(static_cast(toGet[proc].data()), - toGet[proc].size(), MPI_INT, proc, 666 * 666, comm); - ASSERT0(ret == MPI_SUCCESS); - } - } - for (int c = 0; c < cnt; c++) { - int ind{0}; - const auto ret = MPI_Waitany(cnt, reqs2.data(), &ind, MPI_STATUS_IGNORE); - ASSERT0(ret == MPI_SUCCESS); - ASSERT3(ind != MPI_UNDEFINED); - } - } + void setup(); + void commCommLists(); Mesh* mesh; #ifdef _OPENMP - std::vector> o_ids; + // openmp thread-local variable + std::vector> openmp_ids; #endif std::set ids; std::map mapping; bool is_setup{false}; - const fci_comm::GlobalToLocal1D g2lx; - const fci_comm::GlobalToLocal1D g2ly; - const fci_comm::GlobalToLocal1D g2lz; + fci_comm::GlobalToLocal1D g2lx; + fci_comm::GlobalToLocal1D g2ly; + fci_comm::GlobalToLocal1D g2lz; public: - const fci_comm::XYZ2Ind xyzl; - const fci_comm::XYZ2Ind xyzg; + fci_comm::XYZ2Ind xyzl; + fci_comm::XYZ2Ind xyzg; private: std::vector> toGet; @@ -290,43 +171,11 @@ private: std::vector getOffsets; int sendBufferSize{0}; MPI_Comm comm; - std::vector communicate_data(const Field3D& f) { - ASSERT2(is_setup); - ASSERT2(f.getMesh() == mesh); - std::vector data(getOffsets.back()); - std::vector sendBuffer(sendBufferSize); - std::vector reqs(toSend.size()); - int cnt1 = 0; - for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (toGet[proc].empty()) { - continue; - } - auto ret = - MPI_Irecv(static_cast(data.data() + getOffsets[proc]), - toGet[proc].size(), MPI_DOUBLE, proc, 666, comm, reqs.data() + cnt1); - ASSERT0(ret == MPI_SUCCESS); - cnt1++; - } - int cnt = 0; - for (size_t proc = 0; proc < toGet.size(); ++proc) { - if (toSend[proc].empty()) { - continue; - } - const void* start = static_cast(sendBuffer.data() + cnt); - for (auto i : toSend[proc]) { - sendBuffer[cnt++] = f[Ind3D(i)]; - } - auto ret = MPI_Send(start, toSend[proc].size(), MPI_DOUBLE, proc, 666, comm); - ASSERT0(ret == MPI_SUCCESS); - } - for (int j = 0; j < cnt1; ++j) { - int ind{0}; - auto ret = MPI_Waitany(cnt1, reqs.data(), &ind, MPI_STATUS_IGNORE); - ASSERT0(ret == MPI_SUCCESS); - ASSERT3(ind != MPI_UNDEFINED); - ASSERT3(ind >= 0); - ASSERT3(ind < cnt1); - } - return data; - } + std::vector communicate_data(const Field3D& f); }; + +const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { + auto it = gfa->mapping.find(ind.ind); + ASSERT2(it != gfa->mapping.end()); + return data[it->second]; +} From 4632df093c1632eb2f0060e4c037813f4179dcaf Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 11:51:41 +0100 Subject: [PATCH 360/461] Move processor ID calculation to mesh --- include/bout/mesh.hxx | 2 ++ src/mesh/impls/bout/boutmesh.cxx | 4 ++++ src/mesh/impls/bout/boutmesh.hxx | 1 + src/mesh/parallel/fci_comm.cxx | 6 +++--- 4 files changed, 10 insertions(+), 3 deletions(-) diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index 502b0c5668..a1ed6a9011 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -360,6 +360,8 @@ public: virtual int getXProcIndex() const = 0; ///< This processor's index in X direction virtual int getYProcIndex() const = 0; ///< This processor's index in Y direction virtual int getZProcIndex() const = 0; ///< This processor's index in Z direction + /// The index of a processor with given X, Y, and Z index + virtual int getProcIndex(int X, int Y, int Z) const = 0; // X communications virtual bool firstX() diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 4aaa760c04..41440b50ba 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -523,6 +523,8 @@ int BoutMesh::load() { PE_XIND = MYPE % NXPE; PE_ZIND = 0; + ASSERT2(MYPE == getProcIndex(PE_XIND, PE_YIND, PE_ZIND)); + // Set the other grid sizes from nx, ny, nz setDerivedGridSizes(); @@ -1004,6 +1006,8 @@ void BoutMesh::createXBoundaries() { } } +int BoutMesh::getProcIndex(int X, int Y, int Z) { return Y * NXPE + X; } + void BoutMesh::createYBoundaries() { if (MYG <= 0) { return; diff --git a/src/mesh/impls/bout/boutmesh.hxx b/src/mesh/impls/bout/boutmesh.hxx index 3923c34511..b42bf325b5 100644 --- a/src/mesh/impls/bout/boutmesh.hxx +++ b/src/mesh/impls/bout/boutmesh.hxx @@ -63,6 +63,7 @@ public: int getXProcIndex() const override; ///< This processor's index in X direction int getYProcIndex() const override; ///< This processor's index in Y direction int getZProcIndex() const override; ///< This processor's index in Z direction + int getProcIndex(int X, int Y, int Z) const override; ///////////////////////////////////////////// // X communications diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 5ff16a980e..c443912c96 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -76,7 +76,7 @@ void GlobalField3DAccess::setup() { const auto piy = g2ly.convert(gind.y()); const auto piz = g2lz.convert(gind.z()); ASSERT3(piz.proc == 0); - toGet[(piy.proc * g2lx.getNPE()) + pix.proc].push_back( + toGet[mesh.getProcIndex(pix.proc, piy.index, piz.index)].push_back( xyzl.convert(pix.ind, piy.ind, piz.ind).ind); } for (auto& v : toGet) { @@ -97,7 +97,7 @@ void GlobalField3DAccess::setup() { const auto piy = g2ly.convert(gind.y()); const auto piz = g2lz.convert(gind.z()); ASSERT3(piz.proc == 0); - const auto proc = (piy.proc * g2lx.getNPE()) + pix.proc; + const auto proc = mesh.getProcIndex(pix.proc, piy.index, piz.index); const auto& vec = toGet[proc]; const auto tofind = xyzl.convert(pix.ind, piy.ind, piz.ind).ind; auto it = std::lower_bound(vec.begin(), vec.end(), tofind); @@ -116,7 +116,7 @@ void GlobalField3DAccess::commCommLists() { { int thisproc; MPI_Comm_rank(comm, &thisproc); - ASSERT0(thisproc == mesh->getYProcIndex() * g2lx.getNPE() + mesh->getXProcIndex()); + ASSERT0(thisproc == mesh.getProcIndex(pix.proc, piy.index, piz.index)); } #endif std::vector reqs(toSend.size()); From 4b0ed1c452b4e8c1b1954c47d8cbdb1ededb5377 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:03:02 +0100 Subject: [PATCH 361/461] Use getNZPE() --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index af294989fb..c096b1433a 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -121,7 +121,7 @@ public: GlobalField3DAccess(Mesh* mesh) : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, false), g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), - g2lz(mesh->zstart, 1, mesh->LocalNz, true), + g2lz(mesh->zstart, mesh->getNZPE(), mesh->LocalNz, true), xyzl(g2lx.getLocalWith(), g2ly.getLocalWith(), g2lz.getLocalWith()), xyzg(g2lx.getGlobalWith(), g2ly.getGlobalWith(), g2lz.getGlobalWith()), comm(BoutComm::get()) { From 325dde3feaa545d03ab7b0ac360bf09a975d6f9c Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:03:13 +0100 Subject: [PATCH 362/461] respect mesh->periodicX --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index c096b1433a..b90d0e8338 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -119,7 +119,7 @@ class GlobalField3DAccess { public: friend class GlobalField3DAccessInstance; GlobalField3DAccess(Mesh* mesh) - : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, false), + : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, mesh->periodicX), g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), g2lz(mesh->zstart, mesh->getNZPE(), mesh->LocalNz, true), xyzl(g2lx.getLocalWith(), g2ly.getLocalWith(), g2lz.getLocalWith()), From 8dc7b4f4c3cca4eeb7796c5dda790f745b97b2d5 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:03:27 +0100 Subject: [PATCH 363/461] Ensure we are periodic in Y --- src/mesh/parallel/fci_comm.hxx | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index b90d0e8338..6f1064eea7 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -127,6 +127,13 @@ public: comm(BoutComm::get()) { #ifdef _OPENMP openmp_ids.resize(omp_get_max_threads()); +#endif +#if CHECK >= 2 + // We could also allow false, but then we would need to ensure it + // is false everywhere. + for (int x = 0; x < mesh->localNx, ++x) { + ASSERT2(mesh->periodicY(x) == true); + } #endif }; void request(IndG3D ind) { From db5385234a3bc96c73bd89b41ef070a5196f17db Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:03:47 +0100 Subject: [PATCH 364/461] avoid static_cast for pointer --- src/mesh/parallel/fci_comm.cxx | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index c443912c96..6419b455c9 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -121,14 +121,12 @@ void GlobalField3DAccess::commCommLists() { #endif std::vector reqs(toSend.size()); for (size_t proc = 0; proc < toGet.size(); ++proc) { - auto ret = MPI_Irecv(static_cast(&toSendSizes[proc]), 1, MPI_INT, proc, 666, - comm, &reqs[proc]); + auto ret = MPI_Irecv(&toSendSizes[proc], 1, MPI_INT, proc, 666, comm, &reqs[proc]); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { toGetSizes[proc] = toGet[proc].size(); - auto ret = - MPI_Send(static_cast(&toGetSizes[proc]), 1, MPI_INT, proc, 666, comm); + auto ret = MPI_Send(&toGetSizes[proc], 1, MPI_INT, proc, 666, comm); ASSERT0(ret == MPI_SUCCESS); } std::vector reqs2(toSend.size()); @@ -146,14 +144,14 @@ void GlobalField3DAccess::commCommLists() { sendBufferSize += toSendSizes[ind]; toSend[ind].resize(toSendSizes[ind], -1); - ret = MPI_Irecv(static_cast(toSend[ind].data()), toSend[ind].size(), MPI_INT, - ind, 666 * 666, comm, reqs2.data() + cnt++); + ret = MPI_Irecv(toSend[ind].data(), toSend[ind].size(), MPI_INT, ind, 666 * 666, comm, + reqs2.data() + cnt++); ASSERT0(ret == MPI_SUCCESS); } for (size_t proc = 0; proc < toGet.size(); ++proc) { if (!toGet[proc].empty()) { - const auto ret = MPI_Send(static_cast(toGet[proc].data()), - toGet[proc].size(), MPI_INT, proc, 666 * 666, comm); + const auto ret = MPI_Send(toGet[proc].data(), toGet[proc].size(), MPI_INT, proc, + 666 * 666, comm); ASSERT0(ret == MPI_SUCCESS); } } @@ -178,9 +176,8 @@ std::vector GlobalField3DAccess::communicate_data(const Field3D& f) { if (toGet[proc].empty()) { continue; } - auto ret = - MPI_Irecv(static_cast(data.data() + getOffsets[proc]), toGet[proc].size(), - MPI_DOUBLE, proc, 666, comm, reqs.data() + cnt1); + auto ret = MPI_Irecv(data.data() + getOffsets[proc], toGet[proc].size(), MPI_DOUBLE, + proc, 666, comm, reqs.data() + cnt1); ASSERT0(ret == MPI_SUCCESS); cnt1++; } @@ -189,7 +186,7 @@ std::vector GlobalField3DAccess::communicate_data(const Field3D& f) { if (toSend[proc].empty()) { continue; } - const void* start = static_cast(sendBuffer.data() + cnt); + const void* start = sendBuffer.data() + cnt; for (auto i : toSend[proc]) { sendBuffer[cnt++] = f[Ind3D(i)]; } From dcafb6c6b70afdccfed7e97f055c2bed2ae38aa1 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:11:48 +0100 Subject: [PATCH 365/461] Add more docs --- src/mesh/parallel/fci_comm.cxx | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 6419b455c9..ff592f20a5 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -62,6 +62,15 @@ fci_comm::ProcLocal fci_comm::GlobalToLocal1D::convert(int id) const { } void GlobalField3DAccess::setup() { + // We need to send a list of data to every processor of which data + // we want We also get a list of all the data that every other + // processor wants Some of theses lists may be empty. We still need + // to send them, later we can skip that. We also compute where the + // requested data will be stored later on. This is currently + // implemented as a map, to memory efficient, as the data is + // sparse. We could also store the mapping as a dense array, if it + // turns out this lookup is not fast enough, but that may limit + // scaling at some point. ASSERT2(is_setup == false); #ifdef _OPENMP for (auto& o_id : o_ids) { @@ -164,9 +173,12 @@ void GlobalField3DAccess::commCommLists() { } std::vector GlobalField3DAccess::communicate_data(const Field3D& f) { + // Ensure setup is called, to setup communication pattern if (not is_setup) { setup(); } + // We now send the previosly requested data to every processor, that wanted some. + // We also get the data we requested. ASSERT2(f.getMesh() == mesh); std::vector data(getOffsets.back()); std::vector sendBuffer(sendBufferSize); From 424136bc62436d779c1b8d2eef21ef71ef909387 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:14:57 +0100 Subject: [PATCH 366/461] Use better names --- src/mesh/parallel/fci_comm.cxx | 23 ++++++++++++----------- src/mesh/parallel/fci_comm.hxx | 20 ++++++++++---------- 2 files changed, 22 insertions(+), 21 deletions(-) diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index ff592f20a5..238d4eb973 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -78,15 +78,16 @@ void GlobalField3DAccess::setup() { } o_ids.clear(); #endif - toGet.resize(static_cast(g2lx.getNPE() * g2ly.getNPE() * g2lz.getNPE())); + toGet.resize(static_cast(global2local_x.getNPE() * global2local_y.getNPE() + * global2local_z.getNPE())); for (const auto id : ids) { - const IndG3D gind{id, g2ly.getGlobalWith(), g2lz.getGlobalWith()}; - const auto pix = g2lx.convert(gind.x()); - const auto piy = g2ly.convert(gind.y()); - const auto piz = g2lz.convert(gind.z()); + const IndG3D gind{id, global2local_y.getGlobalWith(), global2local_z.getGlobalWith()}; + const auto pix = global2local_x.convert(gind.x()); + const auto piy = global2local_y.convert(gind.y()); + const auto piz = global2local_z.convert(gind.z()); ASSERT3(piz.proc == 0); toGet[mesh.getProcIndex(pix.proc, piy.index, piz.index)].push_back( - xyzl.convert(pix.ind, piy.ind, piz.ind).ind); + xyzlocal.convert(pix.ind, piy.ind, piz.ind).ind); } for (auto& v : toGet) { std::sort(v.begin(), v.end()); @@ -101,14 +102,14 @@ void GlobalField3DAccess::setup() { getOffsets.push_back(offset); } for (const auto id : ids) { - const IndG3D gind{id, g2ly.getGlobalWith(), g2lz.getGlobalWith()}; - const auto pix = g2lx.convert(gind.x()); - const auto piy = g2ly.convert(gind.y()); - const auto piz = g2lz.convert(gind.z()); + const IndG3D gind{id, global2local_y.getGlobalWith(), global2local_z.getGlobalWith()}; + const auto pix = global2local_x.convert(gind.x()); + const auto piy = global2local_y.convert(gind.y()); + const auto piz = global2local_z.convert(gind.z()); ASSERT3(piz.proc == 0); const auto proc = mesh.getProcIndex(pix.proc, piy.index, piz.index); const auto& vec = toGet[proc]; - const auto tofind = xyzl.convert(pix.ind, piy.ind, piz.ind).ind; + const auto tofind = xyzlocal.convert(pix.ind, piy.ind, piz.ind).ind; auto it = std::lower_bound(vec.begin(), vec.end(), tofind); ASSERT3(it != vec.end()); ASSERT3(*it == tofind); diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 6f1064eea7..ef7bfd2eac 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -119,11 +119,12 @@ class GlobalField3DAccess { public: friend class GlobalField3DAccessInstance; GlobalField3DAccess(Mesh* mesh) - : mesh(mesh), g2lx(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, mesh->periodicX), - g2ly(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), - g2lz(mesh->zstart, mesh->getNZPE(), mesh->LocalNz, true), - xyzl(g2lx.getLocalWith(), g2ly.getLocalWith(), g2lz.getLocalWith()), - xyzg(g2lx.getGlobalWith(), g2ly.getGlobalWith(), g2lz.getGlobalWith()), + : mesh(mesh), + global2local_x(mesh->xstart, mesh->getNXPE(), mesh->LocalNx, mesh->periodicX), + global2local_y(mesh->ystart, mesh->getNYPE(), mesh->LocalNy, true), + global2local_z(mesh->zstart, mesh->getNZPE(), mesh->LocalNz, true), + xyzlocal(global2local_x.getLocalWith(), global2local_y.getLocalWith(), + global2local_z.getLocalWith()), comm(BoutComm::get()) { #ifdef _OPENMP openmp_ids.resize(omp_get_max_threads()); @@ -164,13 +165,12 @@ private: std::set ids; std::map mapping; bool is_setup{false}; - fci_comm::GlobalToLocal1D g2lx; - fci_comm::GlobalToLocal1D g2ly; - fci_comm::GlobalToLocal1D g2lz; + fci_comm::GlobalToLocal1D global2local_x; + fci_comm::GlobalToLocal1D global2localy; + fci_comm::GlobalToLocal1D global2local_z; public: - fci_comm::XYZ2Ind xyzl; - fci_comm::XYZ2Ind xyzg; + fci_comm::XYZ2Ind xyzlocal; private: std::vector> toGet; From 500989be908114629ec1bbedf7c5888b1d020178 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:43:54 +0100 Subject: [PATCH 367/461] Fixup getProcIndex --- src/mesh/impls/bout/boutmesh.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index ac797f81cd..3dad85f211 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -1017,7 +1017,7 @@ void BoutMesh::createXBoundaries() { } } -int BoutMesh::getProcIndex(int X, int Y, int Z) { return Y * NXPE + X; } +int BoutMesh::getProcIndex(int X, int Y, int Z) const { return Y * NXPE + X; } void BoutMesh::createYBoundaries() { if (MYG <= 0) { From cabf678708f5fd68f0e650ed904e861101dc2315 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 12:45:06 +0100 Subject: [PATCH 368/461] Add headers from clang-tidy --- src/mesh/interpolation/hermite_spline_xz.cxx | 2 ++ src/mesh/parallel/fci_comm.cxx | 3 +++ 2 files changed, 5 insertions(+) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index f622ff572e..6ec1549429 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -23,6 +23,8 @@ #include "../impls/bout/boutmesh.hxx" #include "../parallel/fci_comm.hxx" #include "bout/bout.hxx" +#include "bout/boutexception.hxx" +#include "bout/build_defines.hxx" #include "bout/globals.hxx" #include "bout/index_derivs_interface.hxx" #include "bout/interpolation_xz.hxx" diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 238d4eb973..c81b1c30a7 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -28,6 +28,9 @@ #include "bout/bout_types.hxx" #include "bout/region.hxx" +#include +#include +#include #include fci_comm::ProcLocal fci_comm::GlobalToLocal1D::convert(int id) const { From 01e22639c8e85f13397b6bff075ec9dbdc38055c Mon Sep 17 00:00:00 2001 From: dschwoerer <5637662+dschwoerer@users.noreply.github.com> Date: Tue, 10 Mar 2026 11:54:53 +0000 Subject: [PATCH 369/461] [bot] Apply format changes --- src/mesh/parallel/fci_comm.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index ef7bfd2eac..852297fde8 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -71,7 +71,7 @@ struct ProcLocal { struct GlobalToLocal1D { GlobalToLocal1D(int mg, int npe, int localwith, bool periodic) : mg(mg), npe(npe), localwith(localwith), local(localwith - (2 * mg)), - global(local * npe), globalwith(global + (2 * mg)), periodic(periodic) {}; + global(local * npe), globalwith(global + (2 * mg)), periodic(periodic){}; ProcLocal convert(int id) const; int getLocalWith() const { return localwith; } int getGlobalWith() const { return globalwith; } From 6bd15d2e0be02f63ff2b8a251e1ea3463750f4e0 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 13:12:07 +0100 Subject: [PATCH 370/461] Apply clang-tidy fixes --- include/bout/interpolation_xz.hxx | 4 ++-- src/mesh/interpolation/hermite_spline_xz.cxx | 16 ++++++++-------- src/mesh/parallel/fci_comm.cxx | 5 +++-- src/mesh/parallel/fci_comm.hxx | 8 ++------ 4 files changed, 15 insertions(+), 18 deletions(-) diff --git a/include/bout/interpolation_xz.hxx b/include/bout/interpolation_xz.hxx index 9639a0a80e..d14a5eeca0 100644 --- a/include/bout/interpolation_xz.hxx +++ b/include/bout/interpolation_xz.hxx @@ -176,8 +176,8 @@ protected: #if BOUT_HAS_PETSC PetscLib* petsclib; bool isInit{false}; - Mat petscWeights; - Vec rhs, result; + Mat petscWeights{}; + Vec rhs{}, result{}; #endif /// Factors to allow for some wiggleroom diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 6ec1549429..eec1e8adce 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -178,7 +178,7 @@ void XZHermiteSplineBase::calcWeights( const int nz = localmesh->LocalNz; const int xend = (localmesh->xend - localmesh->xstart + 1) * localmesh->getNXPE() + localmesh->xstart - 1; - [[maybe_unused]] IndConverter conv{localmesh}; + [[maybe_unused]] const IndConverter conv{localmesh}; [[maybe_unused]] const int y_global_offset = localmesh->getYProcIndex() * (localmesh->yend - localmesh->ystart + 1); @@ -320,7 +320,7 @@ void XZHermiteSplineBase::calcWeights( for (int k = 0; k < 4; ++k) { idxm[k] = conv.fromMeshToGlobal(i_corn - 1 + j, y + y_offset, k_corner(x, y, z) - 1 + k); - vals[k] = newWeights[j * 4 + k][i]; + vals[k] = newWeights[(j * 4) + k][i]; } MatSetValues(petscWeights, 1, idxn, 4, idxm, vals, INSERT_VALUES); } @@ -406,8 +406,8 @@ Field3D XZHermiteSplineBase::interpolate( } if constexpr (imp_type == implementation_type::petsc) { - BoutReal* ptr; - const BoutReal* cptr; + BoutReal* ptr = nullptr; + const BoutReal* cptr = nullptr; VecGetArray(rhs, &ptr); BOUT_FOR(i, f.getRegion("RGN_NOY")) { ptr[int(i)] = f[i]; } VecRestoreArray(rhs, &ptr); @@ -438,10 +438,10 @@ Field3D XZHermiteSplineBase::interpolate( f_interp[iyp] = 0; for (int w = 0; w < 4; ++w) { - f_interp[iyp] += newWeights[w * 4 + 0][i] * f[ic.zm().xp(w - 1)]; - f_interp[iyp] += newWeights[w * 4 + 1][i] * f[ic.xp(w - 1)]; - f_interp[iyp] += newWeights[w * 4 + 2][i] * f[ic.zp().xp(w - 1)]; - f_interp[iyp] += newWeights[w * 4 + 3][i] * f[ic.zp(2).xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 0][i] * f[ic.zm().xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 1][i] * f[ic.xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 2][i] * f[ic.zp().xp(w - 1)]; + f_interp[iyp] += newWeights[(w * 4) + 3][i] * f[ic.zp(2).xp(w - 1)]; } if constexpr (monotonic) { const auto corners = {(*gf)[IndG3D(g3dinds[i][0])], (*gf)[IndG3D(g3dinds[i][1])], diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index c81b1c30a7..6cd59d96cb 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -26,6 +26,7 @@ #include "fci_comm.hxx" #include "bout/assert.hxx" #include "bout/bout_types.hxx" +#include "bout/field3d.hxx" #include "bout/region.hxx" #include @@ -89,7 +90,7 @@ void GlobalField3DAccess::setup() { const auto piy = global2local_y.convert(gind.y()); const auto piz = global2local_z.convert(gind.z()); ASSERT3(piz.proc == 0); - toGet[mesh.getProcIndex(pix.proc, piy.index, piz.index)].push_back( + toGet[mesh->getProcIndex(pix.proc, piy.index, piz.index)].push_back( xyzlocal.convert(pix.ind, piy.ind, piz.ind).ind); } for (auto& v : toGet) { @@ -110,7 +111,7 @@ void GlobalField3DAccess::setup() { const auto piy = global2local_y.convert(gind.y()); const auto piz = global2local_z.convert(gind.z()); ASSERT3(piz.proc == 0); - const auto proc = mesh.getProcIndex(pix.proc, piy.index, piz.index); + const auto proc = mesh->getProcIndex(pix.proc, piy.index, piz.index); const auto& vec = toGet[proc]; const auto tofind = xyzlocal.convert(pix.ind, piy.ind, piz.ind).ind; auto it = std::lower_bound(vec.begin(), vec.end(), tofind); diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index 852297fde8..ae286fd232 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -31,10 +31,6 @@ #include "bout/field3d.hxx" #include "bout/mesh.hxx" #include "bout/region.hxx" -#include -#include -#include -#include #include #include #include @@ -166,11 +162,11 @@ private: std::map mapping; bool is_setup{false}; fci_comm::GlobalToLocal1D global2local_x; - fci_comm::GlobalToLocal1D global2localy; + fci_comm::GlobalToLocal1D global2local_y; fci_comm::GlobalToLocal1D global2local_z; public: - fci_comm::XYZ2Ind xyzlocal; + fci_comm::XYZ2Ind xyzlocal{}; private: std::vector> toGet; From f2d860f80dcf5de4d728a2c8a8e821b7d81d1428 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 10 Mar 2026 13:36:46 +0100 Subject: [PATCH 371/461] Fixup compile issues --- src/mesh/interpolation/hermite_spline_xz.cxx | 10 +++++----- src/mesh/parallel/fci_comm.cxx | 10 ++++++---- src/mesh/parallel/fci_comm.hxx | 11 +++++++---- 3 files changed, 18 insertions(+), 13 deletions(-) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index eec1e8adce..0a2c9904c7 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -44,7 +44,7 @@ class IndConverter { lnz(mesh->LocalNz - 2 * zstart) {} // ix and iz are global indices // iy is local - int fromMeshToGlobal(int ix, int iy, int iz) { + int fromMeshToGlobal(int ix, int iy, int iz) const { const int xstart = mesh->xstart; const int lnx = mesh->LocalNx - xstart * 2; // x-proc-id @@ -78,12 +78,12 @@ class IndConverter { return fromLocalToGlobal(ix - pex * lnx, iy - pey_offset * lny, iz - pez * lnz, pex, pey, 0); } - int fromLocalToGlobal(const int ilocalx, const int ilocaly, const int ilocalz) { + int fromLocalToGlobal(const int ilocalx, const int ilocaly, const int ilocalz) const { return fromLocalToGlobal(ilocalx, ilocaly, ilocalz, mesh->getXProcIndex(), mesh->getYProcIndex(), 0); } int fromLocalToGlobal(const int ilocalx, const int ilocaly, const int ilocalz, - const int pex, const int pey, const int pez) { + const int pex, const int pey, const int pez) const { ASSERT3(ilocalx >= 0); ASSERT3(ilocaly >= 0); ASSERT3(ilocalz >= 0); @@ -327,8 +327,8 @@ void XZHermiteSplineBase::calcWeights( } } if constexpr (monotonic) { - const auto gind = - gf3daccess->xyzg(i_corn, y + y_offset + y_global_offset, k_corner(x, y, z)); + const auto gind = gf3daccess->xyzglobal(i_corn, y + y_offset + y_global_offset, + k_corner(x, y, z)); gf3daccess->request(gind); gf3daccess->request(gind.xp(1)); gf3daccess->request(gind.zp(1)); diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 6cd59d96cb..a9c574c430 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -90,8 +90,8 @@ void GlobalField3DAccess::setup() { const auto piy = global2local_y.convert(gind.y()); const auto piz = global2local_z.convert(gind.z()); ASSERT3(piz.proc == 0); - toGet[mesh->getProcIndex(pix.proc, piy.index, piz.index)].push_back( - xyzlocal.convert(pix.ind, piy.ind, piz.ind).ind); + toGet[mesh->getProcIndex(pix.proc, piy.proc, piz.proc)].push_back( + xyzlocal.convert(pix.index, piy.index, piz.index).ind); } for (auto& v : toGet) { std::sort(v.begin(), v.end()); @@ -113,7 +113,7 @@ void GlobalField3DAccess::setup() { ASSERT3(piz.proc == 0); const auto proc = mesh->getProcIndex(pix.proc, piy.index, piz.index); const auto& vec = toGet[proc]; - const auto tofind = xyzlocal.convert(pix.ind, piy.ind, piz.ind).ind; + const auto tofind = xyzlocal.convert(pix.index, piy.index, piz.index).ind; auto it = std::lower_bound(vec.begin(), vec.end(), tofind); ASSERT3(it != vec.end()); ASSERT3(*it == tofind); @@ -130,7 +130,9 @@ void GlobalField3DAccess::commCommLists() { { int thisproc; MPI_Comm_rank(comm, &thisproc); - ASSERT0(thisproc == mesh.getProcIndex(pix.proc, piy.index, piz.index)); + ASSERT0(thisproc + == mesh->getProcIndex(mesh->getXProcIndex(), mesh->getYProcIndex(), + mesh->getZProcIndex())); } #endif std::vector reqs(toSend.size()); diff --git a/src/mesh/parallel/fci_comm.hxx b/src/mesh/parallel/fci_comm.hxx index ae286fd232..324cae8a22 100644 --- a/src/mesh/parallel/fci_comm.hxx +++ b/src/mesh/parallel/fci_comm.hxx @@ -59,7 +59,7 @@ class GlobalField3DAccess; namespace fci_comm { struct ProcLocal { int proc; - int ind; + int index; }; /// Class to convert global to local indices for 1D @@ -121,6 +121,8 @@ public: global2local_z(mesh->zstart, mesh->getNZPE(), mesh->LocalNz, true), xyzlocal(global2local_x.getLocalWith(), global2local_y.getLocalWith(), global2local_z.getLocalWith()), + xyzglobal(global2local_x.getGlobalWith(), global2local_y.getGlobalWith(), + global2local_z.getGlobalWith()), comm(BoutComm::get()) { #ifdef _OPENMP openmp_ids.resize(omp_get_max_threads()); @@ -128,7 +130,7 @@ public: #if CHECK >= 2 // We could also allow false, but then we would need to ensure it // is false everywhere. - for (int x = 0; x < mesh->localNx, ++x) { + for (int x = 0; x < mesh->LocalNx; ++x) { ASSERT2(mesh->periodicY(x) == true); } #endif @@ -166,7 +168,8 @@ private: fci_comm::GlobalToLocal1D global2local_z; public: - fci_comm::XYZ2Ind xyzlocal{}; + fci_comm::XYZ2Ind xyzlocal; + fci_comm::XYZ2Ind xyzglobal; private: std::vector> toGet; @@ -177,7 +180,7 @@ private: std::vector communicate_data(const Field3D& f); }; -const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { +inline const BoutReal& GlobalField3DAccessInstance::operator[](IndG3D ind) const { auto it = gfa->mapping.find(ind.ind); ASSERT2(it != gfa->mapping.end()); return data[it->second]; From 0ecb0d96e6c5aedfafa4dd32f30aeb7ca53d5516 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Tue, 10 Mar 2026 14:36:15 -0700 Subject: [PATCH 372/461] pvode: Don't include bits/basic_string Not included in Clang distributions. Does not seem to be needed. --- src/solver/impls/pvode/pvode.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/solver/impls/pvode/pvode.cxx b/src/solver/impls/pvode/pvode.cxx index 8ccd5f134a..38901a2a57 100644 --- a/src/solver/impls/pvode/pvode.cxx +++ b/src/solver/impls/pvode/pvode.cxx @@ -47,7 +47,6 @@ #include "fmt/format.h" -#include #include #include #include From 159ce2ff3da1cf9275b3d9abce7c9b1e4893e6ad Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 11 Mar 2026 09:49:55 +0100 Subject: [PATCH 373/461] Add getProcIndex to fake_mesh Also, the indices should all start at 0 --- tests/unit/fake_mesh.hxx | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/tests/unit/fake_mesh.hxx b/tests/unit/fake_mesh.hxx index 4656ee0282..cb7509ebde 100644 --- a/tests/unit/fake_mesh.hxx +++ b/tests/unit/fake_mesh.hxx @@ -113,9 +113,13 @@ public: int getNXPE() const override { return 1; } int getNYPE() const override { return 1; } int getNZPE() const override { return 1; } - int getXProcIndex() const override { return 1; } - int getYProcIndex() const override { return 1; } - int getZProcIndex() const override { return 1; } + int getXProcIndex() const override { return 0; } + int getYProcIndex() const override { return 0; } + int getZProcIndex() const override { return 0; } + int getProcIndex([[maybe_unused]] int X, [[maybe_unused]] int Y, + [[maybe_unused]] int Z) const override { + return 0; + } bool firstX() const override { return true; } bool lastX() const override { return true; } int sendXOut(BoutReal* UNUSED(buffer), int UNUSED(size), int UNUSED(tag)) override { From 1de6329a351074036a4e7168896f1ca58723ba54 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 11 Mar 2026 09:56:55 +0100 Subject: [PATCH 374/461] Add guards for petsc related code --- src/mesh/interpolation/hermite_spline_xz.cxx | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/mesh/interpolation/hermite_spline_xz.cxx b/src/mesh/interpolation/hermite_spline_xz.cxx index 0a2c9904c7..48e7d2904e 100644 --- a/src/mesh/interpolation/hermite_spline_xz.cxx +++ b/src/mesh/interpolation/hermite_spline_xz.cxx @@ -151,11 +151,13 @@ XZHermiteSplineBase::XZHermiteSplineBase(int y_offset, Mesh } } if constexpr (imp_type == implementation_type::petsc) { +#if BOUT_HAS_PETSC petsclib = new PetscLib( &Options::root()["mesh:paralleltransform:xzinterpolation:hermitespline"]); const int m = localmesh->LocalNx * localmesh->LocalNy * localmesh->LocalNz; const int M = m * localmesh->getNXPE() * localmesh->getNYPE() * localmesh->getNZPE(); MatCreateAIJ(BoutComm::get(), m, m, M, M, 16, nullptr, 16, nullptr, &petscWeights); +#endif } if constexpr (monotonic) { gf3daccess = std::make_unique(localmesh); @@ -308,6 +310,7 @@ void XZHermiteSplineBase::calcWeights( newWeights[7][i] -= h11_x[i] * h11_z[i] / 4; newWeights[5][i] += h11_x[i] * h11_z[i] / 4; if (imp_type == implementation_type::petsc) { +#if BOUT_HAS_PETSC PetscInt idxn[1] = {conv.fromLocalToGlobal(x, y + y_offset, z)}; // output.write("debug: {:d} -> {:d}: {:d}:{:d} -> {:d}:{:d}\n", // conv.fromLocalToGlobal(x, y + y_offset, z), @@ -324,6 +327,7 @@ void XZHermiteSplineBase::calcWeights( } MatSetValues(petscWeights, 1, idxn, 4, idxm, vals, INSERT_VALUES); } +#endif } } if constexpr (monotonic) { @@ -337,12 +341,14 @@ void XZHermiteSplineBase::calcWeights( } } if constexpr (imp_type == implementation_type::petsc) { +#if BOUT_HAS_PETSC MatAssemblyBegin(petscWeights, MAT_FINAL_ASSEMBLY); MatAssemblyEnd(petscWeights, MAT_FINAL_ASSEMBLY); if (!isInit) { MatCreateVecs(petscWeights, &rhs, &result); } isInit = true; +#endif } } @@ -406,6 +412,7 @@ Field3D XZHermiteSplineBase::interpolate( } if constexpr (imp_type == implementation_type::petsc) { +#if BOUT_HAS_PETSC BoutReal* ptr = nullptr; const BoutReal* cptr = nullptr; VecGetArray(rhs, &ptr); @@ -429,6 +436,7 @@ Field3D XZHermiteSplineBase::interpolate( ASSERT2(std::isfinite(cptr[int(i)])); } VecRestoreArrayRead(result, &cptr); +#endif } if constexpr (imp_type == implementation_type::new_weights) { From dbb69599079fcf5411fce1ad913928ca766fb6bd Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 17:06:06 +0000 Subject: [PATCH 375/461] dev: Add pre-commit config --- .pre-commit-config.yaml | 62 ++++ pyproject.toml | 20 ++ uv.lock | 698 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 780 insertions(+) create mode 100644 .pre-commit-config.yaml create mode 100644 uv.lock diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml new file mode 100644 index 0000000000..a6fefc34cb --- /dev/null +++ b/.pre-commit-config.yaml @@ -0,0 +1,62 @@ +# This file requires prek rather than pre-commit +exclude: + glob: + - "tools/**/*.pro" + - "tools/**/*.m" + - "tools/**/*.[ch]" + +# See https://pre-commit.com for more information +# See https://pre-commit.com/hooks.html for more hooks +repos: +- repo: builtin + hooks: + - id: trailing-whitespace + - id: end-of-file-fixer + - id: check-added-large-files + +# This would be nice to use, but it is a significant slowdown: + +# # Sync versions of hook tools with `uv.lock` +# - repo: https://github.com/tsvikas/sync-with-uv +# rev: v0.5.0 +# hooks: +# - id: sync-with-uv + +# C++ formatting +- repo: https://github.com/pre-commit/mirrors-clang-format + rev: v22.1.0 + hooks: + - id: clang-format + types_or: [c++, c, cuda] + +# Python linting and formatting +- repo: https://github.com/astral-sh/ruff-pre-commit + rev: v0.15.5 + hooks: + # Run the linter. + - id: ruff-check + # Run the formatter. + - id: ruff-format + +# CMake formatting +- repo: https://github.com/cheshirekow/cmake-format-precommit + rev: v0.6.13 + hooks: + - id: cmake-format + additional_dependencies: [pyyaml>=5.1] + +# Various config files +- repo: https://github.com/python-jsonschema/check-jsonschema + rev: 0.37.0 + hooks: + - id: check-github-workflows + - id: check-readthedocs + - id: check-citation-file-format + - id: check-dependabot + +# Docs linting +- repo: https://github.com/sphinx-contrib/sphinx-lint + rev: v1.0.2 + hooks: + - id: sphinx-lint + files: 'manual/sphinx/.*' diff --git a/pyproject.toml b/pyproject.toml index a87e8bb674..71a67f87dd 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -4,3 +4,23 @@ requires = [] # Defined by PEP 517: build-backend = "backend" backend-path = ["tools/pylib/_boutpp_build/"] + +[dependency-groups] +dev = [ + "cmake~=4.2", + "clang-format~=22.0", + "clang-tidy~=22.0", + "clangd~=22.0", + "clangd-tidy~=1.1", + "cmakelang~=0.6", + "prek>=0.3.5", + "pyyaml~=6.0", + "ruff~=0.15", + "sphinx-lint~=1.0", + "sync-with-uv~=0.5.0", +] +maint = [ + "pygithub~=2.8", + "ruamel-yaml~=0.19", + "Unidecode~=1.3", +] diff --git a/uv.lock b/uv.lock new file mode 100644 index 0000000000..8dce73e775 --- /dev/null +++ b/uv.lock @@ -0,0 +1,698 @@ +version = 1 +revision = 3 +requires-python = ">=3.14" + +[manifest] + +[manifest.dependency-groups] +dev = [ + { name = "clang-format", specifier = "~=22.0" }, + { name = "clang-tidy", specifier = "~=22.0" }, + { name = "clangd", specifier = "~=22.0" }, + { name = "clangd-tidy", specifier = "~=1.1" }, + { name = "cmake", specifier = "~=4.2" }, + { name = "cmakelang", specifier = "~=0.6" }, + { name = "prek", specifier = ">=0.3.5" }, + { name = "pyyaml", specifier = "~=6.0" }, + { name = "ruff", specifier = "~=0.15" }, + { name = "sphinx-lint", specifier = "~=1.0" }, + { name = "sync-with-uv", specifier = "~=0.5.0" }, +] +maint = [ + { name = "pygithub", specifier = "~=2.8" }, + { name = "ruamel-yaml", specifier = "~=0.19" }, + { name = "unidecode", specifier = "~=1.3" }, +] + +[[package]] +name = "attrs" +version = "25.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6b/5c/685e6633917e101e5dcb62b9dd76946cbb57c26e133bae9e0cd36033c0a9/attrs-25.4.0.tar.gz", hash = "sha256:16d5969b87f0859ef33a48b35d55ac1be6e42ae49d5e853b597db70c35c57e11", size = 934251, upload-time = "2025-10-06T13:54:44.725Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3a/2a/7cc015f5b9f5db42b7d48157e23356022889fc354a2813c15934b7cb5c0e/attrs-25.4.0-py3-none-any.whl", hash = "sha256:adcf7e2a1fb3b36ac48d97835bb6d8ade15b8dcce26aba8bf1d14847b57a3373", size = 67615, upload-time = "2025-10-06T13:54:43.17Z" }, +] + +[[package]] +name = "cattrs" +version = "26.1.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/a0/ec/ba18945e7d6e55a58364d9fb2e46049c1c2998b3d805f19b703f14e81057/cattrs-26.1.0.tar.gz", hash = "sha256:fa239e0f0ec0715ba34852ce813986dfed1e12117e209b816ab87401271cdd40", size = 495672, upload-time = "2026-02-18T22:15:19.406Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/56/60547f7801b97c67e97491dc3d9ade9fbccbd0325058fd3dfcb2f5d98d90/cattrs-26.1.0-py3-none-any.whl", hash = "sha256:d1e0804c42639494d469d08d4f26d6b9de9b8ab26b446db7b5f8c2e97f7c3096", size = 73054, upload-time = "2026-02-18T22:15:17.958Z" }, +] + +[[package]] +name = "certifi" +version = "2026.2.25" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/af/2d/7bf41579a8986e348fa033a31cdd0e4121114f6bce2457e8876010b092dd/certifi-2026.2.25.tar.gz", hash = "sha256:e887ab5cee78ea814d3472169153c2d12cd43b14bd03329a39a9c6e2e80bfba7", size = 155029, upload-time = "2026-02-25T02:54:17.342Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9a/3c/c17fb3ca2d9c3acff52e30b309f538586f9f5b9c9cf454f3845fc9af4881/certifi-2026.2.25-py3-none-any.whl", hash = "sha256:027692e4402ad994f1c42e52a4997a9763c646b73e4096e4d5d6db8af1d6f0fa", size = 153684, upload-time = "2026-02-25T02:54:15.766Z" }, +] + +[[package]] +name = "cffi" +version = "2.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pycparser", marker = "implementation_name != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/eb/56/b1ba7935a17738ae8453301356628e8147c79dbb825bcbc73dc7401f9846/cffi-2.0.0.tar.gz", hash = "sha256:44d1b5909021139fe36001ae048dbdde8214afa20200eda0f64c068cac5d5529", size = 523588, upload-time = "2025-09-08T23:24:04.541Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/92/c4/3ce07396253a83250ee98564f8d7e9789fab8e58858f35d07a9a2c78de9f/cffi-2.0.0-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:fc33c5141b55ed366cfaad382df24fe7dcbc686de5be719b207bb248e3053dc5", size = 185320, upload-time = "2025-09-08T23:23:18.087Z" }, + { url = "https://files.pythonhosted.org/packages/59/dd/27e9fa567a23931c838c6b02d0764611c62290062a6d4e8ff7863daf9730/cffi-2.0.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:c654de545946e0db659b3400168c9ad31b5d29593291482c43e3564effbcee13", size = 181487, upload-time = "2025-09-08T23:23:19.622Z" }, + { url = "https://files.pythonhosted.org/packages/d6/43/0e822876f87ea8a4ef95442c3d766a06a51fc5298823f884ef87aaad168c/cffi-2.0.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:24b6f81f1983e6df8db3adc38562c83f7d4a0c36162885ec7f7b77c7dcbec97b", size = 220049, upload-time = "2025-09-08T23:23:20.853Z" }, + { url = "https://files.pythonhosted.org/packages/b4/89/76799151d9c2d2d1ead63c2429da9ea9d7aac304603de0c6e8764e6e8e70/cffi-2.0.0-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:12873ca6cb9b0f0d3a0da705d6086fe911591737a59f28b7936bdfed27c0d47c", size = 207793, upload-time = "2025-09-08T23:23:22.08Z" }, + { url = "https://files.pythonhosted.org/packages/bb/dd/3465b14bb9e24ee24cb88c9e3730f6de63111fffe513492bf8c808a3547e/cffi-2.0.0-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:d9b97165e8aed9272a6bb17c01e3cc5871a594a446ebedc996e2397a1c1ea8ef", size = 206300, upload-time = "2025-09-08T23:23:23.314Z" }, + { url = "https://files.pythonhosted.org/packages/47/d9/d83e293854571c877a92da46fdec39158f8d7e68da75bf73581225d28e90/cffi-2.0.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:afb8db5439b81cf9c9d0c80404b60c3cc9c3add93e114dcae767f1477cb53775", size = 219244, upload-time = "2025-09-08T23:23:24.541Z" }, + { url = "https://files.pythonhosted.org/packages/2b/0f/1f177e3683aead2bb00f7679a16451d302c436b5cbf2505f0ea8146ef59e/cffi-2.0.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:737fe7d37e1a1bffe70bd5754ea763a62a066dc5913ca57e957824b72a85e205", size = 222828, upload-time = "2025-09-08T23:23:26.143Z" }, + { url = "https://files.pythonhosted.org/packages/c6/0f/cafacebd4b040e3119dcb32fed8bdef8dfe94da653155f9d0b9dc660166e/cffi-2.0.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:38100abb9d1b1435bc4cc340bb4489635dc2f0da7456590877030c9b3d40b0c1", size = 220926, upload-time = "2025-09-08T23:23:27.873Z" }, + { url = "https://files.pythonhosted.org/packages/3e/aa/df335faa45b395396fcbc03de2dfcab242cd61a9900e914fe682a59170b1/cffi-2.0.0-cp314-cp314-win32.whl", hash = "sha256:087067fa8953339c723661eda6b54bc98c5625757ea62e95eb4898ad5e776e9f", size = 175328, upload-time = "2025-09-08T23:23:44.61Z" }, + { url = "https://files.pythonhosted.org/packages/bb/92/882c2d30831744296ce713f0feb4c1cd30f346ef747b530b5318715cc367/cffi-2.0.0-cp314-cp314-win_amd64.whl", hash = "sha256:203a48d1fb583fc7d78a4c6655692963b860a417c0528492a6bc21f1aaefab25", size = 185650, upload-time = "2025-09-08T23:23:45.848Z" }, + { url = "https://files.pythonhosted.org/packages/9f/2c/98ece204b9d35a7366b5b2c6539c350313ca13932143e79dc133ba757104/cffi-2.0.0-cp314-cp314-win_arm64.whl", hash = "sha256:dbd5c7a25a7cb98f5ca55d258b103a2054f859a46ae11aaf23134f9cc0d356ad", size = 180687, upload-time = "2025-09-08T23:23:47.105Z" }, + { url = "https://files.pythonhosted.org/packages/3e/61/c768e4d548bfa607abcda77423448df8c471f25dbe64fb2ef6d555eae006/cffi-2.0.0-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:9a67fc9e8eb39039280526379fb3a70023d77caec1852002b4da7e8b270c4dd9", size = 188773, upload-time = "2025-09-08T23:23:29.347Z" }, + { url = "https://files.pythonhosted.org/packages/2c/ea/5f76bce7cf6fcd0ab1a1058b5af899bfbef198bea4d5686da88471ea0336/cffi-2.0.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7a66c7204d8869299919db4d5069a82f1561581af12b11b3c9f48c584eb8743d", size = 185013, upload-time = "2025-09-08T23:23:30.63Z" }, + { url = "https://files.pythonhosted.org/packages/be/b4/c56878d0d1755cf9caa54ba71e5d049479c52f9e4afc230f06822162ab2f/cffi-2.0.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7cc09976e8b56f8cebd752f7113ad07752461f48a58cbba644139015ac24954c", size = 221593, upload-time = "2025-09-08T23:23:31.91Z" }, + { url = "https://files.pythonhosted.org/packages/e0/0d/eb704606dfe8033e7128df5e90fee946bbcb64a04fcdaa97321309004000/cffi-2.0.0-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:92b68146a71df78564e4ef48af17551a5ddd142e5190cdf2c5624d0c3ff5b2e8", size = 209354, upload-time = "2025-09-08T23:23:33.214Z" }, + { url = "https://files.pythonhosted.org/packages/d8/19/3c435d727b368ca475fb8742ab97c9cb13a0de600ce86f62eab7fa3eea60/cffi-2.0.0-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:b1e74d11748e7e98e2f426ab176d4ed720a64412b6a15054378afdb71e0f37dc", size = 208480, upload-time = "2025-09-08T23:23:34.495Z" }, + { url = "https://files.pythonhosted.org/packages/d0/44/681604464ed9541673e486521497406fadcc15b5217c3e326b061696899a/cffi-2.0.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:28a3a209b96630bca57cce802da70c266eb08c6e97e5afd61a75611ee6c64592", size = 221584, upload-time = "2025-09-08T23:23:36.096Z" }, + { url = "https://files.pythonhosted.org/packages/25/8e/342a504ff018a2825d395d44d63a767dd8ebc927ebda557fecdaca3ac33a/cffi-2.0.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:7553fb2090d71822f02c629afe6042c299edf91ba1bf94951165613553984512", size = 224443, upload-time = "2025-09-08T23:23:37.328Z" }, + { url = "https://files.pythonhosted.org/packages/e1/5e/b666bacbbc60fbf415ba9988324a132c9a7a0448a9a8f125074671c0f2c3/cffi-2.0.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:6c6c373cfc5c83a975506110d17457138c8c63016b563cc9ed6e056a82f13ce4", size = 223437, upload-time = "2025-09-08T23:23:38.945Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/ec1a60bd1a10daa292d3cd6bb0b359a81607154fb8165f3ec95fe003b85c/cffi-2.0.0-cp314-cp314t-win32.whl", hash = "sha256:1fc9ea04857caf665289b7a75923f2c6ed559b8298a1b8c49e59f7dd95c8481e", size = 180487, upload-time = "2025-09-08T23:23:40.423Z" }, + { url = "https://files.pythonhosted.org/packages/bf/41/4c1168c74fac325c0c8156f04b6749c8b6a8f405bbf91413ba088359f60d/cffi-2.0.0-cp314-cp314t-win_amd64.whl", hash = "sha256:d68b6cef7827e8641e8ef16f4494edda8b36104d79773a334beaa1e3521430f6", size = 191726, upload-time = "2025-09-08T23:23:41.742Z" }, + { url = "https://files.pythonhosted.org/packages/ae/3a/dbeec9d1ee0844c679f6bb5d6ad4e9f198b1224f4e7a32825f47f6192b0c/cffi-2.0.0-cp314-cp314t-win_arm64.whl", hash = "sha256:0a1527a803f0a659de1af2e1fd700213caba79377e27e4693648c2923da066f9", size = 184195, upload-time = "2025-09-08T23:23:43.004Z" }, +] + +[[package]] +name = "charset-normalizer" +version = "3.4.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1d/35/02daf95b9cd686320bb622eb148792655c9412dbb9b67abb5694e5910a24/charset_normalizer-3.4.5.tar.gz", hash = "sha256:95adae7b6c42a6c5b5b559b1a99149f090a57128155daeea91732c8d970d8644", size = 134804, upload-time = "2026-03-06T06:03:19.46Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/43/be/0f0fd9bb4a7fa4fb5067fb7d9ac693d4e928d306f80a0d02bde43a7c4aee/charset_normalizer-3.4.5-cp314-cp314-macosx_10_15_universal2.whl", hash = "sha256:8197abe5ca1ffb7d91e78360f915eef5addff270f8a71c1fc5be24a56f3e4873", size = 280232, upload-time = "2026-03-06T06:02:01.508Z" }, + { url = "https://files.pythonhosted.org/packages/28/02/983b5445e4bef49cd8c9da73a8e029f0825f39b74a06d201bfaa2e55142a/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:a2aecdb364b8a1802afdc7f9327d55dad5366bc97d8502d0f5854e50712dbc5f", size = 189688, upload-time = "2026-03-06T06:02:02.857Z" }, + { url = "https://files.pythonhosted.org/packages/d0/88/152745c5166437687028027dc080e2daed6fe11cfa95a22f4602591c42db/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:a66aa5022bf81ab4b1bebfb009db4fd68e0c6d4307a1ce5ef6a26e5878dfc9e4", size = 206833, upload-time = "2026-03-06T06:02:05.127Z" }, + { url = "https://files.pythonhosted.org/packages/cb/0f/ebc15c8b02af2f19be9678d6eed115feeeccc45ce1f4b098d986c13e8769/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:d77f97e515688bd615c1d1f795d540f32542d514242067adcb8ef532504cb9ee", size = 202879, upload-time = "2026-03-06T06:02:06.446Z" }, + { url = "https://files.pythonhosted.org/packages/38/9c/71336bff6934418dc8d1e8a1644176ac9088068bc571da612767619c97b3/charset_normalizer-3.4.5-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:01a1ed54b953303ca7e310fafe0fe347aab348bd81834a0bcd602eb538f89d66", size = 195764, upload-time = "2026-03-06T06:02:08.763Z" }, + { url = "https://files.pythonhosted.org/packages/b7/95/ce92fde4f98615661871bc282a856cf9b8a15f686ba0af012984660d480b/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_armv7l.whl", hash = "sha256:b2d37d78297b39a9eb9eb92c0f6df98c706467282055419df141389b23f93362", size = 183728, upload-time = "2026-03-06T06:02:10.137Z" }, + { url = "https://files.pythonhosted.org/packages/1c/e7/f5b4588d94e747ce45ae680f0f242bc2d98dbd4eccfab73e6160b6893893/charset_normalizer-3.4.5-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:e71bbb595973622b817c042bd943c3f3667e9c9983ce3d205f973f486fec98a7", size = 192937, upload-time = "2026-03-06T06:02:11.663Z" }, + { url = "https://files.pythonhosted.org/packages/f9/29/9d94ed6b929bf9f48bf6ede6e7474576499f07c4c5e878fb186083622716/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:4cd966c2559f501c6fd69294d082c2934c8dd4719deb32c22961a5ac6db0df1d", size = 192040, upload-time = "2026-03-06T06:02:13.489Z" }, + { url = "https://files.pythonhosted.org/packages/15/d2/1a093a1cf827957f9445f2fe7298bcc16f8fc5e05c1ed2ad1af0b239035e/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_armv7l.whl", hash = "sha256:d5e52d127045d6ae01a1e821acfad2f3a1866c54d0e837828538fabe8d9d1bd6", size = 184107, upload-time = "2026-03-06T06:02:14.83Z" }, + { url = "https://files.pythonhosted.org/packages/0f/7d/82068ce16bd36135df7b97f6333c5d808b94e01d4599a682e2337ed5fd14/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:30a2b1a48478c3428d047ed9690d57c23038dac838a87ad624c85c0a78ebeb39", size = 208310, upload-time = "2026-03-06T06:02:16.165Z" }, + { url = "https://files.pythonhosted.org/packages/84/4e/4dfb52307bb6af4a5c9e73e482d171b81d36f522b21ccd28a49656baa680/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:d8ed79b8f6372ca4254955005830fd61c1ccdd8c0fac6603e2c145c61dd95db6", size = 192918, upload-time = "2026-03-06T06:02:18.144Z" }, + { url = "https://files.pythonhosted.org/packages/08/a4/159ff7da662cf7201502ca89980b8f06acf3e887b278956646a8aeb178ab/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:c5af897b45fa606b12464ccbe0014bbf8c09191e0a66aab6aa9d5cf6e77e0c94", size = 204615, upload-time = "2026-03-06T06:02:19.821Z" }, + { url = "https://files.pythonhosted.org/packages/d6/62/0dd6172203cb6b429ffffc9935001fde42e5250d57f07b0c28c6046deb6b/charset_normalizer-3.4.5-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:1088345bcc93c58d8d8f3d783eca4a6e7a7752bbff26c3eee7e73c597c191c2e", size = 197784, upload-time = "2026-03-06T06:02:21.86Z" }, + { url = "https://files.pythonhosted.org/packages/c7/5e/1aab5cb737039b9c59e63627dc8bbc0d02562a14f831cc450e5f91d84ce1/charset_normalizer-3.4.5-cp314-cp314-win32.whl", hash = "sha256:ee57b926940ba00bca7ba7041e665cc956e55ef482f851b9b65acb20d867e7a2", size = 133009, upload-time = "2026-03-06T06:02:23.289Z" }, + { url = "https://files.pythonhosted.org/packages/40/65/e7c6c77d7aaa4c0d7974f2e403e17f0ed2cb0fc135f77d686b916bf1eead/charset_normalizer-3.4.5-cp314-cp314-win_amd64.whl", hash = "sha256:4481e6da1830c8a1cc0b746b47f603b653dadb690bcd851d039ffaefe70533aa", size = 143511, upload-time = "2026-03-06T06:02:26.195Z" }, + { url = "https://files.pythonhosted.org/packages/ba/91/52b0841c71f152f563b8e072896c14e3d83b195c188b338d3cc2e582d1d4/charset_normalizer-3.4.5-cp314-cp314-win_arm64.whl", hash = "sha256:97ab7787092eb9b50fb47fa04f24c75b768a606af1bcba1957f07f128a7219e4", size = 133775, upload-time = "2026-03-06T06:02:27.473Z" }, + { url = "https://files.pythonhosted.org/packages/c5/60/3a621758945513adfd4db86827a5bafcc615f913dbd0b4c2ed64a65731be/charset_normalizer-3.4.5-py3-none-any.whl", hash = "sha256:9db5e3fcdcee89a78c04dffb3fe33c79f77bd741a624946db2591c81b2fc85b0", size = 55455, upload-time = "2026-03-06T06:03:17.827Z" }, +] + +[[package]] +name = "clang-format" +version = "22.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/e5/84/8ab81082e59115e00a162365b50495bc608b49bbaffed479c44b8abd8dde/clang_format-22.1.0.tar.gz", hash = "sha256:41c535251ebc6f1ff824ca3d92f36ec7f784eb7c355b3cd6fc3963f3d72ebca8", size = 11508, upload-time = "2026-02-24T22:12:09.578Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d7/c2/db042a9f7a9497fba214935551d8fef098368147be39d9c23161bcb24eb0/clang_format-22.1.0-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:b11aaeb35e19948b290335f65b56e7e8d057137ac1094156584de4eec6b92c54", size = 1492625, upload-time = "2026-02-24T22:11:38.605Z" }, + { url = "https://files.pythonhosted.org/packages/35/9a/0a8634a452d6bb5609854c6cc4fc2ad417ba76c7d1dd5ca6bec66a464f8c/clang_format-22.1.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:2dbbfc5f275121f067c76459de16120fb70b483927d7c93320eb93238b9b6ae4", size = 1478843, upload-time = "2026-02-24T22:11:40.525Z" }, + { url = "https://files.pythonhosted.org/packages/17/6e/722918070bf3d6ce3afdc4972a74bda4c4645f16f8b2d8246ff0818fe9d1/clang_format-22.1.0-py2.py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:97d12f9785f7b909ef737da3de95471d1915462908004d041869f41129de467d", size = 1757448, upload-time = "2026-02-24T22:11:42.314Z" }, + { url = "https://files.pythonhosted.org/packages/1f/e0/68257bff84d0fe8d855d8f255b1bc192cd21db702b551bc002100a46a211/clang_format-22.1.0-py2.py3-none-manylinux_2_26_i686.manylinux_2_28_i686.whl", hash = "sha256:4695288f19a2374e22c09eb615d2874b697dd7fc11c0efe4eae12cfe739083c1", size = 1888405, upload-time = "2026-02-24T22:11:44.242Z" }, + { url = "https://files.pythonhosted.org/packages/5e/29/393907895dbaffe6cbf527890e04d51e80c750b359e1d97cc8c2dfa815d6/clang_format-22.1.0-py2.py3-none-manylinux_2_26_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:91ddd90a5745320e16ab06f6dd279bfe3b304dbe603980278a1035bcb2d9667d", size = 2070418, upload-time = "2026-02-24T22:11:46.229Z" }, + { url = "https://files.pythonhosted.org/packages/fe/d4/908b17c926eb3a90109dfc4f5195b438f9d2e4c1da3de0b955eb93c14e43/clang_format-22.1.0-py2.py3-none-manylinux_2_26_s390x.manylinux_2_28_s390x.whl", hash = "sha256:50b85b2ef55474676465ea3460e63437a9dd0aa2b1e3f9e485daef6508dee086", size = 2101315, upload-time = "2026-02-24T22:11:48.287Z" }, + { url = "https://files.pythonhosted.org/packages/ba/f8/550085c7e0b64178bf409234afd2a789195b320e8a16d4ba060cdac4a6d0/clang_format-22.1.0-py2.py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:ae07624f13f404b4405a2a20bfaffa33ada5602dc75dea6f9c9ab5ecfe6e07b3", size = 1840809, upload-time = "2026-02-24T22:11:50.176Z" }, + { url = "https://files.pythonhosted.org/packages/9d/be/a3e8ba3365c21135673566bb03b7d78f1fb95a4a2511ddca2c8794239c3b/clang_format-22.1.0-py2.py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:5a7f76065a1e80e0397d64d2def12e3c7d5854a13c1108283e65ad7dc5673653", size = 1684794, upload-time = "2026-02-24T22:11:51.615Z" }, + { url = "https://files.pythonhosted.org/packages/55/4c/b3682ada8332959546adc92f8222b0a9fa560e0fa4d591db25fa7a06bb2e/clang_format-22.1.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:f136fee157901366c092ee9e629aa51b337e3c5c45fa7c15522fbf3aee3c8c53", size = 2735158, upload-time = "2026-02-24T22:11:53.406Z" }, + { url = "https://files.pythonhosted.org/packages/7b/a5/14f60ee2a0f88bc59b381472c271eba12877a65dbb4ad5bc425f9a0cb3df/clang_format-22.1.0-py2.py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:44aa67e948a6f03d62f5b4076041033f9fdbdb7b3a48df3906d7cceb73a4a61e", size = 2514611, upload-time = "2026-02-24T22:11:55.464Z" }, + { url = "https://files.pythonhosted.org/packages/c7/97/2bad239dd072a37f3fc71e1bc4950fafffc3f3fac6e8e7ff1bb83e510c0a/clang_format-22.1.0-py2.py3-none-musllinux_1_2_i686.whl", hash = "sha256:e6f4700aeb76b2057de7905bdef988e52a51270d66dc05daadb5c17680395437", size = 2985306, upload-time = "2026-02-24T22:11:57.409Z" }, + { url = "https://files.pythonhosted.org/packages/1a/d2/d6dcd1e0400fa1b8a7d784f6382291f055c57264daee1502549942f27d50/clang_format-22.1.0-py2.py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:8ab962542105ee4e3aabee3f8ef21241f2bb922b4353d5defb76df0363e6eb92", size = 3119154, upload-time = "2026-02-24T22:11:59.121Z" }, + { url = "https://files.pythonhosted.org/packages/be/c3/65de01a192d7237c36e5b73e0ff4a5f81e4bd7991669a38955fc50f93bbd/clang_format-22.1.0-py2.py3-none-musllinux_1_2_s390x.whl", hash = "sha256:05950eeb29d7181075b9000b86c2fc787cf706a23671cc9ee6fbc28e3ac6b3e2", size = 3217108, upload-time = "2026-02-24T22:12:00.863Z" }, + { url = "https://files.pythonhosted.org/packages/c1/8d/8d41438ef6fda40058cd9b85cbbd4fabd3bb1e67f5324b2a234bdf70e72e/clang_format-22.1.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5d7cc77bf7454b8c9f308e7205adf734c99a0dcb5114de76dedebc53c4a84eb", size = 2848307, upload-time = "2026-02-24T22:12:02.793Z" }, + { url = "https://files.pythonhosted.org/packages/19/e0/74ca2c38f2203a729e8f4ff2f3ffeec84216b294f65ce55f7da416d238cf/clang_format-22.1.0-py2.py3-none-win32.whl", hash = "sha256:beeae9d7fc3490cce6ffac0eb5643d508991678cf5b8217ca9a90a16b83a5a1d", size = 1299281, upload-time = "2026-02-24T22:12:04.446Z" }, + { url = "https://files.pythonhosted.org/packages/60/f2/4391a14b872b7c52b68c09bb34f8e22aa068ea93dbbdd1be55962ac58bd2/clang_format-22.1.0-py2.py3-none-win_amd64.whl", hash = "sha256:962c7b1c9362ac028e664ba8b1c4f0d71eab13ceade80f19f84ed8a66d126db4", size = 1460209, upload-time = "2026-02-24T22:12:06.431Z" }, + { url = "https://files.pythonhosted.org/packages/dd/c3/4573ef27c004b30fabb23a82c79eb09d76ac7d6b94422cc767483fa42c72/clang_format-22.1.0-py2.py3-none-win_arm64.whl", hash = "sha256:be5e43f2263ab6d6f9d76acaab39976b210bbdcf658623386560d5aa8f8deeda", size = 1344909, upload-time = "2026-02-24T22:12:08.321Z" }, +] + +[[package]] +name = "clang-tidy" +version = "22.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/34/72/5b7e8c4eacff221fd00483678680b23ffd5e1928a9a5808876dd9b4eb02d/clang_tidy-22.1.0.tar.gz", hash = "sha256:2ae464d2fd5ed83784e68eecb54ada56128b726f15a0285b95e95bb47f386918", size = 11348, upload-time = "2026-03-06T12:55:57.76Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/22/44/0c007982000222f59cc257228818a9ab52990349bb211bf2eacb0a34d06a/clang_tidy-22.1.0-py2.py3-none-macosx_10_9_x86_64.whl", hash = "sha256:6caa36a25ad26af6bcd014698c660484c7b6514ac386ca65e265c710e0203845", size = 30220695, upload-time = "2026-03-06T12:55:18.811Z" }, + { url = "https://files.pythonhosted.org/packages/8b/52/73f5a5cb3004373a2df0a87b2128620c9a22782eee2b9fefd2ded1ee809f/clang_tidy-22.1.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:89752596b4e868877134b21f266e43b312fe354b5bbc464e6e8147aa1f409317", size = 29312798, upload-time = "2026-03-06T12:55:22.995Z" }, + { url = "https://files.pythonhosted.org/packages/bd/73/2551a1759935c075607f64a242926afd21f3a6e1ed2918b4b86d80ae5a22/clang_tidy-22.1.0-py2.py3-none-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:70b819831b16b2a87a266526d9894b88139368dbe5f765536572d730f0926b46", size = 40361554, upload-time = "2026-03-06T12:55:26.352Z" }, + { url = "https://files.pythonhosted.org/packages/ad/f7/06fc2d0b3dd3189eb8c589891c79af71be558aa69215d17b791d0aad03de/clang_tidy-22.1.0-py2.py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:e1d4f56b15c35f844fce44b2ba51e845126cc00bbfbb1dc15d5a711e2ec5b9af", size = 42210624, upload-time = "2026-03-06T12:55:30.456Z" }, + { url = "https://files.pythonhosted.org/packages/20/5a/c0434905d6b7b1c01b6d519da3cf09dcb8d15ad5004c58e064ae0924aa1a/clang_tidy-22.1.0-py2.py3-none-manylinux_2_28_i686.whl", hash = "sha256:3b4f23130aa4f1efe64417ae98527414c9b3c790bcebe06e77933463e35e3a26", size = 47699430, upload-time = "2026-03-06T12:55:34.439Z" }, + { url = "https://files.pythonhosted.org/packages/83/c4/15c6e452e1924767096b27ba5d64abc5e2a20eabfd57c9eaf47814790c6d/clang_tidy-22.1.0-py2.py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:5422e01207382964c04cb843c44675204074d0703b9f045d300b7fa13373ee78", size = 38806060, upload-time = "2026-03-06T12:55:38.05Z" }, + { url = "https://files.pythonhosted.org/packages/7e/3a/67a6a11ea878054028695f430c6c146af6e9974ed1fb081b55006818f4a9/clang_tidy-22.1.0-py2.py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:43de3f3533dc9372af86435ead7b7060b0e9c1191968873137ed359c61034b0f", size = 39643680, upload-time = "2026-03-06T12:55:41.955Z" }, + { url = "https://files.pythonhosted.org/packages/cb/b6/219e48b2aa4833a01f050b8d316e6a8ba1e2f0fc41133c9dd304f000f538/clang_tidy-22.1.0-py2.py3-none-musllinux_1_2_i686.whl", hash = "sha256:1a1d6a3e6d066b9ef9540f17ff9753deeead6f3189e632029f8cd2591426583f", size = 49881664, upload-time = "2026-03-06T12:55:46.008Z" }, + { url = "https://files.pythonhosted.org/packages/92/b6/350a9ed450bb1a51b2201ebc2909c34020985620c56452afd71d6d8cb341/clang_tidy-22.1.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:8e7fee85f434835a47e40c7edb6bd0e335bae25be0ac3110658d05e891786681", size = 44841561, upload-time = "2026-03-06T12:55:49.473Z" }, + { url = "https://files.pythonhosted.org/packages/08/2c/b5c0b970bfd236aa66bbfe20c690bfaa1efc64b68110f97eb4a3b9479309/clang_tidy-22.1.0-py2.py3-none-win32.whl", hash = "sha256:17f9ec2b6445814b49f9a809992c757c0d908e7dcdf3a81586c3dd939aaa413b", size = 22234624, upload-time = "2026-03-06T12:55:52.604Z" }, + { url = "https://files.pythonhosted.org/packages/ff/46/a65f1348aa935037ec4202d331b31e1c775436204ac6d116d25d5f491752/clang_tidy-22.1.0-py2.py3-none-win_amd64.whl", hash = "sha256:98ba65f6a285fbda1cd03fe0deefe4f7faea0e762856f8aeeff50a1069b5492a", size = 25141296, upload-time = "2026-03-06T12:55:55.619Z" }, +] + +[[package]] +name = "clangd" +version = "22.1.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5b/36/1e80860748b610e21335d0174d0f7e0c62ee46730886b57ebb59b498cbf0/clangd-22.1.0.tar.gz", hash = "sha256:6ae2eabbd0d642f6f3b84366767372cca656c03d40c2b9ccda656fa65d50d25a", size = 16179, upload-time = "2026-02-25T20:18:12.235Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/3f/9d/a75f07d0230c9d98f136aaaea16489f31f325056bbd2cadab782c3c47e1d/clangd-22.1.0-py2.py3-none-macosx_10_13_x86_64.whl", hash = "sha256:747ed0937e6eb25b77e77ea4bcad60e499420422ec69171642131f17b49c06f4", size = 18971717, upload-time = "2026-02-25T20:17:51.09Z" }, + { url = "https://files.pythonhosted.org/packages/c0/ae/58e7f1b7b110699f05e58bd06f20bfe97f8df0a740a6595b40a26358d7fe/clangd-22.1.0-py2.py3-none-macosx_11_0_arm64.whl", hash = "sha256:15b92a254f6f407c8ebfeb29e6bb3f3a346a84d7a59e5f8ad40b5918b209360d", size = 18260953, upload-time = "2026-02-25T20:17:53.971Z" }, + { url = "https://files.pythonhosted.org/packages/bd/85/d7b01acb9df5e056954e7b8ee616fb04d7802c0ec6739cf818e4ce4c784d/clangd-22.1.0-py2.py3-none-manylinux_2_27_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6c4328129a80da8a57aac191615c7618e00815004d72dc39b19e813774312d64", size = 24325995, upload-time = "2026-02-25T20:17:56.676Z" }, + { url = "https://files.pythonhosted.org/packages/61/02/6768490100cd078a2515694858981afcf8ede1816abc711fb6b94506ad8e/clangd-22.1.0-py2.py3-none-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:25bc1f5d71233500f9c5ba0ea115f7ba0f29fc4cbd9e6f96104ad0f7ca438cc5", size = 25649484, upload-time = "2026-02-25T20:17:59.566Z" }, + { url = "https://files.pythonhosted.org/packages/d7/50/52fd1f5e65176733fdbeb2080295cbecd2c016cc00daba9ab7efee5d1afd/clangd-22.1.0-py2.py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:4fd0afc780777ff64a12122cd62f836ed89939e58783328b1c5cafadd88d3ec9", size = 26759821, upload-time = "2026-02-25T20:18:02.265Z" }, + { url = "https://files.pythonhosted.org/packages/98/3c/aedf0e27c5fc77d13d852673ab118fd1bcfb74400e5c204bd79b677334c2/clangd-22.1.0-py2.py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:04fc113f599bb1a9c5cf53534a94351359eb4bd6cac676b93791905f81c7edae", size = 27840087, upload-time = "2026-02-25T20:18:04.916Z" }, + { url = "https://files.pythonhosted.org/packages/87/b3/6893440d42ff8ea0314724dbab54536eccdd3ca9e172b9db090db933d85f/clangd-22.1.0-py2.py3-none-win_amd64.whl", hash = "sha256:537a21e1f34b982f9ed44ec05a9ce448b5bc73bf5fb4702737b1c74ce03bb153", size = 18853249, upload-time = "2026-02-25T20:18:07.76Z" }, + { url = "https://files.pythonhosted.org/packages/8f/20/6ca813d7849698315a7fbc01318e2dda0285e1f9017033b65e570493fd45/clangd-22.1.0-py2.py3-none-win_arm64.whl", hash = "sha256:2f7401996eaf42dcd2d0bebc99d2d146a437e4c14e3d9fbb0685033284413140", size = 17033245, upload-time = "2026-02-25T20:18:10.246Z" }, +] + +[[package]] +name = "clangd-tidy" +version = "1.1.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "cattrs" }, + { name = "typing-extensions" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/41/34/9ed0852f40ddf0a4aaf4cad23756064ad48141e12dff178d49007194f432/clangd_tidy-1.1.1.tar.gz", hash = "sha256:d470b46a9ed6a24a0202588d23e1f892d8f15db3572ed6690c0679439795a1c8", size = 23898, upload-time = "2026-02-09T03:58:09.883Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/e8/95/33a9adb7bf48f8863e278aa3efae3d1aca9dad8a7038f9fa477ec9d1c02e/clangd_tidy-1.1.1-py3-none-any.whl", hash = "sha256:305883501ead9fe20d5db39a5de5a18646c886bee37d2c88bee7a52e898c6c4a", size = 21759, upload-time = "2026-02-09T03:58:08.37Z" }, +] + +[[package]] +name = "cmake" +version = "4.2.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/3d/1d/f917d89175a54938837f44f7672bc2bc24d1f6fc85c7f787b8d29c1017d2/cmake-4.2.3.tar.gz", hash = "sha256:7a6bc333453c9f7614c7a6e664950d309a5e1f89fa98512d537d4fde27eb8fae", size = 36957, upload-time = "2026-02-28T17:49:18.848Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/80/5f/965d2caf7b05e15853974eb36cdc3a8029c09bac886f7448d3f6ad4a8c5d/cmake-4.2.3-py3-none-macosx_10_10_universal2.whl", hash = "sha256:8604578dd087631b1c829315e78e6c81d9549708df2085c89722d5be6174a71f", size = 51591021, upload-time = "2026-02-28T17:48:21.225Z" }, + { url = "https://files.pythonhosted.org/packages/29/92/c8895ffc12c5241111282340c56450707dd8d2f8d1f26c715087e21e3b01/cmake-4.2.3-py3-none-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:a7e63d254ef3df90299779f5b41bf84cef02fa864255c567356089ea7c382c65", size = 29007127, upload-time = "2026-02-28T17:48:24.701Z" }, + { url = "https://files.pythonhosted.org/packages/ee/2f/f8438b417275d993d457d99f031fbc104bdda879ec7bdf3c6a78b8af05f7/cmake-4.2.3-py3-none-manylinux2014_i686.manylinux_2_17_i686.whl", hash = "sha256:56e88a69c95e6defafc8e68ba8b99ca9c4dd6e4c97e7f8039283fe263f1de3c9", size = 30064565, upload-time = "2026-02-28T17:48:27.705Z" }, + { url = "https://files.pythonhosted.org/packages/ee/c5/6a2e1b69d58422ddb6370e5d1e5574218dafe200c61a96add1c4cbe5640e/cmake-4.2.3-py3-none-manylinux2014_ppc64le.manylinux_2_17_ppc64le.whl", hash = "sha256:b243937103331a27e8fae4b9a72b4a852c281fb1339e76eef9e9915d4536c7c9", size = 29837850, upload-time = "2026-02-28T17:48:30.749Z" }, + { url = "https://files.pythonhosted.org/packages/a4/1c/9445c89cfcfbafe1c61a6a1c38072ddcb8413397a736db3bbe8c01f015cc/cmake-4.2.3-py3-none-manylinux2014_s390x.manylinux_2_17_s390x.whl", hash = "sha256:c53c2a314a3723f0653c0f90aacca68267f0db003ca596bb2aa65e7fdafa31e2", size = 27749297, upload-time = "2026-02-28T17:48:36.717Z" }, + { url = "https://files.pythonhosted.org/packages/a7/c2/7ecd2aec927262ff2898d371f44ccb15884b6172407326e9388df07f9642/cmake-4.2.3-py3-none-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:8e91b381aaea3c47110583dccc52f4562333d1accdbb806939f953c16e74ec0a", size = 28904315, upload-time = "2026-02-28T17:48:39.835Z" }, + { url = "https://files.pythonhosted.org/packages/0f/10/b41652c2ce1a33102386f6d65c22192284ed780de2d8ab3f416d795d928b/cmake-4.2.3-py3-none-manylinux_2_31_armv7l.whl", hash = "sha256:76eae39cd8855e80a2b485686f3539c39cba5c7eae49c4b634a15638d4edf39f", size = 26086034, upload-time = "2026-02-28T17:48:42.696Z" }, + { url = "https://files.pythonhosted.org/packages/d8/2e/d69fd449414b5bdc551ede33e8ba41fd293458865bf9d4d6745fe8fe4e5a/cmake-4.2.3-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:e3dfbaeffac5848dce60b62a93eecd96b7a3eb0af6d874efc4ec0edb72ec7a24", size = 26203962, upload-time = "2026-02-28T17:48:45.897Z" }, + { url = "https://files.pythonhosted.org/packages/1b/cd/6b703e911069bc9d099097bf9d1cf5f61b627913c1dace890718484218c9/cmake-4.2.3-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:3a21c36a64f52737e37dbeda0ce76391156139d546d995ed0992c3d4bc306636", size = 37881542, upload-time = "2026-02-28T17:48:48.843Z" }, + { url = "https://files.pythonhosted.org/packages/6b/15/4949cb479231bd6dcf204ffff4b5775c2a471754e73b8f9365ba17a6aa49/cmake-4.2.3-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:66dff80bc6c955592861f662abebc50ddc4d097bfd1630d496559f7e7017e769", size = 34523393, upload-time = "2026-02-28T17:48:51.55Z" }, + { url = "https://files.pythonhosted.org/packages/c2/39/9c5071dfe0774ad442dab753716e472b537da0bf3fefbc61362ab60b7e5e/cmake-4.2.3-py3-none-musllinux_1_2_i686.whl", hash = "sha256:2605260fa17826c138ad3d78cdc3c6250b860f6c957653e35208c11e2ca4ad91", size = 40419861, upload-time = "2026-02-28T17:48:54.946Z" }, + { url = "https://files.pythonhosted.org/packages/cf/bd/44655063228db7d8f69f8ce3f84d44b854ab2a9bdc530fd9280e64377811/cmake-4.2.3-py3-none-musllinux_1_2_ppc64le.whl", hash = "sha256:f3693c97daaeedc931c6c2ef67b7213e60ef8e51c11050b6a7f4628f5f2a7883", size = 39615316, upload-time = "2026-02-28T17:48:58.308Z" }, + { url = "https://files.pythonhosted.org/packages/28/e4/f4ae13e3162151cdf978d9b279b6f21dd4b358bcc938607f7bddfd1f1da3/cmake-4.2.3-py3-none-musllinux_1_2_riscv64.whl", hash = "sha256:849ce056d644e1c84ba835976e8adc9777ccd2078d81ca80c20a933e4711b3f7", size = 34768312, upload-time = "2026-02-28T17:49:01.787Z" }, + { url = "https://files.pythonhosted.org/packages/28/5e/32ffc26ca34400de8c7fa5b03f3c132636268cb83f79b6a2f8c7dd8dc923/cmake-4.2.3-py3-none-musllinux_1_2_s390x.whl", hash = "sha256:1432016bf6cb533ff8833e0a434c640aa097ffd1a5c0e9341554c2c146050363", size = 36812265, upload-time = "2026-02-28T17:49:04.873Z" }, + { url = "https://files.pythonhosted.org/packages/fc/71/8324902abe3e883bc1d9adfb3c3e602e87f485b797defc87d0fe15fbb109/cmake-4.2.3-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:22c85da6d4aacebb3ed649bb7dc2fc034c6744f25ed8a02537408fe3c914bf9e", size = 37795828, upload-time = "2026-02-28T17:49:08.142Z" }, + { url = "https://files.pythonhosted.org/packages/cc/df/1b7d450ea66ba054c653165beab71414ccc1c1ea056e51574ae75206ea91/cmake-4.2.3-py3-none-win32.whl", hash = "sha256:2091e4c1b45e6e900dda4aebce1d3e912ddc3ba0c153dd6b35be0b18f7f2b2ce", size = 35499316, upload-time = "2026-02-28T17:49:10.957Z" }, + { url = "https://files.pythonhosted.org/packages/7f/d9/f1693ab5e403ae010df8c7ec12c77b2671080ec5c158b37f8db78814be96/cmake-4.2.3-py3-none-win_amd64.whl", hash = "sha256:0c55af0e1b2db232a94a7c34e89f25f3dbf410a4669b11134d07de0bd7aad03e", size = 38891890, upload-time = "2026-02-28T17:49:13.757Z" }, + { url = "https://files.pythonhosted.org/packages/9b/4b/6b246acb4be50867dc9237d55a0bc1ec0c8fbd47db2c868f1f2b4b690e76/cmake-4.2.3-py3-none-win_arm64.whl", hash = "sha256:e9d3761edc558b89321283c258f3bc036d2cda4c22ecfa181a25bb84e96afd4a", size = 38005743, upload-time = "2026-02-28T17:49:16.563Z" }, +] + +[[package]] +name = "cmakelang" +version = "0.6.13" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "six" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/74/c0/75d4806cf21dcb4198e9fba02f4d2fa61c8db919b7db788862d9cd5f4433/cmakelang-0.6.13.tar.gz", hash = "sha256:03982e87b00654d024d73ef972d9d9bb0e5726cdb6b8a424a15661fb6278e67f", size = 123111, upload-time = "2020-08-19T17:15:25.44Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/86/a8/c4676cac062d133c6b909d7def80a3194162597968953a3291b309878721/cmakelang-0.6.13-py3-none-any.whl", hash = "sha256:764b9467195c7c36453d60a829f30229720d26c7dffd41cb516b99bd9c7daf4e", size = 159803, upload-time = "2020-08-19T17:15:23.981Z" }, +] + +[[package]] +name = "colorama" +version = "0.4.6" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d8/53/6f443c9a4a8358a93a6792e2acffb9d9d5cb0a5cfd8802644b7b1c9a02e4/colorama-0.4.6.tar.gz", hash = "sha256:08695f5cb7ed6e0531a20572697297273c47b8cae5a63ffc6d6ed5c201be6e44", size = 27697, upload-time = "2022-10-25T02:36:22.414Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/d1/d6/3965ed04c63042e047cb6a3e6ed1a63a35087b6a609aa3a15ed8ac56c221/colorama-0.4.6-py2.py3-none-any.whl", hash = "sha256:4f1d9991f5acc0ca119f9d443620b77f9d6b33703e51011c16baf57afb285fc6", size = 25335, upload-time = "2022-10-25T02:36:20.889Z" }, +] + +[[package]] +name = "cryptography" +version = "46.0.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/60/04/ee2a9e8542e4fa2773b81771ff8349ff19cdd56b7258a0cc442639052edb/cryptography-46.0.5.tar.gz", hash = "sha256:abace499247268e3757271b2f1e244b36b06f8515cf27c4d49468fc9eb16e93d", size = 750064, upload-time = "2026-02-10T19:18:38.255Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f7/81/b0bb27f2ba931a65409c6b8a8b358a7f03c0e46eceacddff55f7c84b1f3b/cryptography-46.0.5-cp311-abi3-macosx_10_9_universal2.whl", hash = "sha256:351695ada9ea9618b3500b490ad54c739860883df6c1f555e088eaf25b1bbaad", size = 7176289, upload-time = "2026-02-10T19:17:08.274Z" }, + { url = "https://files.pythonhosted.org/packages/ff/9e/6b4397a3e3d15123de3b1806ef342522393d50736c13b20ec4c9ea6693a6/cryptography-46.0.5-cp311-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:c18ff11e86df2e28854939acde2d003f7984f721eba450b56a200ad90eeb0e6b", size = 4275637, upload-time = "2026-02-10T19:17:10.53Z" }, + { url = "https://files.pythonhosted.org/packages/63/e7/471ab61099a3920b0c77852ea3f0ea611c9702f651600397ac567848b897/cryptography-46.0.5-cp311-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:4d7e3d356b8cd4ea5aff04f129d5f66ebdc7b6f8eae802b93739ed520c47c79b", size = 4424742, upload-time = "2026-02-10T19:17:12.388Z" }, + { url = "https://files.pythonhosted.org/packages/37/53/a18500f270342d66bf7e4d9f091114e31e5ee9e7375a5aba2e85a91e0044/cryptography-46.0.5-cp311-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:50bfb6925eff619c9c023b967d5b77a54e04256c4281b0e21336a130cd7fc263", size = 4277528, upload-time = "2026-02-10T19:17:13.853Z" }, + { url = "https://files.pythonhosted.org/packages/22/29/c2e812ebc38c57b40e7c583895e73c8c5adb4d1e4a0cc4c5a4fdab2b1acc/cryptography-46.0.5-cp311-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:803812e111e75d1aa73690d2facc295eaefd4439be1023fefc4995eaea2af90d", size = 4947993, upload-time = "2026-02-10T19:17:15.618Z" }, + { url = "https://files.pythonhosted.org/packages/6b/e7/237155ae19a9023de7e30ec64e5d99a9431a567407ac21170a046d22a5a3/cryptography-46.0.5-cp311-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ee190460e2fbe447175cda91b88b84ae8322a104fc27766ad09428754a618ed", size = 4456855, upload-time = "2026-02-10T19:17:17.221Z" }, + { url = "https://files.pythonhosted.org/packages/2d/87/fc628a7ad85b81206738abbd213b07702bcbdada1dd43f72236ef3cffbb5/cryptography-46.0.5-cp311-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:f145bba11b878005c496e93e257c1e88f154d278d2638e6450d17e0f31e558d2", size = 3984635, upload-time = "2026-02-10T19:17:18.792Z" }, + { url = "https://files.pythonhosted.org/packages/84/29/65b55622bde135aedf4565dc509d99b560ee4095e56989e815f8fd2aa910/cryptography-46.0.5-cp311-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:e9251e3be159d1020c4030bd2e5f84d6a43fe54b6c19c12f51cde9542a2817b2", size = 4277038, upload-time = "2026-02-10T19:17:20.256Z" }, + { url = "https://files.pythonhosted.org/packages/bc/36/45e76c68d7311432741faf1fbf7fac8a196a0a735ca21f504c75d37e2558/cryptography-46.0.5-cp311-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:47fb8a66058b80e509c47118ef8a75d14c455e81ac369050f20ba0d23e77fee0", size = 4912181, upload-time = "2026-02-10T19:17:21.825Z" }, + { url = "https://files.pythonhosted.org/packages/6d/1a/c1ba8fead184d6e3d5afcf03d569acac5ad063f3ac9fb7258af158f7e378/cryptography-46.0.5-cp311-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:4c3341037c136030cb46e4b1e17b7418ea4cbd9dd207e4a6f3b2b24e0d4ac731", size = 4456482, upload-time = "2026-02-10T19:17:25.133Z" }, + { url = "https://files.pythonhosted.org/packages/f9/e5/3fb22e37f66827ced3b902cf895e6a6bc1d095b5b26be26bd13c441fdf19/cryptography-46.0.5-cp311-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:890bcb4abd5a2d3f852196437129eb3667d62630333aacc13dfd470fad3aaa82", size = 4405497, upload-time = "2026-02-10T19:17:26.66Z" }, + { url = "https://files.pythonhosted.org/packages/1a/df/9d58bb32b1121a8a2f27383fabae4d63080c7ca60b9b5c88be742be04ee7/cryptography-46.0.5-cp311-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:80a8d7bfdf38f87ca30a5391c0c9ce4ed2926918e017c29ddf643d0ed2778ea1", size = 4667819, upload-time = "2026-02-10T19:17:28.569Z" }, + { url = "https://files.pythonhosted.org/packages/ea/ed/325d2a490c5e94038cdb0117da9397ece1f11201f425c4e9c57fe5b9f08b/cryptography-46.0.5-cp311-abi3-win32.whl", hash = "sha256:60ee7e19e95104d4c03871d7d7dfb3d22ef8a9b9c6778c94e1c8fcc8365afd48", size = 3028230, upload-time = "2026-02-10T19:17:30.518Z" }, + { url = "https://files.pythonhosted.org/packages/e9/5a/ac0f49e48063ab4255d9e3b79f5def51697fce1a95ea1370f03dc9db76f6/cryptography-46.0.5-cp311-abi3-win_amd64.whl", hash = "sha256:38946c54b16c885c72c4f59846be9743d699eee2b69b6988e0a00a01f46a61a4", size = 3480909, upload-time = "2026-02-10T19:17:32.083Z" }, + { url = "https://files.pythonhosted.org/packages/00/13/3d278bfa7a15a96b9dc22db5a12ad1e48a9eb3d40e1827ef66a5df75d0d0/cryptography-46.0.5-cp314-cp314t-macosx_10_9_universal2.whl", hash = "sha256:94a76daa32eb78d61339aff7952ea819b1734b46f73646a07decb40e5b3448e2", size = 7119287, upload-time = "2026-02-10T19:17:33.801Z" }, + { url = "https://files.pythonhosted.org/packages/67/c8/581a6702e14f0898a0848105cbefd20c058099e2c2d22ef4e476dfec75d7/cryptography-46.0.5-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:5be7bf2fb40769e05739dd0046e7b26f9d4670badc7b032d6ce4db64dddc0678", size = 4265728, upload-time = "2026-02-10T19:17:35.569Z" }, + { url = "https://files.pythonhosted.org/packages/dd/4a/ba1a65ce8fc65435e5a849558379896c957870dd64fecea97b1ad5f46a37/cryptography-46.0.5-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe346b143ff9685e40192a4960938545c699054ba11d4f9029f94751e3f71d87", size = 4408287, upload-time = "2026-02-10T19:17:36.938Z" }, + { url = "https://files.pythonhosted.org/packages/f8/67/8ffdbf7b65ed1ac224d1c2df3943553766914a8ca718747ee3871da6107e/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_aarch64.whl", hash = "sha256:c69fd885df7d089548a42d5ec05be26050ebcd2283d89b3d30676eb32ff87dee", size = 4270291, upload-time = "2026-02-10T19:17:38.748Z" }, + { url = "https://files.pythonhosted.org/packages/f8/e5/f52377ee93bc2f2bba55a41a886fd208c15276ffbd2569f2ddc89d50e2c5/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_ppc64le.whl", hash = "sha256:8293f3dea7fc929ef7240796ba231413afa7b68ce38fd21da2995549f5961981", size = 4927539, upload-time = "2026-02-10T19:17:40.241Z" }, + { url = "https://files.pythonhosted.org/packages/3b/02/cfe39181b02419bbbbcf3abdd16c1c5c8541f03ca8bda240debc467d5a12/cryptography-46.0.5-cp314-cp314t-manylinux_2_28_x86_64.whl", hash = "sha256:1abfdb89b41c3be0365328a410baa9df3ff8a9110fb75e7b52e66803ddabc9a9", size = 4442199, upload-time = "2026-02-10T19:17:41.789Z" }, + { url = "https://files.pythonhosted.org/packages/c0/96/2fcaeb4873e536cf71421a388a6c11b5bc846e986b2b069c79363dc1648e/cryptography-46.0.5-cp314-cp314t-manylinux_2_31_armv7l.whl", hash = "sha256:d66e421495fdb797610a08f43b05269e0a5ea7f5e652a89bfd5a7d3c1dee3648", size = 3960131, upload-time = "2026-02-10T19:17:43.379Z" }, + { url = "https://files.pythonhosted.org/packages/d8/d2/b27631f401ddd644e94c5cf33c9a4069f72011821cf3dc7309546b0642a0/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:4e817a8920bfbcff8940ecfd60f23d01836408242b30f1a708d93198393a80b4", size = 4270072, upload-time = "2026-02-10T19:17:45.481Z" }, + { url = "https://files.pythonhosted.org/packages/f4/a7/60d32b0370dae0b4ebe55ffa10e8599a2a59935b5ece1b9f06edb73abdeb/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_ppc64le.whl", hash = "sha256:68f68d13f2e1cb95163fa3b4db4bf9a159a418f5f6e7242564fc75fcae667fd0", size = 4892170, upload-time = "2026-02-10T19:17:46.997Z" }, + { url = "https://files.pythonhosted.org/packages/d2/b9/cf73ddf8ef1164330eb0b199a589103c363afa0cf794218c24d524a58eab/cryptography-46.0.5-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:a3d1fae9863299076f05cb8a778c467578262fae09f9dc0ee9b12eb4268ce663", size = 4441741, upload-time = "2026-02-10T19:17:48.661Z" }, + { url = "https://files.pythonhosted.org/packages/5f/eb/eee00b28c84c726fe8fa0158c65afe312d9c3b78d9d01daf700f1f6e37ff/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:c4143987a42a2397f2fc3b4d7e3a7d313fbe684f67ff443999e803dd75a76826", size = 4396728, upload-time = "2026-02-10T19:17:50.058Z" }, + { url = "https://files.pythonhosted.org/packages/65/f4/6bc1a9ed5aef7145045114b75b77c2a8261b4d38717bd8dea111a63c3442/cryptography-46.0.5-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:7d731d4b107030987fd61a7f8ab512b25b53cef8f233a97379ede116f30eb67d", size = 4652001, upload-time = "2026-02-10T19:17:51.54Z" }, + { url = "https://files.pythonhosted.org/packages/86/ef/5d00ef966ddd71ac2e6951d278884a84a40ffbd88948ef0e294b214ae9e4/cryptography-46.0.5-cp314-cp314t-win32.whl", hash = "sha256:c3bcce8521d785d510b2aad26ae2c966092b7daa8f45dd8f44734a104dc0bc1a", size = 3003637, upload-time = "2026-02-10T19:17:52.997Z" }, + { url = "https://files.pythonhosted.org/packages/b7/57/f3f4160123da6d098db78350fdfd9705057aad21de7388eacb2401dceab9/cryptography-46.0.5-cp314-cp314t-win_amd64.whl", hash = "sha256:4d8ae8659ab18c65ced284993c2265910f6c9e650189d4e3f68445ef82a810e4", size = 3469487, upload-time = "2026-02-10T19:17:54.549Z" }, + { url = "https://files.pythonhosted.org/packages/e2/fa/a66aa722105ad6a458bebd64086ca2b72cdd361fed31763d20390f6f1389/cryptography-46.0.5-cp38-abi3-macosx_10_9_universal2.whl", hash = "sha256:4108d4c09fbbf2789d0c926eb4152ae1760d5a2d97612b92d508d96c861e4d31", size = 7170514, upload-time = "2026-02-10T19:17:56.267Z" }, + { url = "https://files.pythonhosted.org/packages/0f/04/c85bdeab78c8bc77b701bf0d9bdcf514c044e18a46dcff330df5448631b0/cryptography-46.0.5-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:7d1f30a86d2757199cb2d56e48cce14deddf1f9c95f1ef1b64ee91ea43fe2e18", size = 4275349, upload-time = "2026-02-10T19:17:58.419Z" }, + { url = "https://files.pythonhosted.org/packages/5c/32/9b87132a2f91ee7f5223b091dc963055503e9b442c98fc0b8a5ca765fab0/cryptography-46.0.5-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:039917b0dc418bb9f6edce8a906572d69e74bd330b0b3fea4f79dab7f8ddd235", size = 4420667, upload-time = "2026-02-10T19:18:00.619Z" }, + { url = "https://files.pythonhosted.org/packages/a1/a6/a7cb7010bec4b7c5692ca6f024150371b295ee1c108bdc1c400e4c44562b/cryptography-46.0.5-cp38-abi3-manylinux_2_28_aarch64.whl", hash = "sha256:ba2a27ff02f48193fc4daeadf8ad2590516fa3d0adeeb34336b96f7fa64c1e3a", size = 4276980, upload-time = "2026-02-10T19:18:02.379Z" }, + { url = "https://files.pythonhosted.org/packages/8e/7c/c4f45e0eeff9b91e3f12dbd0e165fcf2a38847288fcfd889deea99fb7b6d/cryptography-46.0.5-cp38-abi3-manylinux_2_28_ppc64le.whl", hash = "sha256:61aa400dce22cb001a98014f647dc21cda08f7915ceb95df0c9eaf84b4b6af76", size = 4939143, upload-time = "2026-02-10T19:18:03.964Z" }, + { url = "https://files.pythonhosted.org/packages/37/19/e1b8f964a834eddb44fa1b9a9976f4e414cbb7aa62809b6760c8803d22d1/cryptography-46.0.5-cp38-abi3-manylinux_2_28_x86_64.whl", hash = "sha256:3ce58ba46e1bc2aac4f7d9290223cead56743fa6ab94a5d53292ffaac6a91614", size = 4453674, upload-time = "2026-02-10T19:18:05.588Z" }, + { url = "https://files.pythonhosted.org/packages/db/ed/db15d3956f65264ca204625597c410d420e26530c4e2943e05a0d2f24d51/cryptography-46.0.5-cp38-abi3-manylinux_2_31_armv7l.whl", hash = "sha256:420d0e909050490d04359e7fdb5ed7e667ca5c3c402b809ae2563d7e66a92229", size = 3978801, upload-time = "2026-02-10T19:18:07.167Z" }, + { url = "https://files.pythonhosted.org/packages/41/e2/df40a31d82df0a70a0daf69791f91dbb70e47644c58581d654879b382d11/cryptography-46.0.5-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:582f5fcd2afa31622f317f80426a027f30dc792e9c80ffee87b993200ea115f1", size = 4276755, upload-time = "2026-02-10T19:18:09.813Z" }, + { url = "https://files.pythonhosted.org/packages/33/45/726809d1176959f4a896b86907b98ff4391a8aa29c0aaaf9450a8a10630e/cryptography-46.0.5-cp38-abi3-manylinux_2_34_ppc64le.whl", hash = "sha256:bfd56bb4b37ed4f330b82402f6f435845a5f5648edf1ad497da51a8452d5d62d", size = 4901539, upload-time = "2026-02-10T19:18:11.263Z" }, + { url = "https://files.pythonhosted.org/packages/99/0f/a3076874e9c88ecb2ecc31382f6e7c21b428ede6f55aafa1aa272613e3cd/cryptography-46.0.5-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:a3d507bb6a513ca96ba84443226af944b0f7f47dcc9a399d110cd6146481d24c", size = 4452794, upload-time = "2026-02-10T19:18:12.914Z" }, + { url = "https://files.pythonhosted.org/packages/02/ef/ffeb542d3683d24194a38f66ca17c0a4b8bf10631feef44a7ef64e631b1a/cryptography-46.0.5-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:9f16fbdf4da055efb21c22d81b89f155f02ba420558db21288b3d0035bafd5f4", size = 4404160, upload-time = "2026-02-10T19:18:14.375Z" }, + { url = "https://files.pythonhosted.org/packages/96/93/682d2b43c1d5f1406ed048f377c0fc9fc8f7b0447a478d5c65ab3d3a66eb/cryptography-46.0.5-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:ced80795227d70549a411a4ab66e8ce307899fad2220ce5ab2f296e687eacde9", size = 4667123, upload-time = "2026-02-10T19:18:15.886Z" }, + { url = "https://files.pythonhosted.org/packages/45/2d/9c5f2926cb5300a8eefc3f4f0b3f3df39db7f7ce40c8365444c49363cbda/cryptography-46.0.5-cp38-abi3-win32.whl", hash = "sha256:02f547fce831f5096c9a567fd41bc12ca8f11df260959ecc7c3202555cc47a72", size = 3010220, upload-time = "2026-02-10T19:18:17.361Z" }, + { url = "https://files.pythonhosted.org/packages/48/ef/0c2f4a8e31018a986949d34a01115dd057bf536905dca38897bacd21fac3/cryptography-46.0.5-cp38-abi3-win_amd64.whl", hash = "sha256:556e106ee01aa13484ce9b0239bca667be5004efb0aabbed28d353df86445595", size = 3467050, upload-time = "2026-02-10T19:18:18.899Z" }, +] + +[[package]] +name = "cyclopts" +version = "4.8.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "attrs" }, + { name = "docstring-parser" }, + { name = "rich" }, + { name = "rich-rst" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/33/7a/3c3623755561c7f283dd769470e99ae36c46810bf3b3f264d69006f6c97a/cyclopts-4.8.0.tar.gz", hash = "sha256:92cc292d18d8be372e58d8bce1aa966d30f819a5fb3fee02bd2ad4a6bb403f29", size = 164066, upload-time = "2026-03-07T19:39:18.122Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/87/01/6ec7210775ea5e4989a10d89eda6c5ea7ff06caa614231ad533d74fecac8/cyclopts-4.8.0-py3-none-any.whl", hash = "sha256:ef353da05fec36587d4ebce7a6e4b27515d775d184a23bab4b01426f93ddc8d4", size = 201948, upload-time = "2026-03-07T19:39:19.307Z" }, +] + +[[package]] +name = "docstring-parser" +version = "0.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b2/9d/c3b43da9515bd270df0f80548d9944e389870713cc1fe2b8fb35fe2bcefd/docstring_parser-0.17.0.tar.gz", hash = "sha256:583de4a309722b3315439bb31d64ba3eebada841f2e2cee23b99df001434c912", size = 27442, upload-time = "2025-07-21T07:35:01.868Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/55/e2/2537ebcff11c1ee1ff17d8d0b6f4db75873e3b0fb32c2d4a2ee31ecb310a/docstring_parser-0.17.0-py3-none-any.whl", hash = "sha256:cf2569abd23dce8099b300f9b4fa8191e9582dda731fd533daf54c4551658708", size = 36896, upload-time = "2025-07-21T07:35:00.684Z" }, +] + +[[package]] +name = "docutils" +version = "0.22.4" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/ae/b6/03bb70946330e88ffec97aefd3ea75ba575cb2e762061e0e62a213befee8/docutils-0.22.4.tar.gz", hash = "sha256:4db53b1fde9abecbb74d91230d32ab626d94f6badfc575d6db9194a49df29968", size = 2291750, upload-time = "2025-12-18T19:00:26.443Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/02/10/5da547df7a391dcde17f59520a231527b8571e6f46fc8efb02ccb370ab12/docutils-0.22.4-py3-none-any.whl", hash = "sha256:d0013f540772d1420576855455d050a2180186c91c15779301ac2ccb3eeb68de", size = 633196, upload-time = "2025-12-18T19:00:18.077Z" }, +] + +[[package]] +name = "idna" +version = "3.11" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/6f/6d/0703ccc57f3a7233505399edb88de3cbd678da106337b9fcde432b65ed60/idna-3.11.tar.gz", hash = "sha256:795dafcc9c04ed0c1fb032c2aa73654d8e8c5023a7df64a53f39190ada629902", size = 194582, upload-time = "2025-10-12T14:55:20.501Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0e/61/66938bbb5fc52dbdf84594873d5b51fb1f7c7794e9c0f5bd885f30bc507b/idna-3.11-py3-none-any.whl", hash = "sha256:771a87f49d9defaf64091e6e6fe9c18d4833f140bd19464795bc32d966ca37ea", size = 71008, upload-time = "2025-10-12T14:55:18.883Z" }, +] + +[[package]] +name = "markdown-it-py" +version = "4.0.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "mdurl" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5b/f5/4ec618ed16cc4f8fb3b701563655a69816155e79e24a17b651541804721d/markdown_it_py-4.0.0.tar.gz", hash = "sha256:cb0a2b4aa34f932c007117b194e945bd74e0ec24133ceb5bac59009cda1cb9f3", size = 73070, upload-time = "2025-08-11T12:57:52.854Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/94/54/e7d793b573f298e1c9013b8c4dade17d481164aa517d1d7148619c2cedbf/markdown_it_py-4.0.0-py3-none-any.whl", hash = "sha256:87327c59b172c5011896038353a81343b6754500a08cd7a4973bb48c6d578147", size = 87321, upload-time = "2025-08-11T12:57:51.923Z" }, +] + +[[package]] +name = "mdurl" +version = "0.1.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/d6/54/cfe61301667036ec958cb99bd3efefba235e65cdeb9c84d24a8293ba1d90/mdurl-0.1.2.tar.gz", hash = "sha256:bb413d29f5eea38f31dd4754dd7377d4465116fb207585f97bf925588687c1ba", size = 8729, upload-time = "2022-08-14T12:40:10.846Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b3/38/89ba8ad64ae25be8de66a6d463314cf1eb366222074cfda9ee839c56a4b4/mdurl-0.1.2-py3-none-any.whl", hash = "sha256:84008a41e51615a49fc9966191ff91509e3c40b939176e643fd50a5c2196b8f8", size = 9979, upload-time = "2022-08-14T12:40:09.779Z" }, +] + +[[package]] +name = "polib" +version = "1.2.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/10/9a/79b1067d27e38ddf84fe7da6ec516f1743f31f752c6122193e7bce38bdbf/polib-1.2.0.tar.gz", hash = "sha256:f3ef94aefed6e183e342a8a269ae1fc4742ba193186ad76f175938621dbfc26b", size = 161658, upload-time = "2023-02-23T17:53:56.873Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6b/99/45bb1f9926efe370c6dbe324741c749658e44cb060124f28dad201202274/polib-1.2.0-py2.py3-none-any.whl", hash = "sha256:1c77ee1b81feb31df9bca258cbc58db1bbb32d10214b173882452c73af06d62d", size = 20634, upload-time = "2023-02-23T17:53:59.919Z" }, +] + +[[package]] +name = "prek" +version = "0.3.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/46/d6/277e002e56eeab3a9d48f1ca4cc067d249d6326fc1783b770d70ad5ae2be/prek-0.3.5.tar.gz", hash = "sha256:ca40b6685a4192256bc807f32237af94bf9b8799c0d708b98735738250685642", size = 374806, upload-time = "2026-03-09T10:35:18.842Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/a9/16dd8d3a50362ebccffe58518af1f1f571c96f0695d7fcd8bbd386585f58/prek-0.3.5-py3-none-linux_armv6l.whl", hash = "sha256:44b3e12791805804f286d103682b42a84e0f98a2687faa37045e9d3375d3d73d", size = 5105604, upload-time = "2026-03-09T10:35:00.332Z" }, + { url = "https://files.pythonhosted.org/packages/e4/74/bc6036f5bf03860cda66ab040b32737e54802b71a81ec381839deb25df9e/prek-0.3.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:e3cb451cc51ac068974557491beb4c7d2d41dfde29ed559c1694c8ce23bf53e8", size = 5506155, upload-time = "2026-03-09T10:35:17.64Z" }, + { url = "https://files.pythonhosted.org/packages/02/d9/a3745c2a10509c63b6a118ada766614dd705efefd08f275804d5c807aa4a/prek-0.3.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:ad8f5f0d8da53dc94d00b76979af312b3dacccc9dcbc6417756c5dca3633c052", size = 5100383, upload-time = "2026-03-09T10:35:13.302Z" }, + { url = "https://files.pythonhosted.org/packages/43/8e/de965fc515d39309a332789cd3778161f7bc80cde15070bedf17f9f8cb93/prek-0.3.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.musllinux_1_1_aarch64.whl", hash = "sha256:4511e15d34072851ac88e4b2006868fbe13655059ad941d7a0ff9ee17138fd9f", size = 5334913, upload-time = "2026-03-09T10:35:14.813Z" }, + { url = "https://files.pythonhosted.org/packages/3f/8c/44f07e8940256059cfd82520e3cbe0764ab06ddb4aa43148465db00b39ad/prek-0.3.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fcc0b63b8337e2046f51267facaac63ba755bc14aad53991840a5eccba3e5c28", size = 5033825, upload-time = "2026-03-09T10:35:06.976Z" }, + { url = "https://files.pythonhosted.org/packages/94/85/3ff0f96881ff2360c212d310ff23c3cf5a15b223d34fcfa8cdcef203be69/prek-0.3.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:f5fc0d78c3896a674aeb8247a83bbda7efec85274dbdfbc978ceff8d37e4ed20", size = 5438586, upload-time = "2026-03-09T10:34:58.779Z" }, + { url = "https://files.pythonhosted.org/packages/79/a5/c6d08d31293400fcb5d427f8e7e6bacfc959988e868ad3a9d97b4d87c4b7/prek-0.3.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:64cad21cb9072d985179495b77b312f6b81e7b45357d0c68dc1de66e0408eabc", size = 6359714, upload-time = "2026-03-09T10:34:57.454Z" }, + { url = "https://files.pythonhosted.org/packages/ba/18/321dcff9ece8065d42c8c1c7a53a23b45d2b4330aa70993be75dc5f2822f/prek-0.3.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:45ee84199bb48e013bdfde0c84352c17a44cc42d5792681b86d94e9474aab6f8", size = 5717632, upload-time = "2026-03-09T10:35:08.634Z" }, + { url = "https://files.pythonhosted.org/packages/a3/7f/1288226aa381d0cea403157f4e6b64b356e1a745f2441c31dd9d8a1d63da/prek-0.3.5-py3-none-manylinux_2_28_aarch64.whl", hash = "sha256:f43275e5d564e18e52133129ebeb5cb071af7ce4a547766c7f025aa0955dfbb6", size = 5339040, upload-time = "2026-03-09T10:35:03.665Z" }, + { url = "https://files.pythonhosted.org/packages/22/94/cfec83df9c2b8e7ed1608087bcf9538a6a77b4c2e7365123e9e0a3162cd1/prek-0.3.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:abcee520d31522bcbad9311f21326b447694cd5edba33618c25fd023fc9865ec", size = 5162586, upload-time = "2026-03-09T10:35:11.564Z" }, + { url = "https://files.pythonhosted.org/packages/13/b7/741d62132f37a5f7cc0fad1168bd31f20dea9628f482f077f569547e0436/prek-0.3.5-py3-none-musllinux_1_1_armv7l.whl", hash = "sha256:499c56a94a155790c75a973d351a33f8065579d9094c93f6d451ada5d1e469be", size = 5002933, upload-time = "2026-03-09T10:35:16.347Z" }, + { url = "https://files.pythonhosted.org/packages/6f/83/630a5671df6550fcfa67c54955e8a8174eb9b4d97ac38fb05a362029245b/prek-0.3.5-py3-none-musllinux_1_1_i686.whl", hash = "sha256:de1065b59f194624adc9dea269d4ff6b50e98a1b5bb662374a9adaa496b3c1eb", size = 5304934, upload-time = "2026-03-09T10:35:09.975Z" }, + { url = "https://files.pythonhosted.org/packages/de/79/67a7afd0c0b6c436630b7dba6e586a42d21d5d6e5778fbd9eba7bbd3dd26/prek-0.3.5-py3-none-musllinux_1_1_x86_64.whl", hash = "sha256:a1c4869e45ee341735d07179da3a79fa2afb5959cef8b3c8a71906eb52dc6933", size = 5829914, upload-time = "2026-03-09T10:35:05.39Z" }, + { url = "https://files.pythonhosted.org/packages/37/47/e2fe13b33e7b5fdd9dd1a312f5440208bfe1be6183e54c5c99c10f27d848/prek-0.3.5-py3-none-win32.whl", hash = "sha256:70b2152ecedc58f3f4f69adc884617b0cf44259f7414c44d6268ea6f107672eb", size = 4836910, upload-time = "2026-03-09T10:35:01.884Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ab/dc2a139fd4896d11f39631479ed385e86307af7f54059ebe9414bb0d00c6/prek-0.3.5-py3-none-win_amd64.whl", hash = "sha256:01d031b684f7e1546225393af1268d9b4451a44ef6cb9be4101c85c7862e08db", size = 5234234, upload-time = "2026-03-09T10:35:20.193Z" }, + { url = "https://files.pythonhosted.org/packages/ed/38/f7256b4b7581444f658e909c3b566f51bfabe56c03e80d107a6932d62040/prek-0.3.5-py3-none-win_arm64.whl", hash = "sha256:aa774168e3d868039ff79422bdef2df8d5a016ed804a9914607dcdd3d41da053", size = 5083330, upload-time = "2026-03-09T10:34:55.469Z" }, +] + +[[package]] +name = "pycparser" +version = "3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/1b/7d/92392ff7815c21062bea51aa7b87d45576f649f16458d78b7cf94b9ab2e6/pycparser-3.0.tar.gz", hash = "sha256:600f49d217304a5902ac3c37e1281c9fe94e4d0489de643a9504c5cdfdfc6b29", size = 103492, upload-time = "2026-01-21T14:26:51.89Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/0c/c3/44f3fbbfa403ea2a7c779186dc20772604442dde72947e7d01069cbe98e3/pycparser-3.0-py3-none-any.whl", hash = "sha256:b727414169a36b7d524c1c3e31839a521725078d7b2ff038656844266160a992", size = 48172, upload-time = "2026-01-21T14:26:50.693Z" }, +] + +[[package]] +name = "pygithub" +version = "2.8.1" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "pyjwt", extra = ["crypto"] }, + { name = "pynacl" }, + { name = "requests" }, + { name = "typing-extensions" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c1/74/e560bdeffea72ecb26cff27f0fad548bbff5ecc51d6a155311ea7f9e4c4c/pygithub-2.8.1.tar.gz", hash = "sha256:341b7c78521cb07324ff670afd1baa2bf5c286f8d9fd302c1798ba594a5400c9", size = 2246994, upload-time = "2025-09-02T17:41:54.674Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/07/ba/7049ce39f653f6140aac4beb53a5aaf08b4407b6a3019aae394c1c5244ff/pygithub-2.8.1-py3-none-any.whl", hash = "sha256:23a0a5bca93baef082e03411bf0ce27204c32be8bfa7abc92fe4a3e132936df0", size = 432709, upload-time = "2025-09-02T17:41:52.947Z" }, +] + +[[package]] +name = "pygments" +version = "2.19.2" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/b0/77/a5b8c569bf593b0140bde72ea885a803b82086995367bf2037de0159d924/pygments-2.19.2.tar.gz", hash = "sha256:636cb2477cec7f8952536970bc533bc43743542f70392ae026374600add5b887", size = 4968631, upload-time = "2025-06-21T13:39:12.283Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/21/705964c7812476f378728bdf590ca4b771ec72385c533964653c68e86bdc/pygments-2.19.2-py3-none-any.whl", hash = "sha256:86540386c03d588bb81d44bc3928634ff26449851e99741617ecb9037ee5ec0b", size = 1225217, upload-time = "2025-06-21T13:39:07.939Z" }, +] + +[[package]] +name = "pyjwt" +version = "2.11.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/5c/5a/b46fa56bf322901eee5b0454a34343cdbdae202cd421775a8ee4e42fd519/pyjwt-2.11.0.tar.gz", hash = "sha256:35f95c1f0fbe5d5ba6e43f00271c275f7a1a4db1dab27bf708073b75318ea623", size = 98019, upload-time = "2026-01-30T19:59:55.694Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/6f/01/c26ce75ba460d5cd503da9e13b21a33804d38c2165dec7b716d06b13010c/pyjwt-2.11.0-py3-none-any.whl", hash = "sha256:94a6bde30eb5c8e04fee991062b534071fd1439ef58d2adc9ccb823e7bcd0469", size = 28224, upload-time = "2026-01-30T19:59:54.539Z" }, +] + +[package.optional-dependencies] +crypto = [ + { name = "cryptography" }, +] + +[[package]] +name = "pynacl" +version = "1.6.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "cffi", marker = "platform_python_implementation != 'PyPy'" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/d9/9a/4019b524b03a13438637b11538c82781a5eda427394380381af8f04f467a/pynacl-1.6.2.tar.gz", hash = "sha256:018494d6d696ae03c7e656e5e74cdfd8ea1326962cc401bcf018f1ed8436811c", size = 3511692, upload-time = "2026-01-01T17:48:10.851Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/79/0e3c34dc3c4671f67d251c07aa8eb100916f250ee470df230b0ab89551b4/pynacl-1.6.2-cp314-cp314t-macosx_10_10_universal2.whl", hash = "sha256:622d7b07cc5c02c666795792931b50c91f3ce3c2649762efb1ef0d5684c81594", size = 390064, upload-time = "2026-01-01T17:31:57.264Z" }, + { url = "https://files.pythonhosted.org/packages/eb/1c/23a26e931736e13b16483795c8a6b2f641bf6a3d5238c22b070a5112722c/pynacl-1.6.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:d071c6a9a4c94d79eb665db4ce5cedc537faf74f2355e4d502591d850d3913c0", size = 809370, upload-time = "2026-01-01T17:31:59.198Z" }, + { url = "https://files.pythonhosted.org/packages/87/74/8d4b718f8a22aea9e8dcc8b95deb76d4aae380e2f5b570cc70b5fd0a852d/pynacl-1.6.2-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:fe9847ca47d287af41e82be1dd5e23023d3c31a951da134121ab02e42ac218c9", size = 1408304, upload-time = "2026-01-01T17:32:01.162Z" }, + { url = "https://files.pythonhosted.org/packages/fd/73/be4fdd3a6a87fe8a4553380c2b47fbd1f7f58292eb820902f5c8ac7de7b0/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:04316d1fc625d860b6c162fff704eb8426b1a8bcd3abacea11142cbd99a6b574", size = 844871, upload-time = "2026-01-01T17:32:02.824Z" }, + { url = "https://files.pythonhosted.org/packages/55/ad/6efc57ab75ee4422e96b5f2697d51bbcf6cdcc091e66310df91fbdc144a8/pynacl-1.6.2-cp314-cp314t-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:44081faff368d6c5553ccf55322ef2819abb40e25afaec7e740f159f74813634", size = 1446356, upload-time = "2026-01-01T17:32:04.452Z" }, + { url = "https://files.pythonhosted.org/packages/78/b7/928ee9c4779caa0a915844311ab9fb5f99585621c5d6e4574538a17dca07/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_aarch64.whl", hash = "sha256:a9f9932d8d2811ce1a8ffa79dcbdf3970e7355b5c8eb0c1a881a57e7f7d96e88", size = 826814, upload-time = "2026-01-01T17:32:06.078Z" }, + { url = "https://files.pythonhosted.org/packages/f7/a9/1bdba746a2be20f8809fee75c10e3159d75864ef69c6b0dd168fc60e485d/pynacl-1.6.2-cp314-cp314t-manylinux_2_34_x86_64.whl", hash = "sha256:bc4a36b28dd72fb4845e5d8f9760610588a96d5a51f01d84d8c6ff9849968c14", size = 1411742, upload-time = "2026-01-01T17:32:07.651Z" }, + { url = "https://files.pythonhosted.org/packages/f3/2f/5e7ea8d85f9f3ea5b6b87db1d8388daa3587eed181bdeb0306816fdbbe79/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:3bffb6d0f6becacb6526f8f42adfb5efb26337056ee0831fb9a7044d1a964444", size = 801714, upload-time = "2026-01-01T17:32:09.558Z" }, + { url = "https://files.pythonhosted.org/packages/06/ea/43fe2f7eab5f200e40fb10d305bf6f87ea31b3bbc83443eac37cd34a9e1e/pynacl-1.6.2-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:2fef529ef3ee487ad8113d287a593fa26f48ee3620d92ecc6f1d09ea38e0709b", size = 1372257, upload-time = "2026-01-01T17:32:11.026Z" }, + { url = "https://files.pythonhosted.org/packages/4d/54/c9ea116412788629b1347e415f72195c25eb2f3809b2d3e7b25f5c79f13a/pynacl-1.6.2-cp314-cp314t-win32.whl", hash = "sha256:a84bf1c20339d06dc0c85d9aea9637a24f718f375d861b2668b2f9f96fa51145", size = 231319, upload-time = "2026-01-01T17:32:12.46Z" }, + { url = "https://files.pythonhosted.org/packages/ce/04/64e9d76646abac2dccf904fccba352a86e7d172647557f35b9fe2a5ee4a1/pynacl-1.6.2-cp314-cp314t-win_amd64.whl", hash = "sha256:320ef68a41c87547c91a8b58903c9caa641ab01e8512ce291085b5fe2fcb7590", size = 244044, upload-time = "2026-01-01T17:32:13.781Z" }, + { url = "https://files.pythonhosted.org/packages/33/33/7873dc161c6a06f43cda13dec67b6fe152cb2f982581151956fa5e5cdb47/pynacl-1.6.2-cp314-cp314t-win_arm64.whl", hash = "sha256:d29bfe37e20e015a7d8b23cfc8bd6aa7909c92a1b8f41ee416bbb3e79ef182b2", size = 188740, upload-time = "2026-01-01T17:32:15.083Z" }, + { url = "https://files.pythonhosted.org/packages/be/7b/4845bbf88e94586ec47a432da4e9107e3fc3ce37eb412b1398630a37f7dd/pynacl-1.6.2-cp38-abi3-macosx_10_10_universal2.whl", hash = "sha256:c949ea47e4206af7c8f604b8278093b674f7c79ed0d4719cc836902bf4517465", size = 388458, upload-time = "2026-01-01T17:32:16.829Z" }, + { url = "https://files.pythonhosted.org/packages/1e/b4/e927e0653ba63b02a4ca5b4d852a8d1d678afbf69b3dbf9c4d0785ac905c/pynacl-1.6.2-cp38-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.whl", hash = "sha256:8845c0631c0be43abdd865511c41eab235e0be69c81dc66a50911594198679b0", size = 800020, upload-time = "2026-01-01T17:32:18.34Z" }, + { url = "https://files.pythonhosted.org/packages/7f/81/d60984052df5c97b1d24365bc1e30024379b42c4edcd79d2436b1b9806f2/pynacl-1.6.2-cp38-abi3-manylinux2014_x86_64.manylinux_2_17_x86_64.whl", hash = "sha256:22de65bb9010a725b0dac248f353bb072969c94fa8d6b1f34b87d7953cf7bbe4", size = 1399174, upload-time = "2026-01-01T17:32:20.239Z" }, + { url = "https://files.pythonhosted.org/packages/68/f7/322f2f9915c4ef27d140101dd0ed26b479f7e6f5f183590fd32dfc48c4d3/pynacl-1.6.2-cp38-abi3-manylinux_2_26_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:46065496ab748469cdd999246d17e301b2c24ae2fdf739132e580a0e94c94a87", size = 835085, upload-time = "2026-01-01T17:32:22.24Z" }, + { url = "https://files.pythonhosted.org/packages/3e/d0/f301f83ac8dbe53442c5a43f6a39016f94f754d7a9815a875b65e218a307/pynacl-1.6.2-cp38-abi3-manylinux_2_26_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:8a66d6fb6ae7661c58995f9c6435bda2b1e68b54b598a6a10247bfcdadac996c", size = 1437614, upload-time = "2026-01-01T17:32:23.766Z" }, + { url = "https://files.pythonhosted.org/packages/c4/58/fc6e649762b029315325ace1a8c6be66125e42f67416d3dbd47b69563d61/pynacl-1.6.2-cp38-abi3-manylinux_2_34_aarch64.whl", hash = "sha256:26bfcd00dcf2cf160f122186af731ae30ab120c18e8375684ec2670dccd28130", size = 818251, upload-time = "2026-01-01T17:32:25.69Z" }, + { url = "https://files.pythonhosted.org/packages/c9/a8/b917096b1accc9acd878819a49d3d84875731a41eb665f6ebc826b1af99e/pynacl-1.6.2-cp38-abi3-manylinux_2_34_x86_64.whl", hash = "sha256:c8a231e36ec2cab018c4ad4358c386e36eede0319a0c41fed24f840b1dac59f6", size = 1402859, upload-time = "2026-01-01T17:32:27.215Z" }, + { url = "https://files.pythonhosted.org/packages/85/42/fe60b5f4473e12c72f977548e4028156f4d340b884c635ec6b063fe7e9a5/pynacl-1.6.2-cp38-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:68be3a09455743ff9505491220b64440ced8973fe930f270c8e07ccfa25b1f9e", size = 791926, upload-time = "2026-01-01T17:32:29.314Z" }, + { url = "https://files.pythonhosted.org/packages/fa/f9/e40e318c604259301cc091a2a63f237d9e7b424c4851cafaea4ea7c4834e/pynacl-1.6.2-cp38-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:8b097553b380236d51ed11356c953bf8ce36a29a3e596e934ecabe76c985a577", size = 1363101, upload-time = "2026-01-01T17:32:31.263Z" }, + { url = "https://files.pythonhosted.org/packages/48/47/e761c254f410c023a469284a9bc210933e18588ca87706ae93002c05114c/pynacl-1.6.2-cp38-abi3-win32.whl", hash = "sha256:5811c72b473b2f38f7e2a3dc4f8642e3a3e9b5e7317266e4ced1fba85cae41aa", size = 227421, upload-time = "2026-01-01T17:32:33.076Z" }, + { url = "https://files.pythonhosted.org/packages/41/ad/334600e8cacc7d86587fe5f565480fde569dfb487389c8e1be56ac21d8ac/pynacl-1.6.2-cp38-abi3-win_amd64.whl", hash = "sha256:62985f233210dee6548c223301b6c25440852e13d59a8b81490203c3227c5ba0", size = 239754, upload-time = "2026-01-01T17:32:34.557Z" }, + { url = "https://files.pythonhosted.org/packages/29/7d/5945b5af29534641820d3bd7b00962abbbdfee84ec7e19f0d5b3175f9a31/pynacl-1.6.2-cp38-abi3-win_arm64.whl", hash = "sha256:834a43af110f743a754448463e8fd61259cd4ab5bbedcf70f9dabad1d28a394c", size = 184801, upload-time = "2026-01-01T17:32:36.309Z" }, +] + +[[package]] +name = "pyyaml" +version = "6.0.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/05/8e/961c0007c59b8dd7729d542c61a4d537767a59645b82a0b521206e1e25c2/pyyaml-6.0.3.tar.gz", hash = "sha256:d76623373421df22fb4cf8817020cbb7ef15c725b9d5e45f17e189bfc384190f", size = 130960, upload-time = "2025-09-25T21:33:16.546Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/9d/8c/f4bd7f6465179953d3ac9bc44ac1a8a3e6122cf8ada906b4f96c60172d43/pyyaml-6.0.3-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:8d1fab6bb153a416f9aeb4b8763bc0f22a5586065f86f7664fc23339fc1c1fac", size = 181814, upload-time = "2025-09-25T21:32:35.712Z" }, + { url = "https://files.pythonhosted.org/packages/bd/9c/4d95bb87eb2063d20db7b60faa3840c1b18025517ae857371c4dd55a6b3a/pyyaml-6.0.3-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:34d5fcd24b8445fadc33f9cf348c1047101756fd760b4dacb5c3e99755703310", size = 173809, upload-time = "2025-09-25T21:32:36.789Z" }, + { url = "https://files.pythonhosted.org/packages/92/b5/47e807c2623074914e29dabd16cbbdd4bf5e9b2db9f8090fa64411fc5382/pyyaml-6.0.3-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:501a031947e3a9025ed4405a168e6ef5ae3126c59f90ce0cd6f2bfc477be31b7", size = 766454, upload-time = "2025-09-25T21:32:37.966Z" }, + { url = "https://files.pythonhosted.org/packages/02/9e/e5e9b168be58564121efb3de6859c452fccde0ab093d8438905899a3a483/pyyaml-6.0.3-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b3bc83488de33889877a0f2543ade9f70c67d66d9ebb4ac959502e12de895788", size = 836355, upload-time = "2025-09-25T21:32:39.178Z" }, + { url = "https://files.pythonhosted.org/packages/88/f9/16491d7ed2a919954993e48aa941b200f38040928474c9e85ea9e64222c3/pyyaml-6.0.3-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:c458b6d084f9b935061bc36216e8a69a7e293a2f1e68bf956dcd9e6cbcd143f5", size = 794175, upload-time = "2025-09-25T21:32:40.865Z" }, + { url = "https://files.pythonhosted.org/packages/dd/3f/5989debef34dc6397317802b527dbbafb2b4760878a53d4166579111411e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7c6610def4f163542a622a73fb39f534f8c101d690126992300bf3207eab9764", size = 755228, upload-time = "2025-09-25T21:32:42.084Z" }, + { url = "https://files.pythonhosted.org/packages/d7/ce/af88a49043cd2e265be63d083fc75b27b6ed062f5f9fd6cdc223ad62f03e/pyyaml-6.0.3-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:5190d403f121660ce8d1d2c1bb2ef1bd05b5f68533fc5c2ea899bd15f4399b35", size = 789194, upload-time = "2025-09-25T21:32:43.362Z" }, + { url = "https://files.pythonhosted.org/packages/23/20/bb6982b26a40bb43951265ba29d4c246ef0ff59c9fdcdf0ed04e0687de4d/pyyaml-6.0.3-cp314-cp314-win_amd64.whl", hash = "sha256:4a2e8cebe2ff6ab7d1050ecd59c25d4c8bd7e6f400f5f82b96557ac0abafd0ac", size = 156429, upload-time = "2025-09-25T21:32:57.844Z" }, + { url = "https://files.pythonhosted.org/packages/f4/f4/a4541072bb9422c8a883ab55255f918fa378ecf083f5b85e87fc2b4eda1b/pyyaml-6.0.3-cp314-cp314-win_arm64.whl", hash = "sha256:93dda82c9c22deb0a405ea4dc5f2d0cda384168e466364dec6255b293923b2f3", size = 143912, upload-time = "2025-09-25T21:32:59.247Z" }, + { url = "https://files.pythonhosted.org/packages/7c/f9/07dd09ae774e4616edf6cda684ee78f97777bdd15847253637a6f052a62f/pyyaml-6.0.3-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:02893d100e99e03eda1c8fd5c441d8c60103fd175728e23e431db1b589cf5ab3", size = 189108, upload-time = "2025-09-25T21:32:44.377Z" }, + { url = "https://files.pythonhosted.org/packages/4e/78/8d08c9fb7ce09ad8c38ad533c1191cf27f7ae1effe5bb9400a46d9437fcf/pyyaml-6.0.3-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c1ff362665ae507275af2853520967820d9124984e0f7466736aea23d8611fba", size = 183641, upload-time = "2025-09-25T21:32:45.407Z" }, + { url = "https://files.pythonhosted.org/packages/7b/5b/3babb19104a46945cf816d047db2788bcaf8c94527a805610b0289a01c6b/pyyaml-6.0.3-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:6adc77889b628398debc7b65c073bcb99c4a0237b248cacaf3fe8a557563ef6c", size = 831901, upload-time = "2025-09-25T21:32:48.83Z" }, + { url = "https://files.pythonhosted.org/packages/8b/cc/dff0684d8dc44da4d22a13f35f073d558c268780ce3c6ba1b87055bb0b87/pyyaml-6.0.3-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:a80cb027f6b349846a3bf6d73b5e95e782175e52f22108cfa17876aaeff93702", size = 861132, upload-time = "2025-09-25T21:32:50.149Z" }, + { url = "https://files.pythonhosted.org/packages/b1/5e/f77dc6b9036943e285ba76b49e118d9ea929885becb0a29ba8a7c75e29fe/pyyaml-6.0.3-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:00c4bdeba853cc34e7dd471f16b4114f4162dc03e6b7afcc2128711f0eca823c", size = 839261, upload-time = "2025-09-25T21:32:51.808Z" }, + { url = "https://files.pythonhosted.org/packages/ce/88/a9db1376aa2a228197c58b37302f284b5617f56a5d959fd1763fb1675ce6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:66e1674c3ef6f541c35191caae2d429b967b99e02040f5ba928632d9a7f0f065", size = 805272, upload-time = "2025-09-25T21:32:52.941Z" }, + { url = "https://files.pythonhosted.org/packages/da/92/1446574745d74df0c92e6aa4a7b0b3130706a4142b2d1a5869f2eaa423c6/pyyaml-6.0.3-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:16249ee61e95f858e83976573de0f5b2893b3677ba71c9dd36b9cf8be9ac6d65", size = 829923, upload-time = "2025-09-25T21:32:54.537Z" }, + { url = "https://files.pythonhosted.org/packages/f0/7a/1c7270340330e575b92f397352af856a8c06f230aa3e76f86b39d01b416a/pyyaml-6.0.3-cp314-cp314t-win_amd64.whl", hash = "sha256:4ad1906908f2f5ae4e5a8ddfce73c320c2a1429ec52eafd27138b7f1cbe341c9", size = 174062, upload-time = "2025-09-25T21:32:55.767Z" }, + { url = "https://files.pythonhosted.org/packages/f1/12/de94a39c2ef588c7e6455cfbe7343d3b2dc9d6b6b2f40c4c6565744c873d/pyyaml-6.0.3-cp314-cp314t-win_arm64.whl", hash = "sha256:ebc55a14a21cb14062aa4162f906cd962b28e2e9ea38f9b4391244cd8de4ae0b", size = 149341, upload-time = "2025-09-25T21:32:56.828Z" }, +] + +[[package]] +name = "regex" +version = "2026.2.28" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8b/71/41455aa99a5a5ac1eaf311f5d8efd9ce6433c03ac1e0962de163350d0d97/regex-2026.2.28.tar.gz", hash = "sha256:a729e47d418ea11d03469f321aaf67cdee8954cde3ff2cf8403ab87951ad10f2", size = 415184, upload-time = "2026-02-28T02:19:42.792Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cf/03/691015f7a7cb1ed6dacb2ea5de5682e4858e05a4c5506b2839cd533bbcd6/regex-2026.2.28-cp314-cp314-macosx_10_13_universal2.whl", hash = "sha256:78454178c7df31372ea737996fb7f36b3c2c92cccc641d251e072478afb4babc", size = 489497, upload-time = "2026-02-28T02:18:30.889Z" }, + { url = "https://files.pythonhosted.org/packages/c6/ba/8db8fd19afcbfa0e1036eaa70c05f20ca8405817d4ad7a38a6b4c2f031ac/regex-2026.2.28-cp314-cp314-macosx_10_13_x86_64.whl", hash = "sha256:5d10303dd18cedfd4d095543998404df656088240bcfd3cd20a8f95b861f74bd", size = 291295, upload-time = "2026-02-28T02:18:33.426Z" }, + { url = "https://files.pythonhosted.org/packages/5a/79/9aa0caf089e8defef9b857b52fc53801f62ff868e19e5c83d4a96612eba1/regex-2026.2.28-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:19a9c9e0a8f24f39d575a6a854d516b48ffe4cbdcb9de55cb0570a032556ecff", size = 289275, upload-time = "2026-02-28T02:18:35.247Z" }, + { url = "https://files.pythonhosted.org/packages/eb/26/ee53117066a30ef9c883bf1127eece08308ccf8ccd45c45a966e7a665385/regex-2026.2.28-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:09500be324f49b470d907b3ef8af9afe857f5cca486f853853f7945ddbf75911", size = 797176, upload-time = "2026-02-28T02:18:37.15Z" }, + { url = "https://files.pythonhosted.org/packages/05/1b/67fb0495a97259925f343ae78b5d24d4a6624356ae138b57f18bd43006e4/regex-2026.2.28-cp314-cp314-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:fb1c4ff62277d87a7335f2c1ea4e0387b8f2b3ad88a64efd9943906aafad4f33", size = 863813, upload-time = "2026-02-28T02:18:39.478Z" }, + { url = "https://files.pythonhosted.org/packages/a0/1d/93ac9bbafc53618091c685c7ed40239a90bf9f2a82c983f0baa97cb7ae07/regex-2026.2.28-cp314-cp314-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:b8b3f1be1738feadc69f62daa250c933e85c6f34fa378f54a7ff43807c1b9117", size = 908678, upload-time = "2026-02-28T02:18:41.619Z" }, + { url = "https://files.pythonhosted.org/packages/c7/7a/a8f5e0561702b25239846a16349feece59712ae20598ebb205580332a471/regex-2026.2.28-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:dc8ed8c3f41c27acb83f7b6a9eb727a73fc6663441890c5cb3426a5f6a91ce7d", size = 801528, upload-time = "2026-02-28T02:18:43.624Z" }, + { url = "https://files.pythonhosted.org/packages/96/5d/ed6d4cbde80309854b1b9f42d9062fee38ade15f7eb4909f6ef2440403b5/regex-2026.2.28-cp314-cp314-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:fa539be029844c0ce1114762d2952ab6cfdd7c7c9bd72e0db26b94c3c36dcc5a", size = 775373, upload-time = "2026-02-28T02:18:46.102Z" }, + { url = "https://files.pythonhosted.org/packages/6a/e9/6e53c34e8068b9deec3e87210086ecb5b9efebdefca6b0d3fa43d66dcecb/regex-2026.2.28-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:7900157786428a79615a8264dac1f12c9b02957c473c8110c6b1f972dcecaddf", size = 784859, upload-time = "2026-02-28T02:18:48.269Z" }, + { url = "https://files.pythonhosted.org/packages/48/3c/736e1c7ca7f0dcd2ae33819888fdc69058a349b7e5e84bc3e2f296bbf794/regex-2026.2.28-cp314-cp314-musllinux_1_2_ppc64le.whl", hash = "sha256:0b1d2b07614d95fa2bf8a63fd1e98bd8fa2b4848dc91b1efbc8ba219fdd73952", size = 857813, upload-time = "2026-02-28T02:18:50.576Z" }, + { url = "https://files.pythonhosted.org/packages/6e/7c/48c4659ad9da61f58e79dbe8c05223e0006696b603c16eb6b5cbfbb52c27/regex-2026.2.28-cp314-cp314-musllinux_1_2_riscv64.whl", hash = "sha256:b389c61aa28a79c2e0527ac36da579869c2e235a5b208a12c5b5318cda2501d8", size = 763705, upload-time = "2026-02-28T02:18:52.59Z" }, + { url = "https://files.pythonhosted.org/packages/cf/a1/bc1c261789283128165f71b71b4b221dd1b79c77023752a6074c102f18d8/regex-2026.2.28-cp314-cp314-musllinux_1_2_s390x.whl", hash = "sha256:f467cb602f03fbd1ab1908f68b53c649ce393fde056628dc8c7e634dab6bfc07", size = 848734, upload-time = "2026-02-28T02:18:54.595Z" }, + { url = "https://files.pythonhosted.org/packages/10/d8/979407faf1397036e25a5ae778157366a911c0f382c62501009f4957cf86/regex-2026.2.28-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:e8c8cb2deba42f5ec1ede46374e990f8adc5e6456a57ac1a261b19be6f28e4e6", size = 789871, upload-time = "2026-02-28T02:18:57.34Z" }, + { url = "https://files.pythonhosted.org/packages/03/23/da716821277115fcb1f4e3de1e5dc5023a1e6533598c486abf5448612579/regex-2026.2.28-cp314-cp314-win32.whl", hash = "sha256:9036b400b20e4858d56d117108d7813ed07bb7803e3eed766675862131135ca6", size = 271825, upload-time = "2026-02-28T02:18:59.202Z" }, + { url = "https://files.pythonhosted.org/packages/91/ff/90696f535d978d5f16a52a419be2770a8d8a0e7e0cfecdbfc31313df7fab/regex-2026.2.28-cp314-cp314-win_amd64.whl", hash = "sha256:1d367257cd86c1cbb97ea94e77b373a0bbc2224976e247f173d19e8f18b4afa7", size = 280548, upload-time = "2026-02-28T02:19:01.049Z" }, + { url = "https://files.pythonhosted.org/packages/69/f9/5e1b5652fc0af3fcdf7677e7df3ad2a0d47d669b34ac29a63bb177bb731b/regex-2026.2.28-cp314-cp314-win_arm64.whl", hash = "sha256:5e68192bb3a1d6fb2836da24aa494e413ea65853a21505e142e5b1064a595f3d", size = 273444, upload-time = "2026-02-28T02:19:03.255Z" }, + { url = "https://files.pythonhosted.org/packages/d3/eb/8389f9e940ac89bcf58d185e230a677b4fd07c5f9b917603ad5c0f8fa8fe/regex-2026.2.28-cp314-cp314t-macosx_10_13_universal2.whl", hash = "sha256:a5dac14d0872eeb35260a8e30bac07ddf22adc1e3a0635b52b02e180d17c9c7e", size = 492546, upload-time = "2026-02-28T02:19:05.378Z" }, + { url = "https://files.pythonhosted.org/packages/7b/c7/09441d27ce2a6fa6a61ea3150ea4639c1dcda9b31b2ea07b80d6937b24dd/regex-2026.2.28-cp314-cp314t-macosx_10_13_x86_64.whl", hash = "sha256:ec0c608b7a7465ffadb344ed7c987ff2f11ee03f6a130b569aa74d8a70e8333c", size = 292986, upload-time = "2026-02-28T02:19:07.24Z" }, + { url = "https://files.pythonhosted.org/packages/fb/69/4144b60ed7760a6bd235e4087041f487aa4aa62b45618ce018b0c14833ea/regex-2026.2.28-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c7815afb0ca45456613fdaf60ea9c993715511c8d53a83bc468305cbc0ee23c7", size = 291518, upload-time = "2026-02-28T02:19:09.698Z" }, + { url = "https://files.pythonhosted.org/packages/2d/be/77e5426cf5948c82f98c53582009ca9e94938c71f73a8918474f2e2990bb/regex-2026.2.28-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b059e71ec363968671693a78c5053bd9cb2fe410f9b8e4657e88377ebd603a2e", size = 809464, upload-time = "2026-02-28T02:19:12.494Z" }, + { url = "https://files.pythonhosted.org/packages/45/99/2c8c5ac90dc7d05c6e7d8e72c6a3599dc08cd577ac476898e91ca787d7f1/regex-2026.2.28-cp314-cp314t-manylinux2014_ppc64le.manylinux_2_17_ppc64le.manylinux_2_28_ppc64le.whl", hash = "sha256:b8cf76f1a29f0e99dcfd7aef1551a9827588aae5a737fe31442021165f1920dc", size = 869553, upload-time = "2026-02-28T02:19:15.151Z" }, + { url = "https://files.pythonhosted.org/packages/53/34/daa66a342f0271e7737003abf6c3097aa0498d58c668dbd88362ef94eb5d/regex-2026.2.28-cp314-cp314t-manylinux2014_s390x.manylinux_2_17_s390x.manylinux_2_28_s390x.whl", hash = "sha256:180e08a435a0319e6a4821c3468da18dc7001987e1c17ae1335488dfe7518dd8", size = 915289, upload-time = "2026-02-28T02:19:17.331Z" }, + { url = "https://files.pythonhosted.org/packages/c5/c7/e22c2aaf0a12e7e22ab19b004bb78d32ca1ecc7ef245949935463c5567de/regex-2026.2.28-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1e496956106fd59ba6322a8ea17141a27c5040e5ee8f9433ae92d4e5204462a0", size = 812156, upload-time = "2026-02-28T02:19:20.011Z" }, + { url = "https://files.pythonhosted.org/packages/7f/bb/2dc18c1efd9051cf389cd0d7a3a4d90f6804b9fff3a51b5dc3c85b935f71/regex-2026.2.28-cp314-cp314t-manylinux_2_31_riscv64.manylinux_2_39_riscv64.whl", hash = "sha256:bba2b18d70eeb7b79950f12f633beeecd923f7c9ad6f6bae28e59b4cb3ab046b", size = 782215, upload-time = "2026-02-28T02:19:22.047Z" }, + { url = "https://files.pythonhosted.org/packages/17/1e/9e4ec9b9013931faa32226ec4aa3c71fe664a6d8a2b91ac56442128b332f/regex-2026.2.28-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:6db7bfae0f8a2793ff1f7021468ea55e2699d0790eb58ee6ab36ae43aa00bc5b", size = 798925, upload-time = "2026-02-28T02:19:24.173Z" }, + { url = "https://files.pythonhosted.org/packages/71/57/a505927e449a9ccb41e2cc8d735e2abe3444b0213d1cf9cb364a8c1f2524/regex-2026.2.28-cp314-cp314t-musllinux_1_2_ppc64le.whl", hash = "sha256:d0b02e8b7e5874b48ae0f077ecca61c1a6a9f9895e9c6dfb191b55b242862033", size = 864701, upload-time = "2026-02-28T02:19:26.376Z" }, + { url = "https://files.pythonhosted.org/packages/a6/ad/c62cb60cdd93e13eac5b3d9d6bd5d284225ed0e3329426f94d2552dd7cca/regex-2026.2.28-cp314-cp314t-musllinux_1_2_riscv64.whl", hash = "sha256:25b6eb660c5cf4b8c3407a1ed462abba26a926cc9965e164268a3267bcc06a43", size = 770899, upload-time = "2026-02-28T02:19:29.38Z" }, + { url = "https://files.pythonhosted.org/packages/3c/5a/874f861f5c3d5ab99633e8030dee1bc113db8e0be299d1f4b07f5b5ec349/regex-2026.2.28-cp314-cp314t-musllinux_1_2_s390x.whl", hash = "sha256:5a932ea8ad5d0430351ff9c76c8db34db0d9f53c1d78f06022a21f4e290c5c18", size = 854727, upload-time = "2026-02-28T02:19:31.494Z" }, + { url = "https://files.pythonhosted.org/packages/6b/ca/d2c03b0efde47e13db895b975b2be6a73ed90b8ba963677927283d43bf74/regex-2026.2.28-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:1c2c95e1a2b0f89d01e821ff4de1be4b5d73d1f4b0bf679fa27c1ad8d2327f1a", size = 800366, upload-time = "2026-02-28T02:19:34.248Z" }, + { url = "https://files.pythonhosted.org/packages/14/bd/ee13b20b763b8989f7c75d592bfd5de37dc1181814a2a2747fedcf97e3ba/regex-2026.2.28-cp314-cp314t-win32.whl", hash = "sha256:bbb882061f742eb5d46f2f1bd5304055be0a66b783576de3d7eef1bed4778a6e", size = 274936, upload-time = "2026-02-28T02:19:36.313Z" }, + { url = "https://files.pythonhosted.org/packages/cb/e7/d8020e39414c93af7f0d8688eabcecece44abfd5ce314b21dfda0eebd3d8/regex-2026.2.28-cp314-cp314t-win_amd64.whl", hash = "sha256:6591f281cb44dc13de9585b552cec6fc6cf47fb2fe7a48892295ee9bc4a612f9", size = 284779, upload-time = "2026-02-28T02:19:38.625Z" }, + { url = "https://files.pythonhosted.org/packages/13/c0/ad225f4a405827486f1955283407cf758b6d2fb966712644c5f5aef33d1b/regex-2026.2.28-cp314-cp314t-win_arm64.whl", hash = "sha256:dee50f1be42222f89767b64b283283ef963189da0dda4a515aa54a5563c62dec", size = 275010, upload-time = "2026-02-28T02:19:40.65Z" }, +] + +[[package]] +name = "requests" +version = "2.32.5" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "certifi" }, + { name = "charset-normalizer" }, + { name = "idna" }, + { name = "urllib3" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/c9/74/b3ff8e6c8446842c3f5c837e9c3dfcfe2018ea6ecef224c710c85ef728f4/requests-2.32.5.tar.gz", hash = "sha256:dbba0bac56e100853db0ea71b82b4dfd5fe2bf6d3754a8893c3af500cec7d7cf", size = 134517, upload-time = "2025-08-18T20:46:02.573Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/1e/db/4254e3eabe8020b458f1a747140d32277ec7a271daf1d235b70dc0b4e6e3/requests-2.32.5-py3-none-any.whl", hash = "sha256:2462f94637a34fd532264295e186976db0f5d453d1cdd31473c85a6a161affb6", size = 64738, upload-time = "2025-08-18T20:46:00.542Z" }, +] + +[[package]] +name = "rich" +version = "14.3.3" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "markdown-it-py" }, + { name = "pygments" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/b3/c6/f3b320c27991c46f43ee9d856302c70dc2d0fb2dba4842ff739d5f46b393/rich-14.3.3.tar.gz", hash = "sha256:b8daa0b9e4eef54dd8cf7c86c03713f53241884e814f4e2f5fb342fe520f639b", size = 230582, upload-time = "2026-02-19T17:23:12.474Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/14/25/b208c5683343959b670dc001595f2f3737e051da617f66c31f7c4fa93abc/rich-14.3.3-py3-none-any.whl", hash = "sha256:793431c1f8619afa7d3b52b2cdec859562b950ea0d4b6b505397612db8d5362d", size = 310458, upload-time = "2026-02-19T17:23:13.732Z" }, +] + +[[package]] +name = "rich-rst" +version = "1.3.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "docutils" }, + { name = "rich" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/bc/6d/a506aaa4a9eaa945ed8ab2b7347859f53593864289853c5d6d62b77246e0/rich_rst-1.3.2.tar.gz", hash = "sha256:a1196fdddf1e364b02ec68a05e8ff8f6914fee10fbca2e6b6735f166bb0da8d4", size = 14936, upload-time = "2025-10-14T16:49:45.332Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/13/2f/b4530fbf948867702d0a3f27de4a6aab1d156f406d72852ab902c4d04de9/rich_rst-1.3.2-py3-none-any.whl", hash = "sha256:a99b4907cbe118cf9d18b0b44de272efa61f15117c61e39ebdc431baf5df722a", size = 12567, upload-time = "2025-10-14T16:49:42.953Z" }, +] + +[[package]] +name = "ruamel-yaml" +version = "0.19.1" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/3b/ebda527b56beb90cb7652cb1c7e4f91f48649fbcd8d2eb2fb6e77cd3329b/ruamel_yaml-0.19.1.tar.gz", hash = "sha256:53eb66cd27849eff968ebf8f0bf61f46cdac2da1d1f3576dd4ccee9b25c31993", size = 142709, upload-time = "2026-01-02T16:50:31.84Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b8/0c/51f6841f1d84f404f92463fc2b1ba0da357ca1e3db6b7fbda26956c3b82a/ruamel_yaml-0.19.1-py3-none-any.whl", hash = "sha256:27592957fedf6e0b62f281e96effd28043345e0e66001f97683aa9a40c667c93", size = 118102, upload-time = "2026-01-02T16:50:29.201Z" }, +] + +[[package]] +name = "ruff" +version = "0.15.5" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/77/9b/840e0039e65fcf12758adf684d2289024d6140cde9268cc59887dc55189c/ruff-0.15.5.tar.gz", hash = "sha256:7c3601d3b6d76dce18c5c824fc8d06f4eef33d6df0c21ec7799510cde0f159a2", size = 4574214, upload-time = "2026-03-05T20:06:34.946Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/47/20/5369c3ce21588c708bcbe517a8fbe1a8dfdb5dfd5137e14790b1da71612c/ruff-0.15.5-py3-none-linux_armv6l.whl", hash = "sha256:4ae44c42281f42e3b06b988e442d344a5b9b72450ff3c892e30d11b29a96a57c", size = 10478185, upload-time = "2026-03-05T20:06:29.093Z" }, + { url = "https://files.pythonhosted.org/packages/44/ed/e81dd668547da281e5dce710cf0bc60193f8d3d43833e8241d006720e42b/ruff-0.15.5-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:6edd3792d408ebcf61adabc01822da687579a1a023f297618ac27a5b51ef0080", size = 10859201, upload-time = "2026-03-05T20:06:32.632Z" }, + { url = "https://files.pythonhosted.org/packages/c4/8f/533075f00aaf19b07c5cd6aa6e5d89424b06b3b3f4583bfa9c640a079059/ruff-0.15.5-py3-none-macosx_11_0_arm64.whl", hash = "sha256:89f463f7c8205a9f8dea9d658d59eff49db05f88f89cc3047fb1a02d9f344010", size = 10184752, upload-time = "2026-03-05T20:06:40.312Z" }, + { url = "https://files.pythonhosted.org/packages/66/0e/ba49e2c3fa0395b3152bad634c7432f7edfc509c133b8f4529053ff024fb/ruff-0.15.5-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:ba786a8295c6574c1116704cf0b9e6563de3432ac888d8f83685654fe528fd65", size = 10534857, upload-time = "2026-03-05T20:06:19.581Z" }, + { url = "https://files.pythonhosted.org/packages/59/71/39234440f27a226475a0659561adb0d784b4d247dfe7f43ffc12dd02e288/ruff-0.15.5-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:fd4b801e57955fe9f02b31d20375ab3a5c4415f2e5105b79fb94cf2642c91440", size = 10309120, upload-time = "2026-03-05T20:06:00.435Z" }, + { url = "https://files.pythonhosted.org/packages/f5/87/4140aa86a93df032156982b726f4952aaec4a883bb98cb6ef73c347da253/ruff-0.15.5-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:391f7c73388f3d8c11b794dbbc2959a5b5afe66642c142a6effa90b45f6f5204", size = 11047428, upload-time = "2026-03-05T20:05:51.867Z" }, + { url = "https://files.pythonhosted.org/packages/5a/f7/4953e7e3287676f78fbe85e3a0ca414c5ca81237b7575bdadc00229ac240/ruff-0.15.5-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:8dc18f30302e379fe1e998548b0f5e9f4dff907f52f73ad6da419ea9c19d66c8", size = 11914251, upload-time = "2026-03-05T20:06:22.887Z" }, + { url = "https://files.pythonhosted.org/packages/77/46/0f7c865c10cf896ccf5a939c3e84e1cfaeed608ff5249584799a74d33835/ruff-0.15.5-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1cc6e7f90087e2d27f98dc34ed1b3ab7c8f0d273cc5431415454e22c0bd2a681", size = 11333801, upload-time = "2026-03-05T20:05:57.168Z" }, + { url = "https://files.pythonhosted.org/packages/d3/01/a10fe54b653061585e655f5286c2662ebddb68831ed3eaebfb0eb08c0a16/ruff-0.15.5-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:c1cb7169f53c1ddb06e71a9aebd7e98fc0fea936b39afb36d8e86d36ecc2636a", size = 11206821, upload-time = "2026-03-05T20:06:03.441Z" }, + { url = "https://files.pythonhosted.org/packages/7a/0d/2132ceaf20c5e8699aa83da2706ecb5c5dcdf78b453f77edca7fb70f8a93/ruff-0.15.5-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:9b037924500a31ee17389b5c8c4d88874cc6ea8e42f12e9c61a3d754ff72f1ca", size = 11133326, upload-time = "2026-03-05T20:06:25.655Z" }, + { url = "https://files.pythonhosted.org/packages/72/cb/2e5259a7eb2a0f87c08c0fe5bf5825a1e4b90883a52685524596bfc93072/ruff-0.15.5-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:65bb414e5b4eadd95a8c1e4804f6772bbe8995889f203a01f77ddf2d790929dd", size = 10510820, upload-time = "2026-03-05T20:06:37.79Z" }, + { url = "https://files.pythonhosted.org/packages/ff/20/b67ce78f9e6c59ffbdb5b4503d0090e749b5f2d31b599b554698a80d861c/ruff-0.15.5-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:d20aa469ae3b57033519c559e9bc9cd9e782842e39be05b50e852c7c981fa01d", size = 10302395, upload-time = "2026-03-05T20:05:54.504Z" }, + { url = "https://files.pythonhosted.org/packages/5f/e5/719f1acccd31b720d477751558ed74e9c88134adcc377e5e886af89d3072/ruff-0.15.5-py3-none-musllinux_1_2_i686.whl", hash = "sha256:15388dd28c9161cdb8eda68993533acc870aa4e646a0a277aa166de9ad5a8752", size = 10754069, upload-time = "2026-03-05T20:06:06.422Z" }, + { url = "https://files.pythonhosted.org/packages/c3/9c/d1db14469e32d98f3ca27079dbd30b7b44dbb5317d06ab36718dee3baf03/ruff-0.15.5-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:b30da330cbd03bed0c21420b6b953158f60c74c54c5f4c1dabbdf3a57bf355d2", size = 11304315, upload-time = "2026-03-05T20:06:10.867Z" }, + { url = "https://files.pythonhosted.org/packages/28/3a/950367aee7c69027f4f422059227b290ed780366b6aecee5de5039d50fa8/ruff-0.15.5-py3-none-win32.whl", hash = "sha256:732e5ee1f98ba5b3679029989a06ca39a950cced52143a0ea82a2102cb592b74", size = 10551676, upload-time = "2026-03-05T20:06:13.705Z" }, + { url = "https://files.pythonhosted.org/packages/b8/00/bf077a505b4e649bdd3c47ff8ec967735ce2544c8e4a43aba42ee9bf935d/ruff-0.15.5-py3-none-win_amd64.whl", hash = "sha256:821d41c5fa9e19117616c35eaa3f4b75046ec76c65e7ae20a333e9a8696bc7fe", size = 11678972, upload-time = "2026-03-05T20:06:45.379Z" }, + { url = "https://files.pythonhosted.org/packages/fe/4e/cd76eca6db6115604b7626668e891c9dd03330384082e33662fb0f113614/ruff-0.15.5-py3-none-win_arm64.whl", hash = "sha256:b498d1c60d2fe5c10c45ec3f698901065772730b411f164ae270bb6bfcc4740b", size = 10965572, upload-time = "2026-03-05T20:06:16.984Z" }, +] + +[[package]] +name = "six" +version = "1.17.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/e7/b2c673351809dca68a0e064b6af791aa332cf192da575fd474ed7d6f16a2/six-1.17.0.tar.gz", hash = "sha256:ff70335d468e7eb6ec65b95b99d3a2836546063f63acc5171de367e834932a81", size = 34031, upload-time = "2024-12-04T17:35:28.174Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/b7/ce/149a00dd41f10bc29e5921b496af8b574d8413afcd5e30dfa0ed46c2cc5e/six-1.17.0-py2.py3-none-any.whl", hash = "sha256:4721f391ed90541fddacab5acf947aa0d3dc7d27b2e1e8eda2be8970586c3274", size = 11050, upload-time = "2024-12-04T17:35:26.475Z" }, +] + +[[package]] +name = "sphinx-lint" +version = "1.0.2" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "polib" }, + { name = "regex" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/8a/19/9258497fee6e2a0bdb93e8ecea6ef6864afb5d83e996a1606a853f96c658/sphinx_lint-1.0.2.tar.gz", hash = "sha256:4e7fc12f44f750b0006eaad237d7db9b1d8aba92adda9c838af891654b371d35", size = 36870, upload-time = "2025-11-19T08:28:12.269Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c0/62/f29a2988ff706ac01d3c63d0b4cc4ed62d2c83b447916e0317790ca156cf/sphinx_lint-1.0.2-py3-none-any.whl", hash = "sha256:edcd0fa4d916386c5a3ef7ef0f5136f0bb4a15feefc83c1068ba15bc16eec652", size = 20670, upload-time = "2025-11-19T08:28:10.656Z" }, +] + +[[package]] +name = "sync-with-uv" +version = "0.5.0" +source = { registry = "https://pypi.org/simple" } +dependencies = [ + { name = "colorama" }, + { name = "cyclopts" }, + { name = "tomli" }, +] +sdist = { url = "https://files.pythonhosted.org/packages/5f/95/d187e24af4722f83bfef5fe7e050b183c1ae4a4064eb07bd15f7d153dbd5/sync_with_uv-0.5.0.tar.gz", hash = "sha256:236f9029e09c1edf0b13231be7f410119188ff77556069b4bed1c11947bb942c", size = 97828, upload-time = "2025-11-18T18:52:10.12Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/24/96/b5413e21f78d9e477a5a6c327812e74237d6d254ed0b8bd03bbc7acd3b2e/sync_with_uv-0.5.0-py3-none-any.whl", hash = "sha256:6c05052bfa27552e159be61bd191d4d7b04b5d7621e7c126f9a67bf4a6881809", size = 9638, upload-time = "2025-11-18T18:52:08.552Z" }, +] + +[[package]] +name = "tomli" +version = "2.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/82/30/31573e9457673ab10aa432461bee537ce6cef177667deca369efb79df071/tomli-2.4.0.tar.gz", hash = "sha256:aa89c3f6c277dd275d8e243ad24f3b5e701491a860d5121f2cdd399fbb31fc9c", size = 17477, upload-time = "2026-01-11T11:22:38.165Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/f3/c4/84047a97eb1004418bc10bdbcfebda209fca6338002eba2dc27cc6d13563/tomli-2.4.0-cp314-cp314-macosx_10_15_x86_64.whl", hash = "sha256:26ab906a1eb794cd4e103691daa23d95c6919cc2fa9160000ac02370cc9dd3f6", size = 154725, upload-time = "2026-01-11T11:22:17.269Z" }, + { url = "https://files.pythonhosted.org/packages/a8/5d/d39038e646060b9d76274078cddf146ced86dc2b9e8bbf737ad5983609a0/tomli-2.4.0-cp314-cp314-macosx_11_0_arm64.whl", hash = "sha256:20cedb4ee43278bc4f2fee6cb50daec836959aadaf948db5172e776dd3d993fc", size = 148901, upload-time = "2026-01-11T11:22:18.287Z" }, + { url = "https://files.pythonhosted.org/packages/73/e5/383be1724cb30f4ce44983d249645684a48c435e1cd4f8b5cded8a816d3c/tomli-2.4.0-cp314-cp314-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:39b0b5d1b6dd03684b3fb276407ebed7090bbec989fa55838c98560c01113b66", size = 243375, upload-time = "2026-01-11T11:22:19.154Z" }, + { url = "https://files.pythonhosted.org/packages/31/f0/bea80c17971c8d16d3cc109dc3585b0f2ce1036b5f4a8a183789023574f2/tomli-2.4.0-cp314-cp314-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:a26d7ff68dfdb9f87a016ecfd1e1c2bacbe3108f4e0f8bcd2228ef9a766c787d", size = 250639, upload-time = "2026-01-11T11:22:20.168Z" }, + { url = "https://files.pythonhosted.org/packages/2c/8f/2853c36abbb7608e3f945d8a74e32ed3a74ee3a1f468f1ffc7d1cb3abba6/tomli-2.4.0-cp314-cp314-musllinux_1_2_aarch64.whl", hash = "sha256:20ffd184fb1df76a66e34bd1b36b4a4641bd2b82954befa32fe8163e79f1a702", size = 246897, upload-time = "2026-01-11T11:22:21.544Z" }, + { url = "https://files.pythonhosted.org/packages/49/f0/6c05e3196ed5337b9fe7ea003e95fd3819a840b7a0f2bf5a408ef1dad8ed/tomli-2.4.0-cp314-cp314-musllinux_1_2_x86_64.whl", hash = "sha256:75c2f8bbddf170e8effc98f5e9084a8751f8174ea6ccf4fca5398436e0320bc8", size = 254697, upload-time = "2026-01-11T11:22:23.058Z" }, + { url = "https://files.pythonhosted.org/packages/f3/f5/2922ef29c9f2951883525def7429967fc4d8208494e5ab524234f06b688b/tomli-2.4.0-cp314-cp314-win32.whl", hash = "sha256:31d556d079d72db7c584c0627ff3a24c5d3fb4f730221d3444f3efb1b2514776", size = 98567, upload-time = "2026-01-11T11:22:24.033Z" }, + { url = "https://files.pythonhosted.org/packages/7b/31/22b52e2e06dd2a5fdbc3ee73226d763b184ff21fc24e20316a44ccc4d96b/tomli-2.4.0-cp314-cp314-win_amd64.whl", hash = "sha256:43e685b9b2341681907759cf3a04e14d7104b3580f808cfde1dfdb60ada85475", size = 108556, upload-time = "2026-01-11T11:22:25.378Z" }, + { url = "https://files.pythonhosted.org/packages/48/3d/5058dff3255a3d01b705413f64f4306a141a8fd7a251e5a495e3f192a998/tomli-2.4.0-cp314-cp314-win_arm64.whl", hash = "sha256:3d895d56bd3f82ddd6faaff993c275efc2ff38e52322ea264122d72729dca2b2", size = 96014, upload-time = "2026-01-11T11:22:26.138Z" }, + { url = "https://files.pythonhosted.org/packages/b8/4e/75dab8586e268424202d3a1997ef6014919c941b50642a1682df43204c22/tomli-2.4.0-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:5b5807f3999fb66776dbce568cc9a828544244a8eb84b84b9bafc080c99597b9", size = 163339, upload-time = "2026-01-11T11:22:27.143Z" }, + { url = "https://files.pythonhosted.org/packages/06/e3/b904d9ab1016829a776d97f163f183a48be6a4deb87304d1e0116a349519/tomli-2.4.0-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:c084ad935abe686bd9c898e62a02a19abfc9760b5a79bc29644463eaf2840cb0", size = 159490, upload-time = "2026-01-11T11:22:28.399Z" }, + { url = "https://files.pythonhosted.org/packages/e3/5a/fc3622c8b1ad823e8ea98a35e3c632ee316d48f66f80f9708ceb4f2a0322/tomli-2.4.0-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:0f2e3955efea4d1cfbcb87bc321e00dc08d2bcb737fd1d5e398af111d86db5df", size = 269398, upload-time = "2026-01-11T11:22:29.345Z" }, + { url = "https://files.pythonhosted.org/packages/fd/33/62bd6152c8bdd4c305ad9faca48f51d3acb2df1f8791b1477d46ff86e7f8/tomli-2.4.0-cp314-cp314t-manylinux2014_x86_64.manylinux_2_17_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:0e0fe8a0b8312acf3a88077a0802565cb09ee34107813bba1c7cd591fa6cfc8d", size = 276515, upload-time = "2026-01-11T11:22:30.327Z" }, + { url = "https://files.pythonhosted.org/packages/4b/ff/ae53619499f5235ee4211e62a8d7982ba9e439a0fb4f2f351a93d67c1dd2/tomli-2.4.0-cp314-cp314t-musllinux_1_2_aarch64.whl", hash = "sha256:413540dce94673591859c4c6f794dfeaa845e98bf35d72ed59636f869ef9f86f", size = 273806, upload-time = "2026-01-11T11:22:32.56Z" }, + { url = "https://files.pythonhosted.org/packages/47/71/cbca7787fa68d4d0a9f7072821980b39fbb1b6faeb5f5cf02f4a5559fa28/tomli-2.4.0-cp314-cp314t-musllinux_1_2_x86_64.whl", hash = "sha256:0dc56fef0e2c1c470aeac5b6ca8cc7b640bb93e92d9803ddaf9ea03e198f5b0b", size = 281340, upload-time = "2026-01-11T11:22:33.505Z" }, + { url = "https://files.pythonhosted.org/packages/f5/00/d595c120963ad42474cf6ee7771ad0d0e8a49d0f01e29576ee9195d9ecdf/tomli-2.4.0-cp314-cp314t-win32.whl", hash = "sha256:d878f2a6707cc9d53a1be1414bbb419e629c3d6e67f69230217bb663e76b5087", size = 108106, upload-time = "2026-01-11T11:22:34.451Z" }, + { url = "https://files.pythonhosted.org/packages/de/69/9aa0c6a505c2f80e519b43764f8b4ba93b5a0bbd2d9a9de6e2b24271b9a5/tomli-2.4.0-cp314-cp314t-win_amd64.whl", hash = "sha256:2add28aacc7425117ff6364fe9e06a183bb0251b03f986df0e78e974047571fd", size = 120504, upload-time = "2026-01-11T11:22:35.764Z" }, + { url = "https://files.pythonhosted.org/packages/b3/9f/f1668c281c58cfae01482f7114a4b88d345e4c140386241a1a24dcc9e7bc/tomli-2.4.0-cp314-cp314t-win_arm64.whl", hash = "sha256:2b1e3b80e1d5e52e40e9b924ec43d81570f0e7d09d11081b797bc4692765a3d4", size = 99561, upload-time = "2026-01-11T11:22:36.624Z" }, + { url = "https://files.pythonhosted.org/packages/23/d1/136eb2cb77520a31e1f64cbae9d33ec6df0d78bdf4160398e86eec8a8754/tomli-2.4.0-py3-none-any.whl", hash = "sha256:1f776e7d669ebceb01dee46484485f43a4048746235e683bcdffacdf1fb4785a", size = 14477, upload-time = "2026-01-11T11:22:37.446Z" }, +] + +[[package]] +name = "typing-extensions" +version = "4.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/94/1a15dd82efb362ac84269196e94cf00f187f7ed21c242792a923cdb1c61f/typing_extensions-4.15.0.tar.gz", hash = "sha256:0cea48d173cc12fa28ecabc3b837ea3cf6f38c6d1136f85cbaaf598984861466", size = 109391, upload-time = "2025-08-25T13:49:26.313Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/18/67/36e9267722cc04a6b9f15c7f3441c2363321a3ea07da7ae0c0707beb2a9c/typing_extensions-4.15.0-py3-none-any.whl", hash = "sha256:f0fa19c6845758ab08074a0cfa8b7aecb71c999ca73d62883bc25cc018c4e548", size = 44614, upload-time = "2025-08-25T13:49:24.86Z" }, +] + +[[package]] +name = "unidecode" +version = "1.4.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/94/7d/a8a765761bbc0c836e397a2e48d498305a865b70a8600fd7a942e85dcf63/Unidecode-1.4.0.tar.gz", hash = "sha256:ce35985008338b676573023acc382d62c264f307c8f7963733405add37ea2b23", size = 200149, upload-time = "2025-04-24T08:45:03.798Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/8f/b7/559f59d57d18b44c6d1250d2eeaa676e028b9c527431f5d0736478a73ba1/Unidecode-1.4.0-py3-none-any.whl", hash = "sha256:c3c7606c27503ad8d501270406e345ddb480a7b5f38827eafe4fa82a137f0021", size = 235837, upload-time = "2025-04-24T08:45:01.609Z" }, +] + +[[package]] +name = "urllib3" +version = "2.6.3" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/c7/24/5f1b3bdffd70275f6661c76461e25f024d5a38a46f04aaca912426a2b1d3/urllib3-2.6.3.tar.gz", hash = "sha256:1b62b6884944a57dbe321509ab94fd4d3b307075e0c2eae991ac71ee15ad38ed", size = 435556, upload-time = "2026-01-07T16:24:43.925Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/39/08/aaaad47bc4e9dc8c725e68f9d04865dbcb2052843ff09c97b08904852d84/urllib3-2.6.3-py3-none-any.whl", hash = "sha256:bf272323e553dfb2e87d9bfd225ca7b0f467b919d7bbd355436d3fd37cb0acd4", size = 131584, upload-time = "2026-01-07T16:24:42.685Z" }, +] From 9e29906259b0e97fb331b1925590409101842b5a Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 18:12:37 +0000 Subject: [PATCH 376/461] docs: Ignore venv directories --- manual/sphinx/conf.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/manual/sphinx/conf.py b/manual/sphinx/conf.py index 6046173aef..69ed52aeed 100755 --- a/manual/sphinx/conf.py +++ b/manual/sphinx/conf.py @@ -192,7 +192,7 @@ def __getattr__(cls, name): # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path -exclude_patterns = ["_build", "Thumbs.db", ".DS_Store"] +exclude_patterns = ["_build", "Thumbs.db", ".DS_Store", ".venv", "venv"] # The name of the Pygments (syntax highlighting) style to use. pygments_style = "sphinx" From 0f276272869b5413023601baedd5d630ed46274c Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 18:12:55 +0000 Subject: [PATCH 377/461] docs: Describe formatters and linters used in the project --- manual/sphinx/developer_docs/contributing.rst | 58 +++++++++++++++++++ requirements_dev.txt | 6 ++ 2 files changed, 64 insertions(+) create mode 100644 requirements_dev.txt diff --git a/manual/sphinx/developer_docs/contributing.rst b/manual/sphinx/developer_docs/contributing.rst index 4453f518b6..a7aca902d4 100644 --- a/manual/sphinx/developer_docs/contributing.rst +++ b/manual/sphinx/developer_docs/contributing.rst @@ -142,6 +142,45 @@ then they will be able to checkout your branch:: *Note*: If you have write access to the central BOUT-dev repository, you can push your branches there. +Running Tests +~~~~~~~~~~~~~ + +We run many tests and checks automatically on GitHub for a variety of build +configurations. See :ref:`sec-runtestsuite` for how to run them +locally. Running the full test suite can take some time, but it's a good idea to +run at least the unit tests locally when you're making changes. + +Install Pre-Commit Hooks (Optional but recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Along with the automated tests, we also run things like formatters and static +analysis. For frequent developers, we strongly recommend installing them locally +and building them into your regular workflow. You can install the majority of +our developer tools at once using `uv `_: + +.. code-block:: console + + uv sync --only-dev + +This will create a new virtual environment (typically under ``.venv``) and +install all the tools into that. You can then activate the virtual environment +and use the tools manually, or via integrations in your editor. + +We also have a `prek `_ config that can run some of +these automatically when you make commits using "pre-commit hooks". Install our +developer tools with ``uv`` as above, and then install the pre-commit hook: + +.. code-block:: console + + prek install + +This will then automatically call things like `clang-format +`_ and `ruff +`_ when you create a commit, and abort the commit if +there's a problem, for example if a formatter changes any files. For the +formatters, this usually just means staging any changes they make and trying +again. + Making changes, commits ~~~~~~~~~~~~~~~~~~~~~~~ @@ -189,6 +228,25 @@ These guidelines are intended to make code easier to read and therefore easier to understand. Being consistent in coding style also helps comprehension by reducing cognitive load. +We use various tools to enforce code style and quality: + +- `clang-format `_ for formatting + of C++ code +- `clang-tidy `_ for linting + and static analysis of C++ code +- `ruff `_ for formatting and linting of Python code +- `cmake-format `_ for formatting + CMake files +- `sphinx-lint `_ for linting + documentation pages + +You can install all of these tools using: + +.. code-block:: console + + pip install -r requirements_dev.txt + + Comments ~~~~~~~~ diff --git a/requirements_dev.txt b/requirements_dev.txt new file mode 100644 index 0000000000..ba0eeb91e2 --- /dev/null +++ b/requirements_dev.txt @@ -0,0 +1,6 @@ +clang-format~=22.0 +clang-tidy~=22.0 +cmakelang~=0.6 +pre-commit>=4.5.1 +ruff +sphinx-lint~=1.0 From da01a2611498c259e5d3edbe0d208a65a8dcb2a1 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 18:14:36 +0000 Subject: [PATCH 378/461] maint: Ignore noisy `clang-tidy` warning `cppcoreguidelines-pro-bounds-avoid-unchecked-container-access` is not really suitable for numerical code --- .clang-tidy | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.clang-tidy b/.clang-tidy index 121c3f3a60..6fc7fd19d9 100644 --- a/.clang-tidy +++ b/.clang-tidy @@ -1,5 +1,5 @@ --- -Checks: 'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-misc-non-private-member-variables-in-classes,-clang-analyzer-optin.mpi*,-bugprone-exception-escape,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-function-cognitive-complexity,-misc-no-recursion,-bugprone-easily-swappable-parameters,-readability-identifier-length' +Checks: 'clang-diagnostic-*,clang-analyzer-*,performance-*,readability-*,bugprone-*,clang-analyzer-*,cppcoreguidelines-*,mpi-*,misc-*,-readability-magic-numbers,-cppcoreguidelines-avoid-magic-numbers,-misc-non-private-member-variables-in-classes,-clang-analyzer-optin.mpi*,-bugprone-exception-escape,-cppcoreguidelines-pro-bounds-pointer-arithmetic,-readability-function-cognitive-complexity,-misc-no-recursion,-bugprone-easily-swappable-parameters,-readability-identifier-length,-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access' WarningsAsErrors: '' HeaderFilterRegex: '' FormatStyle: file @@ -29,6 +29,7 @@ These are all basically unavoidable in HPC numeric code: -readability-magic-numbers -cppcoreguidelines-avoid-magic-numbers -cppcoreguidelines-pro-bounds-pointer-arithmetic +-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access -readability-function-cognitive-complexity -bugprone-easily-swappable-parameters -readability-identifier-length From 8f24e1ee436385bd73d36b92919d28b14d46021a Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 18:15:31 +0000 Subject: [PATCH 379/461] maint: Enforce `const` on the left of types --- .clang-format | 1 + 1 file changed, 1 insertion(+) diff --git a/.clang-format b/.clang-format index a80c59bddd..51cf39683a 100644 --- a/.clang-format +++ b/.clang-format @@ -93,6 +93,7 @@ PenaltyBreakString: 1000 PenaltyExcessCharacter: 1000000 PenaltyReturnTypeOnItsOwnLine: 60 PointerAlignment: Left +QualifierAlignment: Left ReflowComments: false SortIncludes: true # SortUsingDeclarations: true From 64bf672bd3a3bc2d801fad29cff0acdf947a3ee1 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 18:15:52 +0000 Subject: [PATCH 380/461] maint: Add `clangd` config for editors --- .clangd | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .clangd diff --git a/.clangd b/.clangd new file mode 100644 index 0000000000..421b7e10d3 --- /dev/null +++ b/.clangd @@ -0,0 +1,6 @@ +Diagnostics: + MissingIncludes: Strict + ClangTidy: + FastCheckFilter: None + Includes: + IgnoreHeader: ["adios2/.*", "bits/.*", "cpptrace/.*", "petsc.*"] From d4692f03c670b2a22e505dc63a38c83d7634fe3b Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 18:17:14 +0000 Subject: [PATCH 381/461] CI: Don't add formatting commits to `.git-blame-ignore-revs` --- .github/workflows/auto-formatting.yml | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml index f54b012298..f49f9c65d0 100644 --- a/.github/workflows/auto-formatting.yml +++ b/.github/workflows/auto-formatting.yml @@ -38,7 +38,7 @@ jobs: pwd ls $HOME/.local/bin/ruff format . - + - name: Run cmake-format run: | pwd @@ -50,13 +50,3 @@ jobs: with: commit_message: "[bot] Apply format changes" skip_push: true - - - name: Ignore formatting - run: | - msg=$(git rev-list --format=%s --max-count=1 HEAD|tail -n -1) - test "$msg" = "[bot] Apply format changes" && git rev-parse HEAD >> .git-blame-ignore-revs || : - - - name: Commit formatting - uses: stefanzweifel/git-auto-commit-action@v7 - with: - commit_message: "[bot] Add last format changes commit to ignore file" From 47f93b19ed48e17cb8669f89c38a9395e3aff09c Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 11 Mar 2026 10:17:35 +0100 Subject: [PATCH 382/461] Fixup: always use proc for proc computation --- src/mesh/parallel/fci_comm.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index a9c574c430..562a9e5589 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -111,7 +111,7 @@ void GlobalField3DAccess::setup() { const auto piy = global2local_y.convert(gind.y()); const auto piz = global2local_z.convert(gind.z()); ASSERT3(piz.proc == 0); - const auto proc = mesh->getProcIndex(pix.proc, piy.index, piz.index); + const auto proc = mesh->getProcIndex(pix.proc, piy.proc, piz.proc); const auto& vec = toGet[proc]; const auto tofind = xyzlocal.convert(pix.index, piy.index, piz.index).ind; auto it = std::lower_bound(vec.begin(), vec.end(), tofind); From 58db33a02130e955be86a472d53a1f65282aa800 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 11 Mar 2026 10:26:05 +0100 Subject: [PATCH 383/461] Fixup rename --- src/mesh/parallel/fci_comm.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/parallel/fci_comm.cxx b/src/mesh/parallel/fci_comm.cxx index 562a9e5589..3d213ad6e4 100644 --- a/src/mesh/parallel/fci_comm.cxx +++ b/src/mesh/parallel/fci_comm.cxx @@ -77,10 +77,10 @@ void GlobalField3DAccess::setup() { // scaling at some point. ASSERT2(is_setup == false); #ifdef _OPENMP - for (auto& o_id : o_ids) { + for (auto& o_id : openmp_ids) { ids.merge(o_id); } - o_ids.clear(); + openmp_ids.clear(); #endif toGet.resize(static_cast(global2local_x.getNPE() * global2local_y.getNPE() * global2local_z.getNPE())); From 95f7f17e3e32e6d81063b9cd021053665e18809e Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 10 Mar 2026 20:54:48 +0000 Subject: [PATCH 384/461] CI: Run all formatters and linters through `prek` --- .github/workflows/auto-formatting.yml | 43 ++++++++------------------- 1 file changed, 12 insertions(+), 31 deletions(-) diff --git a/.github/workflows/auto-formatting.yml b/.github/workflows/auto-formatting.yml index f49f9c65d0..3802653082 100644 --- a/.github/workflows/auto-formatting.yml +++ b/.github/workflows/auto-formatting.yml @@ -15,38 +15,19 @@ jobs: ref: ${{ github.head_ref }} fetch-depth: 0 - # This also installs git-clang-format - - name: Install formatters - run: | - sudo apt install clang-format python3-pip python3-setuptools python3-wheel - pip3 install ruff cmake-format + - name: "Set up Python" + uses: actions/setup-python@v6 - - name: Version - run: | - python3 --version - $HOME/.local/bin/ruff --version - clang-format --version - $HOME/.local/bin/cmake-format --version - - - name: Run clang-format - id: format - run: - while ! git clang-format origin/${{ github.base_ref }} ; do git add . ; done + - name: Install uv + uses: astral-sh/setup-uv@v7 + with: + enable-cache: true - - name: Run ruff - run: | - pwd - ls - $HOME/.local/bin/ruff format . + - name: Install dev tools + run: uv sync --only-dev - - name: Run cmake-format - run: | - pwd - ls - $HOME/.local/bin/cmake-format -i $(find -name CMakeLists.txt) cmake/*.cmake + - name: Check prek versions are up-to-date + run: uv tool run sync-with-uv --check --diff - - name: Commit formatting - uses: stefanzweifel/git-auto-commit-action@v7 - with: - commit_message: "[bot] Apply format changes" - skip_push: true + - name: Run everything + run: uv tool run prek run --show-diff-on-failure --from-ref origin/${{ github.base_ref }} From 0b733971110313773116a97b74966c720aa4a027 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 09:38:26 +0000 Subject: [PATCH 385/461] CI: Temporarily remove CUDA build while we get it sorted --- .github/workflows/tests.yml | 36 ------------------------------------ 1 file changed, 36 deletions(-) diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index f44ebb87ec..3ea39bef01 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -232,39 +232,3 @@ jobs: shell: bash env: TRAVIS_BUILD_DIR: ${{ github.workspace }} - CUDA: - timeout-minutes: 60 - runs-on: ubuntu-latest - container: ghcr.io/ggeorgakoudis/boutdev-cuda:latest - env: - BOUT_TEST_DOWNLOAD_FLAGS: --retry-on-http-error=502,503,504 --tries 3 - steps: - - uses: actions/checkout@v6 - with: - submodules: true - - - name: Cache Zenodo test data - uses: actions/cache@v4 - with: - path: build/tests/integrated/test-fci-mpi/grid.fci.nc - # If we update the test, invalidate the cache - key: zenodo-data-${{ hashFiles('tests/integrated/test-fci-mpi/CMakeLists.txt') }} - - - name: Build minimal CUDA 12.2 @ GCC9.4.0 @ Ubuntu 20.04 - run: | - . /spack/share/spack/setup-env.sh - spack env activate -p /spack-env - git config --global --add safe.directory $GITHUB_WORKSPACE - rm -rf build - cmake -S $GITHUB_WORKSPACE -B build \ - -DCMAKE_C_COMPILER=gcc \ - -DCMAKE_CXX_COMPILER=g++ \ - -DBOUT_ENABLE_RAJA=on \ - -DBOUT_ENABLE_UMPIRE=on \ - -DBOUT_ENABLE_CUDA=on \ - -DCMAKE_CUDA_ARCHITECTURES=80 \ - -DCUDA_ARCH=compute_80,code=sm_80 \ - -DBOUT_ENABLE_WARNINGS=off \ - -DBOUT_USE_SYSTEM_FMT=on - cd build - make -j 4 From 7dd76644f54ca455648cec9a0e584fac46274624 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 11 Mar 2026 10:53:19 +0100 Subject: [PATCH 386/461] Ensure consistency Co-authored-by: Peter Hill --- src/field/field3d.cxx | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/src/field/field3d.cxx b/src/field/field3d.cxx index 66a5dbfc18..da7bd494c9 100644 --- a/src/field/field3d.cxx +++ b/src/field/field3d.cxx @@ -144,11 +144,8 @@ void Field3D::splitParallelSlices() { // ParallelTransform, so we don't need a full constructor yup_fields.emplace_back(fieldmesh); ydown_fields.emplace_back(fieldmesh); - if (isFci()) { - yup_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", i + 1)); - ydown_fields[i].setRegion(fmt::format("RGN_YPAR_{:+d}", -i - 1)); - } } + resetRegionParallel(); } void Field3D::clearParallelSlices() { From dddd2aa58d63a3b3d67c5d87b0433a33162afd10 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 10:10:50 +0000 Subject: [PATCH 387/461] Fix formatted i18n string for C++20 --- src/mesh/impls/bout/boutmesh.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 131fb053dc..7e842b3e72 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -516,8 +516,8 @@ int BoutMesh::load() { MYG = options["MYG"].doc("Number of guard cells on each side in Y").withDefault(MYG); } if (meshHasMyg && MYG != meshMyg) { - output_warn.write(_("Options changed the number of y-guard cells. Grid has {} but " - "option specified {}! Continuing with {}"), + output_warn.write(_f("Options changed the number of y-guard cells. Grid has {} but " + "option specified {}! Continuing with {}"), meshMyg, MYG, MYG); } ASSERT0(MYG >= 0); From 0aab0278dff286600d2a55e5bbe8738c32159654 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 21 Jun 2024 17:09:08 +0100 Subject: [PATCH 388/461] Mark some `OptionsNetCDF` methods as `override` --- src/sys/options/options_netcdf.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/options/options_netcdf.hxx b/src/sys/options/options_netcdf.hxx index 09a2da3220..13774c15e0 100644 --- a/src/sys/options/options_netcdf.hxx +++ b/src/sys/options/options_netcdf.hxx @@ -39,7 +39,7 @@ public: /// - "append" File mode, default is false OptionsNetCDF(Options& options); - ~OptionsNetCDF() {} + ~OptionsNetCDF() override = default; OptionsNetCDF(const OptionsNetCDF&) = delete; OptionsNetCDF(OptionsNetCDF&&) noexcept = default; From dd38b45cd68862d67a1d3a59024e2192905589b4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 21 Jun 2024 17:09:23 +0100 Subject: [PATCH 389/461] Make `OptionsNetCDF` registration symbol `const` --- src/sys/options/options_netcdf.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/sys/options/options_netcdf.hxx b/src/sys/options/options_netcdf.hxx index 13774c15e0..09e8a8234c 100644 --- a/src/sys/options/options_netcdf.hxx +++ b/src/sys/options/options_netcdf.hxx @@ -74,7 +74,7 @@ private: }; namespace { -RegisterOptionsIO registeroptionsnetcdf("netcdf"); +const RegisterOptionsIO registeroptionsnetcdf("netcdf"); } } // namespace bout From a4cec3be3fa327823441e097a203df6b982d0a1e Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 21 Jun 2024 17:21:14 +0100 Subject: [PATCH 390/461] Fix `OptionsIO` docstring appearing on wrong thing Appears on include guard instead of class --- include/bout/options_io.hxx | 32 ++++++++++++++++---------------- 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 00dd0c4cf3..1539b09140 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -1,3 +1,19 @@ +#pragma once + +#ifndef OPTIONS_IO_H +#define OPTIONS_IO_H + +#include "bout/build_defines.hxx" +#include "bout/generic_factory.hxx" +#include "bout/options.hxx" + +#include +#include + +class Mesh; + +namespace bout { + /// Parent class for IO to binary files and streams /// /// @@ -36,22 +52,6 @@ /// /// if mesh is omitted, no grid information is added. /// - -#pragma once - -#ifndef OPTIONS_IO_H -#define OPTIONS_IO_H - -#include "bout/build_defines.hxx" -#include "bout/generic_factory.hxx" -#include "bout/mesh.hxx" -#include "bout/options.hxx" - -#include -#include - -namespace bout { - class OptionsIO { public: /// No default constructor, as settings are required From c1fa555b072c2bb45c6b3ee4dfef9f01fe569105 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 3 Jul 2024 11:41:43 +0100 Subject: [PATCH 391/461] Silence clang-tidy warning about unnamed argument --- include/bout/options_io.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index 1539b09140..a93d89d362 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -59,7 +59,7 @@ public: /// Constructor specifies the kind of file, and options to control /// the name of file, mode of operation etc. - OptionsIO(Options&) {} + OptionsIO(Options& /*unused*/) {} virtual ~OptionsIO() = default; From d19bedaa38589289795b75730d7f83a79a3cfaf0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 3 Jul 2024 11:34:29 +0100 Subject: [PATCH 392/461] Add option to change frequency output is flushed to disk --- include/bout/options_io.hxx | 3 +++ include/bout/physicsmodel.hxx | 8 ++++++++ src/physics/physicsmodel.cxx | 21 ++++++++++++++------- src/sys/options/options_netcdf.cxx | 4 ++-- src/sys/options/options_netcdf.hxx | 3 +++ 5 files changed, 30 insertions(+), 9 deletions(-) diff --git a/include/bout/options_io.hxx b/include/bout/options_io.hxx index a93d89d362..77fdd4712b 100644 --- a/include/bout/options_io.hxx +++ b/include/bout/options_io.hxx @@ -81,6 +81,9 @@ public: /// ADIOS: Indicate completion of an output step. virtual void verifyTimesteps() const = 0; + /// NetCDF: Flush file to disk + virtual void flush() {} + /// Create an OptionsIO for I/O to the given file. /// This uses the default file type and default options. static std::unique_ptr create(const std::string& file); diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index 7f4df3aa42..338211c434 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -49,6 +49,7 @@ class PhysicsModel; #include #include +#include #include #include @@ -378,6 +379,9 @@ protected: PhysicsModel* model; }; + /// Set timestep counter for flushing file + void setFlushCounter(std::size_t iteration) { flush_counter = iteration; } + private: /// State for outputs Options output_options; @@ -401,6 +405,10 @@ private: bool initialised{false}; /// write restarts and pass outputMonitor method inside a Monitor subclass PhysicsModelMonitor modelMonitor{this}; + /// How often to flush to disk + std::size_t flush_frequency{1}; + /// Current timestep counter + std::size_t flush_counter{0}; }; /*! diff --git a/src/physics/physicsmodel.cxx b/src/physics/physicsmodel.cxx index a1bde18248..b6bab272fc 100644 --- a/src/physics/physicsmodel.cxx +++ b/src/physics/physicsmodel.cxx @@ -34,12 +34,14 @@ #include "bout/version.hxx" #include +#include #include #include #include #include +#include #include using namespace std::literals; @@ -67,9 +69,10 @@ PhysicsModel::PhysicsModel() .withDefault(true)), restart_enabled(Options::root()["restart_files"]["enabled"] .doc("Write restart files") - .withDefault(true)) - -{ + .withDefault(true)), + flush_frequency(Options::root()["output"]["flush_frequency"] + .doc("How often to flush to disk") + .withDefault(1)) { if (output_enabled) { output_file = bout::OptionsIOFactory::getInstance().createOutput(); } @@ -216,9 +219,7 @@ void PhysicsModel::writeRestartFile() { void PhysicsModel::writeOutputFile() { writeOutputFile(output_options); } void PhysicsModel::writeOutputFile(const Options& options) { - if (output_enabled) { - output_file->write(options, "t"); - } + writeOutputFile(options, "t"); } void PhysicsModel::writeOutputFile(const Options& options, @@ -229,13 +230,19 @@ void PhysicsModel::writeOutputFile(const Options& options, } void PhysicsModel::finishOutputTimestep() const { - if (output_enabled) { + Timer timer("io"); + + if (output_enabled and (flush_counter % flush_frequency == 0)) { + output_file->flush(); output_file->verifyTimesteps(); } } int PhysicsModel::PhysicsModelMonitor::call(Solver* solver, BoutReal simtime, int iteration, int nout) { + + model->setFlushCounter(static_cast(iteration)); + // Restart file variables solver->outputVars(model->restart_options, false); model->restartVars(model->restart_options); diff --git a/src/sys/options/options_netcdf.cxx b/src/sys/options/options_netcdf.cxx index f6edb26dbc..d3e5a993bd 100644 --- a/src/sys/options/options_netcdf.cxx +++ b/src/sys/options/options_netcdf.cxx @@ -791,10 +791,10 @@ void OptionsNetCDF::write(const Options& options, const std::string& time_dim) { } writeGroup(options, *data_file, time_dim); - - data_file->sync(); } +void OptionsNetCDF::flush() { data_file->sync(); } + } // namespace bout #endif // BOUT_HAS_NETCDF diff --git a/src/sys/options/options_netcdf.hxx b/src/sys/options/options_netcdf.hxx index 09e8a8234c..38a79310b7 100644 --- a/src/sys/options/options_netcdf.hxx +++ b/src/sys/options/options_netcdf.hxx @@ -58,6 +58,9 @@ public: /// any differences, otherwise is silent void verifyTimesteps() const override; + /// Flush file to disk + void flush() override; + private: enum class FileMode { replace, ///< Overwrite file when writing From 08134655bf5405cd1913078394c44a5b3539c529 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 3 Jul 2024 11:34:40 +0100 Subject: [PATCH 393/461] Reset output `Options` after writing to disk Avoids rewriting time-independent data, gives slight performance boost --- src/physics/physicsmodel.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/physics/physicsmodel.cxx b/src/physics/physicsmodel.cxx index b6bab272fc..5e31fb8047 100644 --- a/src/physics/physicsmodel.cxx +++ b/src/physics/physicsmodel.cxx @@ -261,5 +261,8 @@ int PhysicsModel::PhysicsModelMonitor::call(Solver* solver, BoutReal simtime, model->outputVars(model->output_options); model->writeOutputFile(); + // Reset output options, this avoids rewriting time-independent data + model->output_options = Options{}; + return monitor_result; } From 5527447246e41f8b8465a904b8b424e9a18676d0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Fri, 10 Oct 2025 17:12:52 +0100 Subject: [PATCH 394/461] Fix some clang-tidy warnings --- src/physics/physicsmodel.cxx | 6 +++--- src/sys/options/options_netcdf.hxx | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/physics/physicsmodel.cxx b/src/physics/physicsmodel.cxx index 5e31fb8047..6cdd78e351 100644 --- a/src/physics/physicsmodel.cxx +++ b/src/physics/physicsmodel.cxx @@ -191,7 +191,7 @@ int PhysicsModel::postInit(bool restarting) { } void PhysicsModel::outputVars(Options& options) { - Timer time("io"); + const Timer time("io"); for (const auto& item : dump.getData()) { bout::utils::visit(bout::OptionsConversionVisitor{options, item.name}, item.value); if (item.repeat) { @@ -201,7 +201,7 @@ void PhysicsModel::outputVars(Options& options) { } void PhysicsModel::restartVars(Options& options) { - Timer time("io"); + const Timer time("io"); for (const auto& item : restart.getData()) { bout::utils::visit(bout::OptionsConversionVisitor{options, item.name}, item.value); if (item.repeat) { @@ -230,7 +230,7 @@ void PhysicsModel::writeOutputFile(const Options& options, } void PhysicsModel::finishOutputTimestep() const { - Timer timer("io"); + const Timer timer("io"); if (output_enabled and (flush_counter % flush_frequency == 0)) { output_file->flush(); diff --git a/src/sys/options/options_netcdf.hxx b/src/sys/options/options_netcdf.hxx index 38a79310b7..3e2a69b6d7 100644 --- a/src/sys/options/options_netcdf.hxx +++ b/src/sys/options/options_netcdf.hxx @@ -77,7 +77,7 @@ private: }; namespace { -const RegisterOptionsIO registeroptionsnetcdf("netcdf"); +const inline RegisterOptionsIO registeroptionsnetcdf("netcdf"); } } // namespace bout From c9e2e27e83c5ebfa080852e4f9f5495d4dfab2a9 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 6 Jan 2026 16:02:05 +0000 Subject: [PATCH 395/461] Add docs for `flush_frequency` Also remove deleted options! --- manual/sphinx/user_docs/bout_options.rst | 58 ++++++++++++------------ 1 file changed, 29 insertions(+), 29 deletions(-) diff --git a/manual/sphinx/user_docs/bout_options.rst b/manual/sphinx/user_docs/bout_options.rst index 4a9c568e89..df244cd7c1 100644 --- a/manual/sphinx/user_docs/bout_options.rst +++ b/manual/sphinx/user_docs/bout_options.rst @@ -554,42 +554,42 @@ may be useful anyway. See :ref:`sec-output` for more details. Input and Output ---------------- -The output (dump) files with time-history are controlled by settings -in a section called “output”. Restart files contain a single -time-slice, and are controlled by a section called “restart”. The -options available are listed in table :numref:`tab-outputopts`. +The output (dump) files with time-history are controlled by settings in a +section called ``"output"``. Restart files contain a single time-slice, and are +controlled by a section called ``"restart"``. The options available are listed +in table :numref:`tab-outputopts`. .. _tab-outputopts: .. table:: Output file options - +-------------+----------------------------------------------------+--------------+ - | Option | Description | Default | - | | | value | - +-------------+----------------------------------------------------+--------------+ - | enabled | Writing is enabled | true | - +-------------+----------------------------------------------------+--------------+ - | type | File type e.g. "netcdf" or "adios" | "netcdf" | - +-------------+----------------------------------------------------+--------------+ - | prefix | File name prefix | "BOUT.dmp" | - +-------------+----------------------------------------------------+--------------+ - | path | Directory to write the file into | ``datadir`` | - +-------------+----------------------------------------------------+--------------+ - | floats | Write floats rather than doubles | false | - +-------------+----------------------------------------------------+--------------+ - | flush | Flush the file to disk after each write | true | - +-------------+----------------------------------------------------+--------------+ - | guards | Output guard cells | true | - +-------------+----------------------------------------------------+--------------+ - | openclose | Re-open the file for each write, and close after | true | - +-------------+----------------------------------------------------+--------------+ + +----------------------+-----------------------------------------+----------------+ + | Option | Description | Default value | + +======================+=========================================+================+ + | ``append`` | Append to existing file if true, | ``false`` | + | | otherwise overwrite | | + +----------------------+-----------------------------------------+----------------+ + | ``enabled`` | Writing is enabled | ``true`` | + +----------------------+-----------------------------------------+----------------+ + | ``flush_frequency`` | How many output timesteps between | ``1`` | + | | writing output to disk (NetCDF only) | | + +----------------------+-----------------------------------------+----------------+ + | ``prefix`` | File name prefix | ``"BOUT.dmp"`` | + +----------------------+-----------------------------------------+----------------+ + | ``path`` | Directory to write the file into | ``datadir`` | + +----------------------+-----------------------------------------+----------------+ + | ``type`` | File type, either ``"netcdf"`` or | ``"netcdf"`` | + | | ``"adios"`` | | + +----------------------+-----------------------------------------+----------------+ | -**enabled** is useful mainly for doing performance or scaling tests, where you -want to exclude I/O from the timings. **floats** can be used to reduce the size -of the output files: files are stored as double by default, but setting -**floats = true** changes the output to single-precision floats. - +- ``enabled`` is useful mainly for doing performance or scaling tests, where you + want to exclude I/O from the timings. +- If you find that IO is taking more and more time as your simulation goes on, + try setting ``flush_frequency`` to a larger value such as ``10``. This can + workaround an issue with NetCDF where subsequent writes take longer and + longer. However, larger values risk losing more data in the event of a crash + or the simulation being killed early. Implementation -------------- From 1b4707187a3a85126338303dc766280b8fb2dc56 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:29:57 +0000 Subject: [PATCH 396/461] [bot] Apply format changes --- include/bout/physicsmodel.hxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/bout/physicsmodel.hxx b/include/bout/physicsmodel.hxx index 338211c434..d653d96d68 100644 --- a/include/bout/physicsmodel.hxx +++ b/include/bout/physicsmodel.hxx @@ -48,8 +48,8 @@ class PhysicsModel; #include "bout/utils.hxx" #include -#include #include +#include #include #include From 4c9b456735d00a14113759fed9ad84a027214094 Mon Sep 17 00:00:00 2001 From: ZedThree <1486942+ZedThree@users.noreply.github.com> Date: Wed, 11 Mar 2026 11:29:58 +0000 Subject: [PATCH 397/461] [bot] Add last format changes commit to ignore file --- .git-blame-ignore-revs | 1 + 1 file changed, 1 insertion(+) diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 84be8e384a..a3b913621f 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -14,3 +14,4 @@ c8f38049359170a34c915e209276238ea66b9a1e 8d5cb39e03c2644715a50684f8cd0318b4e82676 ec69e8838be2dde140a915e50506f8e1ce3cb534 f2bc0488a298f136294c523bc5ab4086d090059b +1b4707187a3a85126338303dc766280b8fb2dc56 From 9235bcfd382bd457ddf52d87a0a122ff1b556e35 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Sat, 28 Feb 2026 11:39:03 +0000 Subject: [PATCH 398/461] Convert `LaplacePetsc` to use `PetscMatrix` --- .../laplace/impls/petsc/petsc_laplace.cxx | 1036 +++++------------ .../laplace/impls/petsc/petsc_laplace.hxx | 46 +- .../test-petsc_laplace/test_petsc_laplace.cxx | 24 +- 3 files changed, 347 insertions(+), 759 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 673f9c0df5..da964d443d 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -34,13 +34,20 @@ #include #include #include +#include +#include #include #include +#include #include +#include #include +#include #include #include +#include +#include #include #define KSP_RICHARDSON "richardson" @@ -65,6 +72,81 @@ PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { PetscFunctionReturn(laplace->precon(x, y)); // NOLINT } + +// TODO: handle fourth order +auto set_stencil(const Mesh& localmesh, bool fourth_order) { + OperatorStencil stencil; + IndexOffset zero; + // Start with a square stencil 1-point wide + std::set offsets = { + // clang-format off + zero.xm().zp(), zero.zp(), zero.xp().zp(), + zero.xm(), zero, zero.xp(), + zero.xm().zm(), zero.zm(), zero.xp().zm(), + // clang-format on + }; + + if (fourth_order) { + // Add a square stencil 2-points wide + offsets.insert({ + // clang-format off + zero.xm(2).zp(2), zero.xm().zp(2), zero.zp(2), zero.xp().zp(2), zero.xp(2).zp(2), + zero.xm(2).zp(), zero.xp(2).zp(), + zero.xm(2), zero.xp(2), + zero.xm(2).zm(), zero.xp(2).zm(), + zero.xm(2).zm(2), zero.xm().zm(2), zero.zm(2), zero.xp().zm(2), zero.xp(2).zm(2), + // clang-format on + }); + } + + std::vector offsetsVec(offsets.begin(), offsets.end()); + stencil.add( + [&localmesh](IndPerp ind) -> bool { + return (localmesh.xstart <= ind.x() && ind.x() <= localmesh.xend + and (localmesh.zstart <= ind.z() && ind.z() <= localmesh.zend)); + }, + offsetsVec); + + // Add inner X boundary + if (localmesh.firstX()) { + const auto first_boundary = localmesh.xstart - 1; + const auto second_boundary = localmesh.xstart - 2; + + if (fourth_order) { + stencil.add( + [first_boundary, second_boundary](IndPerp ind) -> bool { + const auto x = ind.x(); + return x == first_boundary or x == second_boundary; + }, + {zero, zero.xp(1), zero.xp(2), zero.xp(3), zero.xp(4)}); + } else { + stencil.add( + [first_boundary](IndPerp ind) -> bool { return ind.x() == first_boundary; }, + {zero, zero.xp()}); + } + } + // Add outer X boundary + if (localmesh.lastX()) { + const auto first_boundary = localmesh.xend + 1; + const auto second_boundary = localmesh.xend + 2; + + if (fourth_order) { + stencil.add( + [first_boundary, second_boundary](IndPerp ind) -> bool { + const auto x = ind.x(); + return x == first_boundary or x == second_boundary; + }, + {zero, zero.xm(1), zero.xm(2), zero.xm(3), zero.xm(4)}); + } else { + stencil.add( + [first_boundary](IndPerp ind) -> bool { return ind.x() == first_boundary; }, + {zero, zero.xm()}); + } + } + + stencil.add([](IndPerp UNUSED(ind)) -> bool { return true; }, {zero}); + return stencil; +} } // namespace LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, @@ -91,7 +173,9 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, direct((*opts)["direct"].doc("Use direct (LU) solver?").withDefault(false)), fourth_order( (*opts)["fourth_order"].doc("Use fourth order stencil").withDefault(false)), - lib(opts) { + indexer(std::make_shared>( + localmesh, set_stencil(*localmesh, fourth_order))), + operator2D(indexer), lib(opts) { A.setLocation(location); C1.setLocation(location); @@ -111,114 +195,6 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, } #endif - const bool first_x = localmesh->firstX(); - const bool last_x = localmesh->lastX(); - - // Need to determine local size to use based on prior parallelisation - // Coefficient values are stored only on local processors. - const auto local_nz = localmesh->zend - localmesh->zstart + 1; - localN = (localmesh->xend - localmesh->xstart + 1) * local_nz; - if (first_x) { - // If on first processor add on width of boundary region - localN += localmesh->xstart * local_nz; - } - if (last_x) { - // If on last processor add on width of boundary region - localN += localmesh->xstart * local_nz; - } - - // Calculate 'size' (the total number of points in physical grid) - size = localN; - if (bout::globals::mpi->MPI_Allreduce(&localN, &size, 1, MPI_INT, MPI_SUM, comm) - != MPI_SUCCESS) { - throw BoutException("Error in MPI_Allreduce during LaplacePetsc initialisation"); - } - - // Calculate total (physical) grid dimensions - meshz = local_nz; - meshx = size / meshz; - - // Create PETSc type of vectors for the solution and the RHS vector - VecCreate(comm, &xs); - VecSetSizes(xs, localN, size); - VecSetFromOptions(xs); - VecDuplicate(xs, &bs); - - // Set size of (the PETSc) Matrix on each processor to localN x localN - MatCreate(comm, &MatA); - MatSetSizes(MatA, localN, localN, size, size); - MatSetFromOptions(MatA); - - // Pre allocate memory. See MatMPIAIJSetPreallocation docs for more info - // Number of non-zero elements in each row of matrix owned by this processor - // ("diagonal submatrices") - std::vector d_nnz(localN, 0); - // Number of non-zero elements in each row of matrix owned by other processors - // ("offdiagonal submatrices") - std::vector o_nnz(localN, 0); - - if (fourth_order) { - // first and last 2*localmesh-LocalNz entries are the edge x-values that - // (may) have 'off-diagonal' components (i.e. on another processor) - - const int first_first_off = first_x ? 0 : 10; - const int first_last_off = last_x ? 0 : 10; - const int second_first_off = first_x ? 0 : 5; - const int second_last_off = last_x ? 0 : 5; - - for (int i = 0; i <= local_nz; i++) { - d_nnz[i] = 15; - d_nnz[localN - 1 - i] = 15; - o_nnz[i] = first_first_off; - o_nnz[localN - 1 - i] = first_last_off; - } - for (int i = local_nz; i < 2 * local_nz; i++) { - d_nnz[i] = 20; - d_nnz[localN - 1 - i] = 20; - o_nnz[i] = second_first_off; - o_nnz[localN - 1 - i] = second_last_off; - } - - for (int i = 2 * local_nz; i < localN - (2 * local_nz); i++) { - d_nnz[i] = 25; - d_nnz[localN - 1 - i] = 25; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - } else { - // first and last local_nz entries are the edge x-values that - // (may) have 'off-diagonal' components (i.e. on another processor) - const int first_off = first_x ? 0 : 3; - const int last_off = last_x ? 0 : 3; - - for (int i = 0; i <= local_nz; i++) { - d_nnz[i] = 6; - d_nnz[localN - 1 - i] = 6; - o_nnz[i] = first_off; - o_nnz[localN - 1 - i] = last_off; - } - - for (int i = local_nz; i < localN - local_nz; i++) { - d_nnz[i] = 9; - d_nnz[localN - 1 - i] = 9; - o_nnz[i] = 0; - o_nnz[localN - 1 - i] = 0; - } - } - - if (first_x and last_x) { - // Only one processor in X - MatSeqAIJSetPreallocation(MatA, 0, d_nnz.data()); - } else { - MatMPIAIJSetPreallocation(MatA, 0, d_nnz.data(), 0, o_nnz.data()); - } - - // Sets up the internal matrix data structures for the later use. - MatSetUp(MatA); - - // Declare KSP Context (abstract PETSc object that manages all Krylov methods) - KSPCreate(comm, &ksp); - // Let "user" be a synonym for "shell" if (pctype == "user") { pctype = PCSHELL; @@ -236,6 +212,12 @@ LaplacePetsc::LaplacePetsc(Options* opt, const CELL_LOC loc, Mesh* mesh_in, } } +LaplacePetsc::~LaplacePetsc() { + if (ksp_initialised) { + KSPDestroy(&ksp); + } +} + FieldPerp LaplacePetsc::solve(const FieldPerp& b) { return solve(b, b); } /*! @@ -265,404 +247,34 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { checkFlags(); #endif - int y = b.getIndex(); // Get the Y index - sol.setIndex(y); // Initialize the solution field. - sol = 0.; - - // Determine which row/columns of the matrix are locally owned - MatGetOwnershipRange(MatA, &Istart, &Iend); - - int i = Istart; // The row in the PETSc matrix + const int y = b.getIndex(); // Get the Y index { Timer timer("petscsetup"); - // if ((fourth_order) && !(lastflag&INVERT_4TH_ORDER)) throw BoutException("Should not change INVERT_4TH_ORDER flag in LaplacePetsc: 2nd order and 4th order require different pre-allocation to optimize PETSc solver"); + const bool inner_X_neumann = isInnerBoundaryFlagSet(INVERT_AC_GRAD); + const bool outer_X_neumann = isOuterBoundaryFlagSet(INVERT_AC_GRAD); - /* Set Matrix Elements - * - * Loop over locally owned rows of matrix A - * i labels NODE POINT from - * bottom left = (0,0) = 0 - * to - * top right = (meshx-1,meshz-1) = meshx*meshz-1 - * - * i increments by 1 for an increase of 1 in Z - * i increments by meshz for an increase of 1 in X. - * - * In other word the indexing is done in a row-major order, but starting at - * bottom left rather than top left - */ - // X=0 to localmesh->xstart-1 defines the boundary region of the domain. - // Set the values for the inner boundary region - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val; // Value of element to be set in the matrix - // If Neumann Boundary Conditions are set. - if (isInnerBoundaryFlagSet(INVERT_AC_GRAD)) { - // Set values corresponding to nodes adjacent in x - if (fourth_order) { - // Fourth Order Accuracy on Boundary - Element(i, x, z, 0, 0, - -25.0 / (12.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, 1, 0, - 4.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 2, 0, - -3.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 3, 0, - 4.0 / (3.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, 4, 0, - -1.0 / (4.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - } else { - // Second Order Accuracy on Boundary - // Element(i,x,z, 0, 0, -3.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, 1, 0, 2.0 / coords->dx(x,y), MatA ); - // Element(i,x,z, 2, 0, -1.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset these elements to 0 - // in case 4th order flag was used previously: not allowed now - // Element(i,x,z, 4, 0, 0.0, MatA ); - // Second Order Accuracy on Boundary, set half-way between grid points - Element(i, x, z, 0, 0, - -1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 1, 0, - 1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, 2, 0, 0.0, MatA); - // Element(i,x,z, 3, 0, 0.0, MatA ); // Reset - // these elements to 0 in case 4th order flag was - // used previously: not allowed now - // Element(i,x,z, 4, 0, 0.0, MatA ); - } - } else { - if (fourth_order) { - // Set Diagonal Values to 1 - Element(i, x, z, 0, 0, 1., MatA); - - // Set off diagonal elements to zero - Element(i, x, z, 1, 0, 0.0, MatA); - Element(i, x, z, 2, 0, 0.0, MatA); - Element(i, x, z, 3, 0, 0.0, MatA); - Element(i, x, z, 4, 0, 0.0, MatA); - } else { - Element(i, x, z, 0, 0, 0.5, MatA); - Element(i, x, z, 1, 0, 0.5, MatA); - Element(i, x, z, 2, 0, 0., MatA); - } - } - - val = 0; // Initialize val - - // Set Components of RHS - // If the inner boundary value should be set by b or x0 - if (isInnerBoundaryFlagSet(INVERT_RHS)) { - val = b[x][z]; - } else if (isInnerBoundaryFlagSet(INVERT_SET)) { - val = x0[x][z]; - } - - // Set components of the RHS (the PETSc vector bs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - - // Set components of the and trial solution (the PETSc vector xs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - val = x0[x][z]; - VecSetValues(xs, 1, &i, &val, INSERT_VALUES); - - i++; // Increment row in Petsc matrix - } - } + // Set the operator matrix + if (fourth_order) { + setFourthOrderMatrix(y, inner_X_neumann, outer_X_neumann); + } else { + setSecondOrderMatrix(y, inner_X_neumann, outer_X_neumann); } - // Set the values for the main domain - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - // NOTE: Only A0 is the A from setCoefA () - BoutReal A0, A1, A2, A3, A4, A5; - A0 = A(x, y, z); - - ASSERT3(std::isfinite(A0)); - - // Set the matrix coefficients - Coeffs(x, y, z, A1, A2, A3, A4, A5); - - BoutReal dx = coords->dx(x, y, z); - BoutReal dx2 = SQ(dx); - BoutReal dz = coords->dz(x, y, z); - BoutReal dz2 = SQ(dz); - BoutReal dxdz = dx * dz; - - ASSERT3(std::isfinite(A1)); - ASSERT3(std::isfinite(A2)); - ASSERT3(std::isfinite(A3)); - ASSERT3(std::isfinite(A4)); - ASSERT3(std::isfinite(A5)); - - // Set Matrix Elements - PetscScalar val = 0.; - if (fourth_order) { - // f(i,j) = f(x,z) - val = A0 - (5.0 / 2.0) * ((A1 / dx2) + (A2 / dz2)); - Element(i, x, z, 0, 0, val, MatA); - - // f(i-2,j-2) - val = A3 / (144.0 * dxdz); - Element(i, x, z, -2, -2, val, MatA); - - // f(i-2,j-1) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, -2, -1, val, MatA); - - // f(i-2,j) - val = (1.0 / 12.0) * ((-1.0 * A1 / dx2) + (A4 / dx)); - Element(i, x, z, -2, 0, val, MatA); - - // f(i-2,j+1) - val = A3 / (18.0 * dxdz); - Element(i, x, z, -2, 1, val, MatA); - - // f(i-2,j+2) - val = -1.0 * A3 / (144.0 * dxdz); - Element(i, x, z, -2, 2, val, MatA); - - // f(i-1,j-2) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, -1, -2, val, MatA); - - // f(i-1,j-1) - val = 4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, -1, -1, val, MatA); - - // f(i-1,j) - val = (4.0 * A1 / (3.0 * dx2)) - (2.0 * A4 / (3.0 * dx)); - Element(i, x, z, -1, 0, val, MatA); - - // f(i-1,j+1) - val = -4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, -1, 1, val, MatA); - - // f(i-1,j+2) - val = A3 / (18.0 * dxdz); - Element(i, x, z, -1, 2, val, MatA); - - // f(i,j-2) - val = (1.0 / 12.0) * ((-1.0 * A2 / dz2) + (A5 / dz)); - Element(i, x, z, 0, -2, val, MatA); - - // f(i,j-1) - val = (4.0 * A2 / (3.0 * dz2)) - (2.0 * A5 / (3.0 * dz)); - Element(i, x, z, 0, -1, val, MatA); - - // f(i,j+1) - val = (4.0 * A2 / (3.0 * dz2)) + (2.0 * A5 / (3.0 * dz)); - Element(i, x, z, 0, 1, val, MatA); - - // f(i,j+2) - val = (-1.0 / 12.0) * ((A2 / dz2) + (A5 / dz)); - Element(i, x, z, 0, 2, val, MatA); - - // f(i+1,j-2) - val = A3 / (18.0 * dxdz); - Element(i, x, z, 1, -2, val, MatA); - - // f(i+1,j-1) - val = -4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, 1, -1, val, MatA); - - // f(i+1,j) - val = (4.0 * A1 / (3.0 * dx2)) + (2.0 * A4 / (3.0 * dx)); - Element(i, x, z, 1, 0, val, MatA); - - // f(i+1,j+1) - val = 4.0 * A3 / (9.0 * dxdz); - Element(i, x, z, 1, 1, val, MatA); - - // f(i+1,j+2) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, 1, 2, val, MatA); - - // f(i+2,j-2) - val = -1.0 * A3 / (144.0 * dxdz); - Element(i, x, z, 2, -2, val, MatA); - - // f(i+2,j-1) - val = A3 / (18.0 * dxdz); - Element(i, x, z, 2, -1, val, MatA); - - // f(i+2,j) - val = (-1.0 / 12.0) * ((A1 / dx2) + (A4 / dx)); - Element(i, x, z, 2, 0, val, MatA); - - // f(i+2,j+1) - val = -1.0 * A3 / (18.0 * dxdz); - Element(i, x, z, 2, 1, val, MatA); - - // f(i+2,j+2) - val = A3 / (144.0 * dxdz); - Element(i, x, z, 2, 2, val, MatA); - } else { - // Second order - - // f(i,j) = f(x,z) - val = A0 - 2.0 * ((A1 / dx2) + (A2 / dz2)); - Element(i, x, z, 0, 0, val, MatA); - - // f(i-1,j-1) - val = A3 / (4.0 * dxdz); - Element(i, x, z, -1, -1, val, MatA); - - // f(i-1,j) - val = (A1 / dx2) - A4 / (2.0 * dx); - Element(i, x, z, -1, 0, val, MatA); - - // f(i-1,j+1) - val = -1.0 * A3 / (4.0 * dxdz); - Element(i, x, z, -1, 1, val, MatA); - - // f(i,j-1) - val = (A2 / dz2) - (A5 / (2.0 * dz)); - Element(i, x, z, 0, -1, val, MatA); - - // f(i,j+1) - val = (A2 / dz2) + (A5 / (2.0 * dz)); - Element(i, x, z, 0, 1, val, MatA); - - // f(i+1,j-1) - val = -1.0 * A3 / (4.0 * dxdz); - Element(i, x, z, 1, -1, val, MatA); + operator2D.assemble(); + MatSetBlockSize(*operator2D.get(), 1); - // f(i+1,j) - val = (A1 / dx2) + (A4 / (2.0 * dx)); - Element(i, x, z, 1, 0, val, MatA); - - // f(i+1,j+1) - val = A3 / (4.0 * dxdz); - Element(i, x, z, 1, 1, val, MatA); - } - // Set Components of RHS Vector - val = b[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - - // Set Components of Trial Solution Vector - val = x0[x][z]; - VecSetValues(xs, 1, &i, &val, INSERT_VALUES); - i++; - } - } - - // X=localmesh->xend+1 to localmesh->LocalNx-1 defines the upper boundary region of the domain. - // Set the values for the outer boundary region - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - // Set Diagonal Values to 1 - PetscScalar val = 1; - Element(i, x, z, 0, 0, val, MatA); - - // If Neumann Boundary Conditions are set. - if (isOuterBoundaryFlagSet(INVERT_AC_GRAD)) { - // Set values corresponding to nodes adjacent in x - if (fourth_order) { - // Fourth Order Accuracy on Boundary - Element(i, x, z, 0, 0, - 25.0 / (12.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, -1, 0, - -4.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -2, 0, - 3.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -3, 0, - -4.0 / (3.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - Element(i, x, z, -4, 0, - 1.0 / (4.0 * coords->dx(x, y, z)) / sqrt(coords->g_11(x, y, z)), - MatA); - } else { - // // Second Order Accuracy on Boundary - // Element(i,x,z, 0, 0, 3.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, -1, 0, -2.0 / coords->dx(x,y), MatA ); - // Element(i,x,z, -2, 0, 1.0 / (2.0*coords->dx(x,y)), MatA ); - // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 - // in case 4th order flag was used previously: not allowed now - // Element(i,x,z, -4, 0, 0.0, MatA ); - // Second Order Accuracy on Boundary, set half-way between grid - // points - Element(i, x, z, 0, 0, - 1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -1, 0, - -1.0 / coords->dx(x, y, z) / sqrt(coords->g_11(x, y, z)), MatA); - Element(i, x, z, -2, 0, 0.0, MatA); - // Element(i,x,z, -3, 0, 0.0, MatA ); // Reset these elements to 0 - // in case 4th order flag was used previously: not allowed now - // Element(i,x,z, -4, 0, 0.0, MatA ); - } - } else { - if (fourth_order) { - // Set off diagonal elements to zero - Element(i, x, z, -1, 0, 0.0, MatA); - Element(i, x, z, -2, 0, 0.0, MatA); - Element(i, x, z, -3, 0, 0.0, MatA); - Element(i, x, z, -4, 0, 0.0, MatA); - } else { - Element(i, x, z, 0, 0, 0.5, MatA); - Element(i, x, z, -1, 0, 0.5, MatA); - Element(i, x, z, -2, 0, 0., MatA); - } - } - - // Set Components of RHS - // If the inner boundary value should be set by b or x0 - val = 0; - if (isOuterBoundaryFlagSet(INVERT_RHS)) { - val = b[x][z]; - } else if (isOuterBoundaryFlagSet(INVERT_SET)) { - val = x0[x][z]; - } - - // Set components of the RHS (the PETSc vector bs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - - // Set components of the and trial solution (the PETSc vector xs) - // 1 element is being set in row i to val - // INSERT_VALUES replaces existing entries with new values - val = x0[x][z]; - VecSetValues(xs, 1, &i, &val, INSERT_VALUES); - - i++; // Increment row in Petsc matrix - } - } - } - - if (i != Iend) { - throw BoutException("Petsc index sanity check failed: i={} != Iend={}", i, Iend); + // Declare KSP Context (abstract PETSc object that manages all Krylov methods) + if (ksp_initialised) { + KSPDestroy(&ksp); } - - // Assemble Matrix - MatAssemblyBegin(MatA, MAT_FINAL_ASSEMBLY); - MatAssemblyEnd(MatA, MAT_FINAL_ASSEMBLY); - - // // Record which flags were used for this matrix - // lastflag = flags; - - // Assemble RHS Vector - VecAssemblyBegin(bs); - VecAssemblyEnd(bs); - - // Assemble Trial Solution Vector - VecAssemblyBegin(xs); - VecAssemblyEnd(xs); + KSPCreate(comm, &ksp); // Configure Linear Solver #if PETSC_VERSION_GE(3, 5, 0) - KSPSetOperators(ksp, MatA, MatA); + KSPSetOperators(ksp, *operator2D.get(), *operator2D.get()); #else - KSPSetOperators(ksp, MatA, MatA, DIFFERENT_NONZERO_PATTERN); + KSPSetOperators(ksp, *operator2D.get(), *operator2D.get(), DIFFERENT_NONZERO_PATTERN); #endif PC pc; // The preconditioner option @@ -717,19 +329,34 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { } else { KSPSetPCSide(ksp, PC_LEFT); // Left preconditioning } - //ierr = PCShellSetApply(pc,laplacePCapply);CHKERRQ(ierr); - //ierr = PCShellSetContext(pc,this);CHKERRQ(ierr); - //ierr = KSPSetPCSide(ksp, PC_RIGHT);CHKERRQ(ierr); } lib.setOptionsFromInputFile(ksp); } } + PetscVector rhs(b, indexer); + PetscVector guess(x0, indexer); + + // Set boundary conditions + if (!isInnerBoundaryFlagSet(INVERT_RHS)) { + BOUT_FOR_SERIAL(index, indexer->getRegionInnerX()) { + rhs(index) = isInnerBoundaryFlagSet(INVERT_SET) ? x0[index] : 0.0; + } + } + if (!isOuterBoundaryFlagSet(INVERT_RHS)) { + BOUT_FOR_SERIAL(index, indexer->getRegionOuterX()) { + rhs(index) = isInnerBoundaryFlagSet(INVERT_SET) ? x0[index] : 0.0; + } + } + + rhs.assemble(); + guess.assemble(); + // Call the actual solver { Timer timer("petscsolve"); - KSPSolve(ksp, bs, xs); // Call the solver to solve the system + KSPSolve(ksp, *rhs.get(), *guess.get()); } KSPConvergedReason reason; @@ -743,118 +370,14 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { KSPConvergedReasons[reason], static_cast(reason)); } - // Add data to FieldPerp Object - i = Istart; - // Set the inner boundary values - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val = 0; - VecGetValues(xs, 1, &i, &val); - sol[x][z] = val; - i++; // Increment row in Petsc matrix - } - } - } - - // Set the main domain values - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val = 0; - VecGetValues(xs, 1, &i, &val); - sol[x][z] = val; - i++; // Increment row in Petsc matrix - } - } - - // Set the outer boundary values - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val = 0; - VecGetValues(xs, 1, &i, &val); - sol[x][z] = val; - i++; // Increment row in Petsc matrix - } - } - } - - if (i != Iend) { - throw BoutException("Petsc index sanity check 2 failed"); - } - + auto sol = guess.toField(); + sol.setIndex(y); checkData(sol); // Return the solution return sol; } -/*! - * Sets the elements of the matrix A, which is used to solve the problem Ax=b. - * - * \param[in] - * i - * The row of the PETSc matrix - * \param[in] x Local x index of the mesh - * \param[in] z Local z index of the mesh - * \param[in] xshift The shift in rows from the index x - * \param[in] zshift The shift in columns from the index z - * \param[in] ele Value of the element - * \param[in] MatA The matrix A used in the inversion - * - * \param[out] MatA The matrix A used in the inversion - */ -void LaplacePetsc::Element(int i, int x, int z, int xshift, int zshift, PetscScalar ele, - Mat& MatA) { - - // Need to convert LOCAL x to GLOBAL x in order to correctly calculate - // PETSC Matrix Index. - int xoffset = Istart / meshz; -#if CHECK > 2 - const int rem = Istart % meshz; - if (rem != 0) { - throw BoutException("Petsc index sanity check 3 failed: Istart={} % meshz={} == {}", - Istart, meshz, rem); - } -#endif - - // Calculate the row to be set - int row_new = x + xshift; // should never be out of range. - if (!localmesh->firstX()) { - row_new += (xoffset - localmesh->xstart); - } - - // Calculate the column to be set - int col_new = z + zshift - localmesh->zstart; - if (col_new < 0) { - col_new += meshz; - } else if (col_new > meshz - 1) { - col_new -= meshz; - } - - // Convert to global indices - int index = (row_new * meshz) + col_new; - -#if CHECK > 2 - if (!std::isfinite(ele)) { - throw BoutException("Non-finite element at x={:d}, z={:d}, row={:d}, col={:d}\n", x, - z, i, index); - } -#endif - - /* Inserts or adds a block of values into a matrix - * Input: - * MatA - The matrix to set the values in - * 1 - The number of rows to be set - * &i - The global index of the row - * 1 - The number of columns to be set - * &index - The global index of the column - * &ele - The vlaue to be set - * INSERT_VALUES replaces existing entries with new values - */ - MatSetValues(MatA, 1, &i, 1, &index, &ele, INSERT_VALUES); -} - /*! * Set the matrix components of A in Ax=b, solving * D*Laplace_perp(x) + (1/C1)Grad_perp(C2)*Grad_perp(x) + Ax = B @@ -887,19 +410,19 @@ void LaplacePetsc::Element(int i, int x, int z, int xshift, int zshift, PetscSca * \param[out] coef5 Convenient variable used to set matrix * (see manual for details) */ -void LaplacePetsc::Coeffs(int x, int y, int z, BoutReal& coef1, BoutReal& coef2, - BoutReal& coef3, BoutReal& coef4, BoutReal& coef5) { +LaplacePetsc::CoeffsA LaplacePetsc::Coeffs(Ind3D i) { + const auto x = i.x(); - coef1 = coords->g11(x, y, z); // X 2nd derivative coefficient - coef2 = coords->g33(x, y, z); // Z 2nd derivative coefficient - coef3 = 2. * coords->g13(x, y, z); // X-Z mixed derivative coefficient + BoutReal coef1 = coords->g11[i]; // X 2nd derivative coefficient + BoutReal coef2 = coords->g33[i]; // Z 2nd derivative coefficient + BoutReal coef3 = 2. * coords->g13[i]; // X-Z mixed derivative coefficient - coef4 = 0.0; - coef5 = 0.0; + BoutReal coef4 = 0.0; + BoutReal coef5 = 0.0; // If global flag all_terms are set (true by default) if (all_terms) { - coef4 = coords->G1(x, y, z); // X 1st derivative - coef5 = coords->G3(x, y, z); // Z 1st derivative + coef4 = coords->G1[i]; // X 1st derivative + coef5 = coords->G3[i]; // Z 1st derivative ASSERT3(std::isfinite(coef4)); ASSERT3(std::isfinite(coef5)); @@ -908,71 +431,48 @@ void LaplacePetsc::Coeffs(int x, int y, int z, BoutReal& coef1, BoutReal& coef2, if (nonuniform) { // non-uniform mesh correction if ((x != 0) && (x != (localmesh->LocalNx - 1))) { - coef4 -= 0.5 - * ((coords->dx(x + 1, y, z) - coords->dx(x - 1, y, z)) - / SQ(coords->dx(x, y, z))) + coef4 -= 0.5 * ((coords->dx[i.xp()] - coords->dx[i.xm()]) / SQ(coords->dx[i])) * coef1; // BOUT-06 term } } if (localmesh->IncIntShear) { // d2dz2 term - coef2 += coords->g11(x, y, z) * coords->IntShiftTorsion(x, y, z) - * coords->IntShiftTorsion(x, y, z); + coef2 += coords->g11[i] * coords->IntShiftTorsion[i] * coords->IntShiftTorsion[i]; // Mixed derivative coef3 = 0.0; // This cancels out } if (issetD) { - coef1 *= D(x, y, z); - coef2 *= D(x, y, z); - coef3 *= D(x, y, z); - coef4 *= D(x, y, z); - coef5 *= D(x, y, z); + coef1 *= D[i]; + coef2 *= D[i]; + coef3 *= D[i]; + coef4 *= D[i]; + coef5 *= D[i]; } // A second/fourth order derivative term if (issetC) { - // if( (x > 0) && (x < (localmesh->LocalNx-1)) ) //Valid if doing second order derivative, not if fourth: should only be called for xstart<=x<=xend anyway if ((x > 1) && (x < (localmesh->LocalNx - 2))) { - int zp = z + 1; // z plus 1 - if (zp > meshz - 1) { - zp -= meshz; - } - int zm = z - 1; // z minus 1 - if (zm < 0) { - zm += meshz; - } BoutReal ddx_C; BoutReal ddz_C; if (fourth_order) { - int zpp = z + 2; // z plus 1 plus 1 - if (zpp > meshz - 1) { - zpp -= meshz; - } - int zmm = z - 2; // z minus 1 minus 1 - if (zmm < 0) { - zmm += meshz; - } // Fourth order discretization of C in x - ddx_C = (-C2(x + 2, y, z) + 8. * C2(x + 1, y, z) - 8. * C2(x - 1, y, z) - + C2(x - 2, y, z)) - / (12. * coords->dx(x, y, z) * (C1(x, y, z))); + ddx_C = (-C2[i.xpp()] + 8. * C2[i.xp()] - 8. * C2[i.xm()] + C2[i.xmm()]) + / (12. * coords->dx[i] * (C1[i])); // Fourth order discretization of C in z - ddz_C = (-C2(x, y, zpp) + 8. * C2(x, y, zp) - 8. * C2(x, y, zm) + C2(x, y, zmm)) - / (12. * coords->dz(x, y, z) * (C1(x, y, z))); + ddz_C = (-C2[i.zpp()] + 8. * C2[i.zp()] - 8. * C2[i.zm()] + C2[i.zmm()]) + / (12. * coords->dz[i] * (C1[i])); } else { // Second order discretization of C in x - ddx_C = (C2(x + 1, y, z) - C2(x - 1, y, z)) - / (2. * coords->dx(x, y, z) * (C1(x, y, z))); + ddx_C = (C2[i.xp()] - C2[i.xm()]) / (2. * coords->dx[i] * (C1[i])); // Second order discretization of C in z - ddz_C = - (C2(x, y, zp) - C2(x, y, zm)) / (2. * coords->dz(x, y, z) * (C1(x, y, z))); + ddz_C = (C2[i.zp()] - C2[i.zm()]) / (2. * coords->dz[i] * (C1[i])); } - coef4 += coords->g11(x, y, z) * ddx_C + coords->g13(x, y, z) * ddz_C; - coef5 += coords->g13(x, y, z) * ddx_C + coords->g33(x, y, z) * ddz_C; + coef4 += (coords->g11[i] * ddx_C) + (coords->g13[i] * ddz_C); + coef5 += (coords->g13[i] * ddx_C) + (coords->g33[i] * ddz_C); } } @@ -986,99 +486,199 @@ void LaplacePetsc::Coeffs(int x, int y, int z, BoutReal& coef1, BoutReal& coef2, */ if (issetE) { // These coefficients are 0 by default - coef4 += Ex(x, y, z); - coef5 += Ez(x, y, z); + coef4 += Ex[i]; + coef5 += Ez[i]; } -} - -void LaplacePetsc::vecToField(Vec xs, FieldPerp& f) { - ASSERT1(localmesh == f.getMesh()); + return {coef1, coef2, coef3, coef4, coef5}; +} - f.allocate(); - int i = Istart; - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val; - VecGetValues(xs, 1, &i, &val); - f[x][z] = val; - i++; // Increment row in Petsc matrix - } +void LaplacePetsc::setSecondOrderMatrix(int y, bool inner_X_neumann, + bool outer_X_neumann) { + // Set the boundaries + if (inner_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); + + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = -factor; + operator2D(i, i.xp()) = factor; } - } - - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val; - VecGetValues(xs, 1, &i, &val); - f[x][z] = val; - i++; // Increment row in Petsc matrix + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + operator2D(i, i) = 0.5; + operator2D(i, i.xp()) = 0.5; } } + if (outer_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val; - VecGetValues(xs, 1, &i, &val); - f[x][z] = val; - i++; // Increment row in Petsc matrix - } + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = factor; + operator2D(i, i.xm()) = -factor; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + operator2D(i, i) = 0.5; + operator2D(i, i.xm()) = 0.5; } } - ASSERT1(i == Iend); -} -void LaplacePetsc::fieldToVec(const FieldPerp& f, Vec bs) { - ASSERT1(localmesh == f.getMesh()); + // Set the interior region + BOUT_FOR_SERIAL(l, indexer->getRegionNobndry()) { + const auto i = localmesh->indPerpto3D(l, y); - int i = Istart; - if (localmesh->firstX()) { - for (int x = 0; x < localmesh->xstart; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val = f[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - i++; // Increment row in Petsc matrix - } - } + // NOTE: Only A0 is the A from setCoefA () + const BoutReal A0 = A[i]; + + ASSERT3(std::isfinite(A0)); + + // Set the matrix coefficients + const auto [A1, A2, A3, A4, A5] = Coeffs(i); + + ASSERT3(std::isfinite(A1)); + ASSERT3(std::isfinite(A2)); + ASSERT3(std::isfinite(A3)); + ASSERT3(std::isfinite(A4)); + ASSERT3(std::isfinite(A5)); + + BoutReal dx = coords->dx[i]; + BoutReal dx2 = SQ(dx); + BoutReal dz = coords->dz[i]; + BoutReal dz2 = SQ(dz); + BoutReal dxdz = dx * dz; + operator2D(l, l) = A0 - (2.0 * ((A1 / dx2) + (A2 / dz2))); + operator2D(l, l.xm().zm()) = A3 / (4.0 * dxdz); + operator2D(l, l.xm()) = (A1 / dx2) - (A4 / (2.0 * dx)); + operator2D(l, l.xm().zp()) = -1.0 * A3 / (4.0 * dxdz); + operator2D(l, l.zm()) = (A2 / dz2) - (A5 / (2.0 * dz)); + operator2D(l, l.zp()) = (A2 / dz2) + (A5 / (2.0 * dz)); + operator2D(l, l.xp().zm()) = -1.0 * A3 / (4.0 * dxdz); + operator2D(l, l.xp()) = (A1 / dx2) + (A4 / (2.0 * dx)); + operator2D(l, l.xp().zp()) = A3 / (4.0 * dxdz); } +} - for (int x = localmesh->xstart; x <= localmesh->xend; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val = f[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - i++; // Increment row in Petsc matrix +void LaplacePetsc::setFourthOrderMatrix(int y, bool inner_X_neumann, + bool outer_X_neumann) { + + // Set boundaries + if (inner_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); + + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = (-25.0 / 12.0) * factor; + operator2D(i, i.xp(1)) = 4.0 * factor; + operator2D(i, i.xp(2)) = -3.0 * factor; + operator2D(i, i.xp(3)) = (4.0 / 3.0) * factor; + operator2D(i, i.xp(4)) = (-1.0 / 4.0) * factor; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionInnerX()) { + operator2D(i, i) = 1.0; + operator2D(i, i.xp(1)) = 0.0; + operator2D(i, i.xp(2)) = 0.0; + operator2D(i, i.xp(3)) = 0.0; + operator2D(i, i.xp(4)) = 0.0; } } - if (localmesh->lastX()) { - for (int x = localmesh->xend + 1; x < localmesh->LocalNx; x++) { - for (int z = localmesh->zstart; z <= localmesh->zend; z++) { - PetscScalar val = f[x][z]; - VecSetValues(bs, 1, &i, &val, INSERT_VALUES); - i++; // Increment row in Petsc matrix - } + if (outer_X_neumann) { + const auto dx = sliceXZ(coords->dx, y); + const auto g11 = sliceXZ(coords->g11, y); + + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + const auto factor = 1. / dx[i] / std::sqrt(g11[i]); + operator2D(i, i) = (25.0 / 12.0) * factor; + operator2D(i, i.xm(1)) = -4.0 * factor; + operator2D(i, i.xm(2)) = 3.0 * factor; + operator2D(i, i.xm(3)) = (-4.0 / 3.0) * factor; + operator2D(i, i.xm(4)) = (1.0 / 4.0) * factor; + } + } else { + BOUT_FOR_SERIAL(i, indexer->getRegionOuterX()) { + operator2D(i, i) = 1.0; + operator2D(i, i.xm(1)) = 0.0; + operator2D(i, i.xm(2)) = 0.0; + operator2D(i, i.xm(3)) = 0.0; + operator2D(i, i.xm(4)) = 0.0; } } - ASSERT1(i == Iend); - VecAssemblyBegin(bs); - VecAssemblyEnd(bs); + // Set interior region + BOUT_FOR_SERIAL(l, indexer->getRegionNobndry()) { + const auto i = localmesh->indPerpto3D(l, y); + + // NOTE: Only A0 is the A from setCoefA () + const BoutReal A0 = A[i]; + + ASSERT3(std::isfinite(A0)); + + // Set the matrix coefficients + const auto [A1, A2, A3, A4, A5] = Coeffs(i); + + ASSERT3(std::isfinite(A1)); + ASSERT3(std::isfinite(A2)); + ASSERT3(std::isfinite(A3)); + ASSERT3(std::isfinite(A4)); + ASSERT3(std::isfinite(A5)); + + BoutReal dx = coords->dx[i]; + BoutReal dx2 = SQ(dx); + BoutReal dz = coords->dz[i]; + BoutReal dz2 = SQ(dz); + BoutReal dxdz = dx * dz; + + operator2D(l, l) = A0 - ((5.0 / 2.0) * ((A1 / dx2) + (A2 / dz2))); + operator2D(l, l.xmm().zmm()) = A3 / (144.0 * dxdz); + operator2D(l, l.xmm().zm()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xmm()) = (1.0 / 12.0) * ((-1.0 * A1 / dx2) + (A4 / dx)); + operator2D(l, l.xmm().zp()) = A3 / (18.0 * dxdz); + operator2D(l, l.xmm().zpp()) = -1.0 * A3 / (144.0 * dxdz); + operator2D(l, l.xm().zmm()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xm().zm()) = 4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xm()) = (4.0 * A1 / (3.0 * dx2)) - (2.0 * A4 / (3.0 * dx)); + operator2D(l, l.xm().zp()) = -4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xm().zpp()) = A3 / (18.0 * dxdz); + operator2D(l, l.zmm()) = (1.0 / 12.0) * ((-1.0 * A2 / dz2) + (A5 / dz)); + operator2D(l, l.zm()) = (4.0 * A2 / (3.0 * dz2)) - (2.0 * A5 / (3.0 * dz)); + operator2D(l, l.zp()) = (4.0 * A2 / (3.0 * dz2)) + (2.0 * A5 / (3.0 * dz)); + operator2D(l, l.zpp()) = (-1.0 / 12.0) * ((A2 / dz2) + (A5 / dz)); + operator2D(l, l.xp().zmm()) = A3 / (18.0 * dxdz); + operator2D(l, l.xp().zm()) = -4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xp()) = (4.0 * A1 / (3.0 * dx2)) + (2.0 * A4 / (3.0 * dx)); + operator2D(l, l.xp().zp()) = 4.0 * A3 / (9.0 * dxdz); + operator2D(l, l.xp().zpp()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xpp().zmm()) = -1.0 * A3 / (144.0 * dxdz); + operator2D(l, l.xpp().zm()) = A3 / (18.0 * dxdz); + operator2D(l, l.xpp()) = (-1.0 / 12.0) * ((A1 / dx2) + (A4 / dx)); + operator2D(l, l.xpp().zp()) = -1.0 * A3 / (18.0 * dxdz); + operator2D(l, l.xpp().zpp()) = A3 / (144.0 * dxdz); + } } /// Preconditioner function int LaplacePetsc::precon(Vec x, Vec y) { - // Get field to be preconditioned - FieldPerp xfield; - vecToField(x, xfield); - xfield.setIndex(sol.getIndex()); // y index stored in sol variable + FieldPerp xfield(indexer->getMesh(), location, sol.getIndex()); + xfield = 0.0; + + BOUT_FOR_SERIAL(i, indexer->getRegionAll()) { + const auto ind = indexer->getGlobal(i); + PetscScalar val = BoutNaN; + VecGetValues(x, 1, &ind, &val); + xfield[i] = val; + } // Call the preconditioner solver FieldPerp yfield = pcsolve->solve(xfield); - // Put result into y - fieldToVec(yfield, y); + VecCopy(*PetscVector{yfield, indexer}.get(), y); + return 0; } diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.hxx b/src/invert/laplace/impls/petsc/petsc_laplace.hxx index 611bfd6fa1..977602dbcc 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.hxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.hxx @@ -41,11 +41,15 @@ RegisterUnavailableLaplace registerlaplacepetsc(LAPLACE_PETSC, #else +#include #include +#include #include #include #include +#include #include +#include #include @@ -59,12 +63,7 @@ class LaplacePetsc : public Laplacian { public: LaplacePetsc(Options* opt = nullptr, const CELL_LOC loc = CELL_CENTRE, Mesh* mesh_in = nullptr, Solver* solver = nullptr); - ~LaplacePetsc() { - KSPDestroy(&ksp); - VecDestroy(&xs); - VecDestroy(&bs); - MatDestroy(&MatA); - } + ~LaplacePetsc() override; using Laplacian::setCoefA; using Laplacian::setCoefC; @@ -199,9 +198,21 @@ public: int precon(Vec x, Vec y); ///< Preconditioner function private: - void Element(int i, int x, int z, int xshift, int zshift, PetscScalar ele, Mat& MatA); - void Coeffs(int x, int y, int z, BoutReal& A1, BoutReal& A2, BoutReal& A3, BoutReal& A4, - BoutReal& A5); + struct CoeffsA { + BoutReal A1; + BoutReal A2; + BoutReal A3; + BoutReal A4; + BoutReal A5; + }; + + /// Calculate the coefficients ``A1-5`` + CoeffsA Coeffs(Ind3D i); + + /// Set `operator2D` for the second order scheme + void setSecondOrderMatrix(int y, bool inner_X_neumann, bool outer_X_neumann); + /// Set `operator2D` for the fourth order scheme + void setFourthOrderMatrix(int y, bool inner_X_neumann, bool outer_X_neumann); /* Ex and Ez * Additional 1st derivative terms to allow for solution field to be @@ -220,17 +231,9 @@ private: FieldPerp sol; // solution Field - /// Istart is the first row of MatA owned by the process, Iend is 1 greater than the last row. - int Istart = -1; - int Iend = -1; - - /// Mesh sizes, total size, no of points on this processor - int meshx, meshz, size, localN; MPI_Comm comm; - Mat MatA = nullptr; ///< Stencil matrix - Vec xs = nullptr; ///< Solution vector - Vec bs = nullptr; ///< RHS vector - KSP ksp = nullptr; ///< PETSc solver + KSP ksp = nullptr; ///< PETSc solver + bool ksp_initialised = false; Options* opts; ///< Laplace Section Options Object std::string ksptype; ///< KSP solver type @@ -248,14 +251,13 @@ private: bool direct; //Use direct LU solver if true. bool fourth_order; + IndexerPtr indexer; + PetscMatrix operator2D; PetscLib lib; bool rightprec; // Right preconditioning std::unique_ptr pcsolve; // Laplacian solver for preconditioning - void vecToField(Vec x, FieldPerp& f); // Copy a vector into a fieldperp - void fieldToVec(const FieldPerp& f, Vec x); // Copy a fieldperp into a vector - static constexpr int implemented_flags = INVERT_START_NEW; static constexpr int implemented_boundary_flags = INVERT_AC_GRAD | INVERT_SET | INVERT_RHS; diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index d4f375e1d7..60240b39d7 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -37,6 +37,7 @@ #include "bout/options.hxx" #include "bout/options_io.hxx" #include "bout/output.hxx" +#include "bout/petsclib.hxx" #include "bout/traits.hxx" #include "bout/vecops.hxx" @@ -172,6 +173,10 @@ Field3D generate_a5(Mesh& mesh) { int main(int argc, char** argv) { BoutInitialise(argc, argv); + + // Need this here to ensure PETSc isn't finalised until after the global mesh, + // otherwise we get problems from `MPI_Comm_free` on the X communicator + PetscLib lib{}; { // Not be used for 3D metrics Options::root()["laplace"].setConditionallyUsed(); @@ -210,25 +215,6 @@ int main(int argc, char** argv) { // Solving equations of the form d*Delp2(f) + 1/c*Grad_perp(c).Grad_perp(f) + a*f = b for various f, a, c, d using bout::globals::mesh; - Field3D loop_x; - Field3D loop_z; - loop_x.allocate(); - loop_z.allocate(); - for (int jx = mesh->xstart; jx <= mesh->xend; jx++) { - const BoutReal x = mesh->GlobalX(jx); - for (int jy = 0; jy < mesh->LocalNy; jy++) { - for (int jz = mesh->zstart; jz <= mesh->zend; jz++) { - const BoutReal z = mesh->GlobalZ(jz); - loop_x(jx, jy, jz) = x; - loop_z(jx, jy, jz) = z; - } - } - } - dump["loop_x"] = loop_x; - dump["loop_z"] = loop_z; - dump["exp_x"] = FieldFactory::get()->create3D("x"); - dump["exp_z"] = FieldFactory::get()->create3D("z"); - // Only Neumann x-boundary conditions are implemented so far, so test functions should be Neumann in x and periodic in z. // Use Field3D's, but solver only works on FieldPerp slices, so only use 1 y-point From 7c0be3cd214948be0bc9c517157d7be9c4c12bf7 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 2 Mar 2026 10:15:50 +0000 Subject: [PATCH 399/461] Remove `TRACE` macro from `PetscMatrix` The string formatting done in a hot loop ends up being _very_ expensive, 10x slowdown --- include/bout/petsc_interface.hxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/include/bout/petsc_interface.hxx b/include/bout/petsc_interface.hxx index 5edd42a441..25f8fb4b97 100644 --- a/include/bout/petsc_interface.hxx +++ b/include/bout/petsc_interface.hxx @@ -394,7 +394,6 @@ public: private: void setValues(BoutReal val, InsertMode mode) { - TRACE("PetscMatrix setting values at ({}, {})", petscRow, petscCol); ASSERT3(positions.size() > 0); std::vector values; std::transform(weights.begin(), weights.end(), std::back_inserter(values), @@ -405,7 +404,8 @@ public: status = MatSetValues(*petscMatrix, 1, &petscRow, positions.size(), positions.data(), values.data(), mode); if (status != 0) { - throw BoutException("Error when setting elements of a PETSc matrix."); + throw BoutException("Error when setting elements of a PETSc matrix at ({}, {})", + petscRow, petscCol); } } Mat* petscMatrix; From 0f0723b1101750398003a706e5f7296b0486aa0b Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 2 Mar 2026 10:16:37 +0000 Subject: [PATCH 400/461] Include all headers in petsc interface --- include/bout/petsc_interface.hxx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/include/bout/petsc_interface.hxx b/include/bout/petsc_interface.hxx index 25f8fb4b97..5239039f0c 100644 --- a/include/bout/petsc_interface.hxx +++ b/include/bout/petsc_interface.hxx @@ -40,18 +40,27 @@ #include #include +#include +#include #include #include +#include #include #include +#include #include #include #include #include #include +#include +#include #include #include +#include + +#include /*! * A class which wraps PETSc vector objects, allowing them to be From 48a1e03ec25f74c192a6bfd6255542e6f23bee22 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 2 Mar 2026 11:31:18 +0000 Subject: [PATCH 401/461] Use preconditioner for petsc MAST grids test --- tests/integrated/test-petsc_laplace_MAST-grid/data/BOUT.inp | 2 ++ .../test_petsc_laplace_MAST_grid.cxx | 4 ++++ 2 files changed, 6 insertions(+) diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/data/BOUT.inp b/tests/integrated/test-petsc_laplace_MAST-grid/data/BOUT.inp index a63e7e876c..4bd3c40bd8 100644 --- a/tests/integrated/test-petsc_laplace_MAST-grid/data/BOUT.inp +++ b/tests/integrated/test-petsc_laplace_MAST-grid/data/BOUT.inp @@ -23,6 +23,7 @@ nonuniform = true rtol = 1e-10 include_yguards = false maxits = 1000 +pctype = sor [petsc4th] type = petsc @@ -31,6 +32,7 @@ nonuniform = true rtol = 1e-10 include_yguards = false maxits = 1000 +pctype = sor fourth_order = true gmres_max_steps = 32 diff --git a/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx b/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx index 945d5c6443..3f9bb172e2 100644 --- a/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx +++ b/tests/integrated/test-petsc_laplace_MAST-grid/test_petsc_laplace_MAST_grid.cxx @@ -23,6 +23,7 @@ * **************************************************************************/ +#include "bout/petsclib.hxx" #include #include // #include @@ -36,6 +37,9 @@ BoutReal max_error_at_ystart(const Field3D& error); int main(int argc, char** argv) { BoutInitialise(argc, argv); + + PetscLib lib{}; + { Options* options = Options::getRoot()->getSection("petsc2nd"); auto invert = Laplacian::create(options); From e02167d29ccabf041bc80a7eaaae84ee133aa569 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 15:43:21 +0000 Subject: [PATCH 402/461] Remove `todo` comment --- src/invert/laplace/impls/petsc/petsc_laplace.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index da964d443d..132b3c3137 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -73,7 +73,6 @@ PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { PetscFunctionReturn(laplace->precon(x, y)); // NOLINT } -// TODO: handle fourth order auto set_stencil(const Mesh& localmesh, bool fourth_order) { OperatorStencil stencil; IndexOffset zero; From 2dcebb8c11891e52b4a8e875f4524267fc874aa4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 15:43:35 +0000 Subject: [PATCH 403/461] Fix missing headers --- src/invert/laplace/impls/petsc/petsc_laplace.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 132b3c3137..69ed686321 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -47,6 +47,7 @@ #include #include +#include #include #include @@ -143,7 +144,7 @@ auto set_stencil(const Mesh& localmesh, bool fourth_order) { } } - stencil.add([](IndPerp UNUSED(ind)) -> bool { return true; }, {zero}); + stencil.add([]([[maybe_unused]] IndPerp ind) -> bool { return true; }, {zero}); return stencil; } } // namespace From e1e1f6b4134e07527f0a9f47695d84fda2e9cfb0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Mon, 9 Mar 2026 16:37:52 +0000 Subject: [PATCH 404/461] Apply clang-tidy fixes --- .../laplace/impls/petsc/petsc_laplace.cxx | 40 +++++++++---------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 69ed686321..a3b81f27ff 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -99,7 +99,7 @@ auto set_stencil(const Mesh& localmesh, bool fourth_order) { }); } - std::vector offsetsVec(offsets.begin(), offsets.end()); + const std::vector offsetsVec(offsets.begin(), offsets.end()); stencil.add( [&localmesh](IndPerp ind) -> bool { return (localmesh.xstart <= ind.x() && ind.x() <= localmesh.xend @@ -249,7 +249,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { const int y = b.getIndex(); // Get the Y index { - Timer timer("petscsetup"); + const Timer timer("petscsetup"); const bool inner_X_neumann = isInnerBoundaryFlagSet(INVERT_AC_GRAD); const bool outer_X_neumann = isOuterBoundaryFlagSet(INVERT_AC_GRAD); @@ -276,7 +276,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { #else KSPSetOperators(ksp, *operator2D.get(), *operator2D.get(), DIFFERENT_NONZERO_PATTERN); #endif - PC pc; // The preconditioner option + PC pc = nullptr; // The preconditioner option if (direct) { // If a direct solver has been chosen // Get the preconditioner @@ -355,11 +355,11 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Call the actual solver { - Timer timer("petscsolve"); + const Timer timer("petscsolve"); KSPSolve(ksp, *rhs.get(), *guess.get()); } - KSPConvergedReason reason; + KSPConvergedReason reason = KSP_CONVERGED_ITERATING; KSPGetConvergedReason(ksp, &reason); if (reason == -3) { // Too many iterations, might be fixed by taking smaller timestep throw BoutIterationFail("petsc_laplace: too many iterations"); @@ -454,15 +454,15 @@ LaplacePetsc::CoeffsA LaplacePetsc::Coeffs(Ind3D i) { // A second/fourth order derivative term if (issetC) { if ((x > 1) && (x < (localmesh->LocalNx - 2))) { - BoutReal ddx_C; - BoutReal ddz_C; + BoutReal ddx_C = BoutNaN; + BoutReal ddz_C = BoutNaN; if (fourth_order) { // Fourth order discretization of C in x - ddx_C = (-C2[i.xpp()] + 8. * C2[i.xp()] - 8. * C2[i.xm()] + C2[i.xmm()]) + ddx_C = (-C2[i.xpp()] + (8. * C2[i.xp()]) - (8. * C2[i.xm()]) + C2[i.xmm()]) / (12. * coords->dx[i] * (C1[i])); // Fourth order discretization of C in z - ddz_C = (-C2[i.zpp()] + 8. * C2[i.zp()] - 8. * C2[i.zm()] + C2[i.zmm()]) + ddz_C = (-C2[i.zpp()] + (8. * C2[i.zp()]) - (8. * C2[i.zm()]) + C2[i.zmm()]) / (12. * coords->dz[i] * (C1[i])); } else { // Second order discretization of C in x @@ -545,11 +545,11 @@ void LaplacePetsc::setSecondOrderMatrix(int y, bool inner_X_neumann, ASSERT3(std::isfinite(A4)); ASSERT3(std::isfinite(A5)); - BoutReal dx = coords->dx[i]; - BoutReal dx2 = SQ(dx); - BoutReal dz = coords->dz[i]; - BoutReal dz2 = SQ(dz); - BoutReal dxdz = dx * dz; + const BoutReal dx = coords->dx[i]; + const BoutReal dx2 = SQ(dx); + const BoutReal dz = coords->dz[i]; + const BoutReal dz2 = SQ(dz); + const BoutReal dxdz = dx * dz; operator2D(l, l) = A0 - (2.0 * ((A1 / dx2) + (A2 / dz2))); operator2D(l, l.xm().zm()) = A3 / (4.0 * dxdz); operator2D(l, l.xm()) = (A1 / dx2) - (A4 / (2.0 * dx)); @@ -628,11 +628,11 @@ void LaplacePetsc::setFourthOrderMatrix(int y, bool inner_X_neumann, ASSERT3(std::isfinite(A4)); ASSERT3(std::isfinite(A5)); - BoutReal dx = coords->dx[i]; - BoutReal dx2 = SQ(dx); - BoutReal dz = coords->dz[i]; - BoutReal dz2 = SQ(dz); - BoutReal dxdz = dx * dz; + const BoutReal dx = coords->dx[i]; + const BoutReal dx2 = SQ(dx); + const BoutReal dz = coords->dz[i]; + const BoutReal dz2 = SQ(dz); + const BoutReal dxdz = dx * dz; operator2D(l, l) = A0 - ((5.0 / 2.0) * ((A1 / dx2) + (A2 / dz2))); operator2D(l, l.xmm().zmm()) = A3 / (144.0 * dxdz); @@ -675,7 +675,7 @@ int LaplacePetsc::precon(Vec x, Vec y) { } // Call the preconditioner solver - FieldPerp yfield = pcsolve->solve(xfield); + const FieldPerp yfield = pcsolve->solve(xfield); VecCopy(*PetscVector{yfield, indexer}.get(), y); From abbbec7635c35d682bd241d5a973f55c2b9c2ae0 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 11:08:43 +0000 Subject: [PATCH 405/461] Remove some unused defines --- src/invert/laplace/impls/petsc/petsc_laplace.cxx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index a3b81f27ff..71a3e4433b 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -51,19 +51,6 @@ #include #include -#define KSP_RICHARDSON "richardson" -#define KSP_CHEBYSHEV "chebyshev" -#define KSP_CG "cg" -#define KSP_GMRES "gmres" -#define KSP_TCQMR "tcqmr" -#define KSP_BCGS "bcgs" -#define KSP_CGS "cgs" -#define KSP_TFQMR "tfqmr" -#define KSP_CR "cr" -#define KSP_LSQR "lsqr" -#define KSP_BICG "bicg" -#define KSP_PREONLY "preonly" - namespace { PetscErrorCode laplacePCapply(PC pc, Vec x, Vec y) { PetscFunctionBegin; // NOLINT From 9b778bee9a3dcabb15891595b367f6e7a53135d9 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 11:33:57 +0000 Subject: [PATCH 406/461] Always use `sor` preconditioner in PETSc Laplace test for 3D metrics For some reason, `PETSC_HAVE_MUMPS` doesn't seem to always be enough to detect if PETSc actually has the mump preconditioner --- .../test-petsc_laplace/test_petsc_laplace.cxx | 12 ++---------- 1 file changed, 2 insertions(+), 10 deletions(-) diff --git a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx index 60240b39d7..39b1918480 100644 --- a/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx +++ b/tests/integrated/test-petsc_laplace/test_petsc_laplace.cxx @@ -182,16 +182,8 @@ int main(int argc, char** argv) { Options::root()["laplace"].setConditionallyUsed(); // For 3D metrics, we need to use one of PETSc's preconditioners - // and mumps seems to be the best? Or at least HYPRE doesn't work - // on this test with more than one processor. -#ifdef PETSC_HAVE_MUMPS - constexpr auto petsc_has_mumps = true; -#else - constexpr auto petsc_has_mumps = false; -#endif - - // Preconditioner to use for 3D metrics - constexpr auto petsc_pc = petsc_has_mumps ? "mumps" : "sor"; + // and `sor` seems to always be available + constexpr auto petsc_pc = "sor"; auto& options_2nd = Options::root()["petsc2nd"]; if constexpr (bout::build::use_metric_3d) { From 74b00f8ffa652a3905d7f4eed954e0515386ff4d Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 16:02:58 +0000 Subject: [PATCH 407/461] docs: Expand description of developer tools --- manual/sphinx/developer_docs/contributing.rst | 111 ++++++++++++------ 1 file changed, 72 insertions(+), 39 deletions(-) diff --git a/manual/sphinx/developer_docs/contributing.rst b/manual/sphinx/developer_docs/contributing.rst index a7aca902d4..4ef9892bff 100644 --- a/manual/sphinx/developer_docs/contributing.rst +++ b/manual/sphinx/developer_docs/contributing.rst @@ -142,45 +142,6 @@ then they will be able to checkout your branch:: *Note*: If you have write access to the central BOUT-dev repository, you can push your branches there. -Running Tests -~~~~~~~~~~~~~ - -We run many tests and checks automatically on GitHub for a variety of build -configurations. See :ref:`sec-runtestsuite` for how to run them -locally. Running the full test suite can take some time, but it's a good idea to -run at least the unit tests locally when you're making changes. - -Install Pre-Commit Hooks (Optional but recommended) -~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - -Along with the automated tests, we also run things like formatters and static -analysis. For frequent developers, we strongly recommend installing them locally -and building them into your regular workflow. You can install the majority of -our developer tools at once using `uv `_: - -.. code-block:: console - - uv sync --only-dev - -This will create a new virtual environment (typically under ``.venv``) and -install all the tools into that. You can then activate the virtual environment -and use the tools manually, or via integrations in your editor. - -We also have a `prek `_ config that can run some of -these automatically when you make commits using "pre-commit hooks". Install our -developer tools with ``uv`` as above, and then install the pre-commit hook: - -.. code-block:: console - - prek install - -This will then automatically call things like `clang-format -`_ and `ruff -`_ when you create a commit, and abort the commit if -there's a problem, for example if a formatter changes any files. For the -formatters, this usually just means staging any changes they make and trying -again. - Making changes, commits ~~~~~~~~~~~~~~~~~~~~~~~ @@ -212,6 +173,78 @@ give you comments to improve the code. You can make additional changes and push them to the same feature branch and they will be automatically added to the pull request. +Running Tests +~~~~~~~~~~~~~ + +We run many tests and checks automatically on GitHub (collectively called "CI") +for a variety of build configurations. See :ref:`sec-runtestsuite` for how to +run them locally. Running the full test suite can take some time, but it's a +good idea to run at least the unit tests locally when you're making changes: + +.. code-block:: console + + cmake --build build --target check-unit-tests + +Along with the automated tests, we also run things like formatters and static +analysis in CI, which may provide further feedback (see :ref:`sec-coding-style` +for more details). If your PR fails the format check on CI, please install the +formatters and run them according to the next section. Usually this is just: + +.. code-block:: console + + git clang-format next + +which will format just the parts of C++ files that have changed since ``next``. + +Formatting and Linters +~~~~~~~~~~~~~~~~~~~~~~ + +For frequent developers, we strongly recommend installing the formatting and +linting tools locally and building them into your regular workflow. You can +install the majority of our developer tools at once using `uv +`_ from the top of your BOUT++ directory: + +.. code-block:: console + + uv sync --only-dev --inexact + +This will install all the developer tools into a virtual environment (creating +one if necessary, typically under ``.venv``). You can then activate the virtual +environment and use the tools manually, or via integrations in your editor. + +We *strongly* recommend using ``uv`` to install the developer tools, as this +will ensure that you get the *exact* versions used in CI and by other +developers, but you could also use ``pip install --group dev`` if you wish. + +The quickest way to run all the formatters on a PR at once is typically: + +.. code-block:: console + + uv tool run prek run --from-ref next + +This will run all our formatters on files that have changed since ``next``. + +Install Pre-Commit Hooks (Optional but recommended) +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +We also have a `prek `_ config that can run some of +these automatically using pre-commit hooks -- that is, when you run ``git +commit``, these tools will run and abort the commit if they change any +files. You then just have to stage the new changes and try again. + +Install our developer tools with ``uv`` as above, and then install the +pre-commit hook: + +.. code-block:: console + + prek install + +That's it! ``prek`` will install the tools into their own separate virtual +environment so they won't interfere with any you may have, and ``git`` will take +care of running them automatically. + +.. _sec-coding-style: + Coding Style ------------ From c37f6988f6c5c599968d736fac98f19b8c88ea09 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 16:04:17 +0000 Subject: [PATCH 408/461] CI: Use annotations instead of comments for `clang-tidy-review` --- .github/workflows/clang-tidy-review.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/clang-tidy-review.yml b/.github/workflows/clang-tidy-review.yml index 087c910987..7c245a7a3c 100644 --- a/.github/workflows/clang-tidy-review.yml +++ b/.github/workflows/clang-tidy-review.yml @@ -24,6 +24,7 @@ jobs: uses: ZedThree/clang-tidy-review@v0.23.0 id: review with: + annotations: true build_dir: build apt_packages: "libfftw3-dev,libnetcdf-c++4-dev,libopenmpi-dev,petsc-dev,slepc-dev,liblapack-dev,libparpack2-dev,libsundials-dev,uuid-dev" config_file: ".clang-tidy" From b74b9170b4d64de6f1010ae4c4a3997f5a1c8380 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 16:52:25 +0000 Subject: [PATCH 409/461] Fix setting yindex for shell preconditioner --- src/invert/laplace/impls/petsc/petsc_laplace.cxx | 12 +++++++----- src/invert/laplace/impls/petsc/petsc_laplace.hxx | 3 ++- 2 files changed, 9 insertions(+), 6 deletions(-) diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.cxx b/src/invert/laplace/impls/petsc/petsc_laplace.cxx index 71a3e4433b..e1595a2817 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.cxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.cxx @@ -234,7 +234,9 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { checkFlags(); #endif - const int y = b.getIndex(); // Get the Y index + // Set member variable so that we can pass through to shell preconditioner if + // required + yindex = b.getIndex(); { const Timer timer("petscsetup"); @@ -243,9 +245,9 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { // Set the operator matrix if (fourth_order) { - setFourthOrderMatrix(y, inner_X_neumann, outer_X_neumann); + setFourthOrderMatrix(yindex, inner_X_neumann, outer_X_neumann); } else { - setSecondOrderMatrix(y, inner_X_neumann, outer_X_neumann); + setSecondOrderMatrix(yindex, inner_X_neumann, outer_X_neumann); } operator2D.assemble(); @@ -358,7 +360,7 @@ FieldPerp LaplacePetsc::solve(const FieldPerp& b, const FieldPerp& x0) { } auto sol = guess.toField(); - sol.setIndex(y); + sol.setIndex(yindex); checkData(sol); // Return the solution @@ -651,7 +653,7 @@ void LaplacePetsc::setFourthOrderMatrix(int y, bool inner_X_neumann, /// Preconditioner function int LaplacePetsc::precon(Vec x, Vec y) { - FieldPerp xfield(indexer->getMesh(), location, sol.getIndex()); + FieldPerp xfield(indexer->getMesh(), location, yindex); xfield = 0.0; BOUT_FOR_SERIAL(i, indexer->getRegionAll()) { diff --git a/src/invert/laplace/impls/petsc/petsc_laplace.hxx b/src/invert/laplace/impls/petsc/petsc_laplace.hxx index 977602dbcc..abef05c0ae 100644 --- a/src/invert/laplace/impls/petsc/petsc_laplace.hxx +++ b/src/invert/laplace/impls/petsc/petsc_laplace.hxx @@ -229,7 +229,8 @@ private: bool issetC; bool issetE; - FieldPerp sol; // solution Field + /// Y-index of solution field. + int yindex = -1; MPI_Comm comm; KSP ksp = nullptr; ///< PETSc solver From d8a1b6661aca197f3996c9116b92cc01847b5a1e Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 17:46:12 +0000 Subject: [PATCH 410/461] Add basic custom formatter for fields --- include/bout/output_bout_types.hxx | 64 ++++++++- tests/unit/CMakeLists.txt | 1 + tests/unit/sys/test_output_bout_types.cxx | 151 ++++++++++++++++++++++ 3 files changed, 214 insertions(+), 2 deletions(-) create mode 100644 tests/unit/sys/test_output_bout_types.cxx diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index b67762521b..43d1b4adfa 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -5,9 +5,13 @@ #ifndef OUTPUT_BOUT_TYPES_H #define OUTPUT_BOUT_TYPES_H +#include "fmt/base.h" #include -#include "bout/output.hxx" +#include "bout/bout_types.hxx" +#include "bout/field2d.hxx" +#include "bout/mesh.hxx" +#include "bout/output.hxx" // IWYU pragma: keep #include "bout/region.hxx" template @@ -17,7 +21,8 @@ struct fmt::formatter> { // Parses format specifications of the form ['c' | 'i']. constexpr auto parse(format_parse_context& ctx) { - auto it = ctx.begin(), end = ctx.end(); + const auto* it = ctx.begin(); + const auto* end = ctx.end(); if (it != end && (*it == 'c' || *it == 'i')) { presentation = *it++; } @@ -50,4 +55,59 @@ struct fmt::formatter> { } }; +class Field2D; +class Field3D; +class FieldPerp; + +template <> +struct fmt::formatter : fmt::formatter { + auto format(const Field2D& f, format_context& ctx) const -> format_context::iterator { + const auto* mesh = f.getMesh(); + for (int ix = 0; ix < mesh->LocalNx; ++ix) { + for (int jy = 0; jy < mesh->LocalNy; ++jy) { + format_to(ctx.out(), "({}, {}): ", ix, jy); + formatter::format(f(ix, jy), ctx); + format_to(ctx.out(), (jy < mesh->LocalNy - 1) ? "; " : ";"); + } + format_to(ctx.out(), "\n"); + } + return format_to(ctx.out(), "\n"); + } +}; + +template <> +struct fmt::formatter : fmt::formatter { + auto format(const Field3D& f, format_context& ctx) const -> format_context::iterator { + const auto* mesh = f.getMesh(); + for (int ix = 0; ix < mesh->LocalNx; ++ix) { + for (int jy = 0; jy < mesh->LocalNy; ++jy) { + for (int kz = 0; kz < mesh->LocalNz; ++kz) { + format_to(ctx.out(), "({}, {}, {}): ", ix, jy, kz); + formatter::format(f(ix, jy, kz), ctx); + format_to(ctx.out(), (kz < mesh->LocalNz - 1) ? "; " : ";"); + } + format_to(ctx.out(), "\n"); + } + format_to(ctx.out(), "\n"); + } + return format_to(ctx.out(), "\n"); + } +}; + +template <> +struct fmt::formatter : fmt::formatter { + auto format(const FieldPerp& f, format_context& ctx) const -> format_context::iterator { + const auto* mesh = f.getMesh(); + for (int ix = 0; ix < mesh->LocalNx; ++ix) { + for (int kz = 0; kz < mesh->LocalNz; ++kz) { + format_to(ctx.out(), "({}, {}): ", ix, kz); + formatter::format(f(ix, kz), ctx); + format_to(ctx.out(), (kz < mesh->LocalNz - 1) ? "; " : ";"); + } + format_to(ctx.out(), "\n"); + } + return format_to(ctx.out(), "\n"); + } +}; + #endif // OUTPUT_BOUT_TYPES_H diff --git a/tests/unit/CMakeLists.txt b/tests/unit/CMakeLists.txt index 7cf0bb0af7..b347de7354 100644 --- a/tests/unit/CMakeLists.txt +++ b/tests/unit/CMakeLists.txt @@ -121,6 +121,7 @@ set(serial_tests_source ./fake_mesh.hxx ./fake_mesh_fixture.hxx ./src/test_bout++.cxx + ./sys/test_output_bout_types.cxx ) if(BOUT_HAS_HYPRE) diff --git a/tests/unit/sys/test_output_bout_types.cxx b/tests/unit/sys/test_output_bout_types.cxx new file mode 100644 index 0000000000..b3a7493803 --- /dev/null +++ b/tests/unit/sys/test_output_bout_types.cxx @@ -0,0 +1,151 @@ +#include "fake_mesh_fixture.hxx" +#include "test_extras.hxx" +#include "gtest/gtest.h" + +#include "bout/field2d.hxx" +#include "bout/globals.hxx" +#include "bout/output_bout_types.hxx" + +#include + +#include + +using FormatFieldTest = FakeMeshFixture_tmpl<3, 5, 2>; + +TEST_F(FormatFieldTest, Field2D) { + Field2D f{bout::globals::mesh}; + + fillField(f, {{0., 1., 2., 3., 4.}, {5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14.}}); + + const auto out = fmt::format("{}", f); + + const std::string expected = + R"((0, 0): 0; (0, 1): 1; (0, 2): 2; (0, 3): 3; (0, 4): 4; +(1, 0): 5; (1, 1): 6; (1, 2): 7; (1, 3): 8; (1, 4): 9; +(2, 0): 10; (2, 1): 11; (2, 2): 12; (2, 3): 13; (2, 4): 14; + +)"; + EXPECT_EQ(out, expected); +} + +TEST_F(FormatFieldTest, Field2DSpec) { + Field2D f{bout::globals::mesh}; + + fillField(f, {{0., 1., 2., 3., 4.}, {5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14.}}); + + const auto out = fmt::format("{:3.1e}", f); + + const std::string expected = + R"((0, 0): 0.0e+00; (0, 1): 1.0e+00; (0, 2): 2.0e+00; (0, 3): 3.0e+00; (0, 4): 4.0e+00; +(1, 0): 5.0e+00; (1, 1): 6.0e+00; (1, 2): 7.0e+00; (1, 3): 8.0e+00; (1, 4): 9.0e+00; +(2, 0): 1.0e+01; (2, 1): 1.1e+01; (2, 2): 1.2e+01; (2, 3): 1.3e+01; (2, 4): 1.4e+01; + +)"; + EXPECT_EQ(out, expected); +} + +TEST_F(FormatFieldTest, Field3D) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + const auto out = fmt::format("{}", f); + + const std::string expected = + R"((0, 0, 0): 0; (0, 0, 1): 1; +(0, 1, 0): 2; (0, 1, 1): 3; +(0, 2, 0): 4; (0, 2, 1): 5; +(0, 3, 0): 6; (0, 3, 1): 7; +(0, 4, 0): 8; (0, 4, 1): 9; + +(1, 0, 0): 10; (1, 0, 1): 11; +(1, 1, 0): 12; (1, 1, 1): 13; +(1, 2, 0): 14; (1, 2, 1): 15; +(1, 3, 0): 16; (1, 3, 1): 17; +(1, 4, 0): 18; (1, 4, 1): 19; + +(2, 0, 0): 20; (2, 0, 1): 21; +(2, 1, 0): 22; (2, 1, 1): 23; +(2, 2, 0): 24; (2, 2, 1): 25; +(2, 3, 0): 26; (2, 3, 1): 27; +(2, 4, 0): 28; (2, 4, 1): 29; + + +)"; + EXPECT_EQ(out, expected); +} + +TEST_F(FormatFieldTest, Field3DSpec) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + const auto out = fmt::format("{:3.1e}", f); + + const std::string expected = + R"((0, 0, 0): 0.0e+00; (0, 0, 1): 1.0e+00; +(0, 1, 0): 2.0e+00; (0, 1, 1): 3.0e+00; +(0, 2, 0): 4.0e+00; (0, 2, 1): 5.0e+00; +(0, 3, 0): 6.0e+00; (0, 3, 1): 7.0e+00; +(0, 4, 0): 8.0e+00; (0, 4, 1): 9.0e+00; + +(1, 0, 0): 1.0e+01; (1, 0, 1): 1.1e+01; +(1, 1, 0): 1.2e+01; (1, 1, 1): 1.3e+01; +(1, 2, 0): 1.4e+01; (1, 2, 1): 1.5e+01; +(1, 3, 0): 1.6e+01; (1, 3, 1): 1.7e+01; +(1, 4, 0): 1.8e+01; (1, 4, 1): 1.9e+01; + +(2, 0, 0): 2.0e+01; (2, 0, 1): 2.1e+01; +(2, 1, 0): 2.2e+01; (2, 1, 1): 2.3e+01; +(2, 2, 0): 2.4e+01; (2, 2, 1): 2.5e+01; +(2, 3, 0): 2.6e+01; (2, 3, 1): 2.7e+01; +(2, 4, 0): 2.8e+01; (2, 4, 1): 2.9e+01; + + +)"; + EXPECT_EQ(out, expected); +} + +TEST_F(FormatFieldTest, FieldPerp) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + FieldPerp g = sliceXZ(f, 0); + + const auto out = fmt::format("{}", g); + + const std::string expected = + R"((0, 0): 0; (0, 1): 1; +(1, 0): 10; (1, 1): 11; +(2, 0): 20; (2, 1): 21; + +)"; + EXPECT_EQ(out, expected); +} + +TEST_F(FormatFieldTest, FieldPerpSpec) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + FieldPerp g = sliceXZ(f, 0); + + const auto out = fmt::format("{:3.1e}", g); + + const std::string expected = + R"((0, 0): 0.0e+00; (0, 1): 1.0e+00; +(1, 0): 1.0e+01; (1, 1): 1.1e+01; +(2, 0): 2.0e+01; (2, 1): 2.1e+01; + +)"; + EXPECT_EQ(out, expected); +} From 459724c1712ead503e83dff52e95e37c593b6b88 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 21:08:22 +0000 Subject: [PATCH 411/461] Consolidate Field formatters into one implementation --- include/bout/output_bout_types.hxx | 82 +++++++++-------------- tests/unit/sys/test_output_bout_types.cxx | 66 +++++++++++------- 2 files changed, 75 insertions(+), 73 deletions(-) diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index 43d1b4adfa..171188664c 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -5,14 +5,16 @@ #ifndef OUTPUT_BOUT_TYPES_H #define OUTPUT_BOUT_TYPES_H -#include "fmt/base.h" -#include - #include "bout/bout_types.hxx" -#include "bout/field2d.hxx" #include "bout/mesh.hxx" #include "bout/output.hxx" // IWYU pragma: keep #include "bout/region.hxx" +#include "bout/traits.hxx" + +#include "fmt/base.h" +#include + +#include template struct fmt::formatter> { @@ -55,58 +57,38 @@ struct fmt::formatter> { } }; -class Field2D; -class Field3D; -class FieldPerp; - -template <> -struct fmt::formatter : fmt::formatter { - auto format(const Field2D& f, format_context& ctx) const -> format_context::iterator { +/// Formatter for Fields +template +struct fmt::formatter, char>> : fmt::formatter { + auto format(const T& f, format_context& ctx) const -> format_context::iterator { const auto* mesh = f.getMesh(); - for (int ix = 0; ix < mesh->LocalNx; ++ix) { - for (int jy = 0; jy < mesh->LocalNy; ++jy) { - format_to(ctx.out(), "({}, {}): ", ix, jy); - formatter::format(f(ix, jy), ctx); - format_to(ctx.out(), (jy < mesh->LocalNy - 1) ? "; " : ";"); - } - format_to(ctx.out(), "\n"); - } - return format_to(ctx.out(), "\n"); - } -}; + int previous_x = 0; + int previous_y = 0; + int previous_z = 0; -template <> -struct fmt::formatter : fmt::formatter { - auto format(const Field3D& f, format_context& ctx) const -> format_context::iterator { - const auto* mesh = f.getMesh(); - for (int ix = 0; ix < mesh->LocalNx; ++ix) { - for (int jy = 0; jy < mesh->LocalNy; ++jy) { - for (int kz = 0; kz < mesh->LocalNz; ++kz) { - format_to(ctx.out(), "({}, {}, {}): ", ix, jy, kz); - formatter::format(f(ix, jy, kz), ctx); - format_to(ctx.out(), (kz < mesh->LocalNz - 1) ? "; " : ";"); - } + BOUT_FOR_SERIAL(i, f.getRegion("RGN_ALL")) { + const auto ix = i.x(); + const auto iy = i.y(); + const auto iz = i.z(); + + if (iz > previous_z) { + format_to(ctx.out(), " "); + } + if (iy > previous_y) { format_to(ctx.out(), "\n"); } - format_to(ctx.out(), "\n"); - } - return format_to(ctx.out(), "\n"); - } -}; - -template <> -struct fmt::formatter : fmt::formatter { - auto format(const FieldPerp& f, format_context& ctx) const -> format_context::iterator { - const auto* mesh = f.getMesh(); - for (int ix = 0; ix < mesh->LocalNx; ++ix) { - for (int kz = 0; kz < mesh->LocalNz; ++kz) { - format_to(ctx.out(), "({}, {}): ", ix, kz); - formatter::format(f(ix, kz), ctx); - format_to(ctx.out(), (kz < mesh->LocalNz - 1) ? "; " : ";"); + if (ix > previous_x) { + format_to(ctx.out(), "\n\n"); } - format_to(ctx.out(), "\n"); + + format_to(ctx.out(), "{:c}: ", i); + formatter::format(f[i], ctx); + format_to(ctx.out(), ";"); + previous_x = ix; + previous_y = iy; + previous_z = iz; } - return format_to(ctx.out(), "\n"); + return format_to(ctx.out(), ""); } }; diff --git a/tests/unit/sys/test_output_bout_types.cxx b/tests/unit/sys/test_output_bout_types.cxx index b3a7493803..82eb1754d0 100644 --- a/tests/unit/sys/test_output_bout_types.cxx +++ b/tests/unit/sys/test_output_bout_types.cxx @@ -3,8 +3,10 @@ #include "gtest/gtest.h" #include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/fieldperp.hxx" #include "bout/globals.hxx" -#include "bout/output_bout_types.hxx" +#include "bout/output_bout_types.hxx" // IWYU pragma: keep #include @@ -20,11 +22,23 @@ TEST_F(FormatFieldTest, Field2D) { const auto out = fmt::format("{}", f); const std::string expected = - R"((0, 0): 0; (0, 1): 1; (0, 2): 2; (0, 3): 3; (0, 4): 4; -(1, 0): 5; (1, 1): 6; (1, 2): 7; (1, 3): 8; (1, 4): 9; -(2, 0): 10; (2, 1): 11; (2, 2): 12; (2, 3): 13; (2, 4): 14; - -)"; + R"((0, 0): 0; +(0, 1): 1; +(0, 2): 2; +(0, 3): 3; +(0, 4): 4; + +(1, 0): 5; +(1, 1): 6; +(1, 2): 7; +(1, 3): 8; +(1, 4): 9; + +(2, 0): 10; +(2, 1): 11; +(2, 2): 12; +(2, 3): 13; +(2, 4): 14;)"; EXPECT_EQ(out, expected); } @@ -36,11 +50,23 @@ TEST_F(FormatFieldTest, Field2DSpec) { const auto out = fmt::format("{:3.1e}", f); const std::string expected = - R"((0, 0): 0.0e+00; (0, 1): 1.0e+00; (0, 2): 2.0e+00; (0, 3): 3.0e+00; (0, 4): 4.0e+00; -(1, 0): 5.0e+00; (1, 1): 6.0e+00; (1, 2): 7.0e+00; (1, 3): 8.0e+00; (1, 4): 9.0e+00; -(2, 0): 1.0e+01; (2, 1): 1.1e+01; (2, 2): 1.2e+01; (2, 3): 1.3e+01; (2, 4): 1.4e+01; - -)"; + R"((0, 0): 0.0e+00; +(0, 1): 1.0e+00; +(0, 2): 2.0e+00; +(0, 3): 3.0e+00; +(0, 4): 4.0e+00; + +(1, 0): 5.0e+00; +(1, 1): 6.0e+00; +(1, 2): 7.0e+00; +(1, 3): 8.0e+00; +(1, 4): 9.0e+00; + +(2, 0): 1.0e+01; +(2, 1): 1.1e+01; +(2, 2): 1.2e+01; +(2, 3): 1.3e+01; +(2, 4): 1.4e+01;)"; EXPECT_EQ(out, expected); } @@ -70,10 +96,7 @@ TEST_F(FormatFieldTest, Field3D) { (2, 1, 0): 22; (2, 1, 1): 23; (2, 2, 0): 24; (2, 2, 1): 25; (2, 3, 0): 26; (2, 3, 1): 27; -(2, 4, 0): 28; (2, 4, 1): 29; - - -)"; +(2, 4, 0): 28; (2, 4, 1): 29;)"; EXPECT_EQ(out, expected); } @@ -103,10 +126,7 @@ TEST_F(FormatFieldTest, Field3DSpec) { (2, 1, 0): 2.2e+01; (2, 1, 1): 2.3e+01; (2, 2, 0): 2.4e+01; (2, 2, 1): 2.5e+01; (2, 3, 0): 2.6e+01; (2, 3, 1): 2.7e+01; -(2, 4, 0): 2.8e+01; (2, 4, 1): 2.9e+01; - - -)"; +(2, 4, 0): 2.8e+01; (2, 4, 1): 2.9e+01;)"; EXPECT_EQ(out, expected); } @@ -123,10 +143,10 @@ TEST_F(FormatFieldTest, FieldPerp) { const std::string expected = R"((0, 0): 0; (0, 1): 1; + (1, 0): 10; (1, 1): 11; -(2, 0): 20; (2, 1): 21; -)"; +(2, 0): 20; (2, 1): 21;)"; EXPECT_EQ(out, expected); } @@ -143,9 +163,9 @@ TEST_F(FormatFieldTest, FieldPerpSpec) { const std::string expected = R"((0, 0): 0.0e+00; (0, 1): 1.0e+00; + (1, 0): 1.0e+01; (1, 1): 1.1e+01; -(2, 0): 2.0e+01; (2, 1): 2.1e+01; -)"; +(2, 0): 2.0e+01; (2, 1): 2.1e+01;)"; EXPECT_EQ(out, expected); } From 6b0a366887c995141ca1cfe0a236bca3f39deb93 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 24 Feb 2026 21:45:36 +0000 Subject: [PATCH 412/461] Add region to Field format spec --- include/bout/output_bout_types.hxx | 65 ++++++++++++++++++++--- tests/unit/sys/test_output_bout_types.cxx | 25 +++++++-- 2 files changed, 80 insertions(+), 10 deletions(-) diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index 171188664c..a30830f934 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -11,9 +11,12 @@ #include "bout/region.hxx" #include "bout/traits.hxx" -#include "fmt/base.h" +#include #include +#include +#include +#include #include template @@ -59,14 +62,62 @@ struct fmt::formatter> { /// Formatter for Fields template -struct fmt::formatter, char>> : fmt::formatter { +struct fmt::formatter, char>> { +private: + fmt::formatter underlying; + + static constexpr auto default_region = "RGN_ALL"; + std::string_view region = default_region; + +public: + constexpr auto parse(format_parse_context& ctx) { + const auto* it = ctx.begin(); + const auto* end = ctx.end(); + + if (it == end) { + return underlying.parse(ctx); + } + + // Other cases handled explicitly below + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) + switch (*it) { + case 'r': + ++it; + if (*it != '\'') { + throw fmt::format_error("invalid format for Field"); + } + const auto* rgn_start = ++it; + std::size_t size = 0; + while (*it != '\'') { + ++size; + ++it; + } + region = std::string_view(rgn_start, size); + ++it; + break; + } + + if (it != end && *it != '}') { + if (*it != ':') { + throw fmt::format_error("invalid format specifier"); + } + ++it; + } + + ctx.advance_to(it); + return underlying.parse(ctx); + } + auto format(const T& f, format_context& ctx) const -> format_context::iterator { const auto* mesh = f.getMesh(); - int previous_x = 0; - int previous_y = 0; - int previous_z = 0; - BOUT_FOR_SERIAL(i, f.getRegion("RGN_ALL")) { + const auto rgn = f.getRegion(std::string(region)); + const auto i = rgn.begin(); + int previous_x = i->x(); + int previous_y = i->y(); + int previous_z = i->z(); + + BOUT_FOR_SERIAL(i, rgn) { const auto ix = i.x(); const auto iy = i.y(); const auto iz = i.z(); @@ -82,7 +133,7 @@ struct fmt::formatter, char>> : f } format_to(ctx.out(), "{:c}: ", i); - formatter::format(f[i], ctx); + underlying.format(f[i], ctx); format_to(ctx.out(), ";"); previous_x = ix; previous_y = iy; diff --git a/tests/unit/sys/test_output_bout_types.cxx b/tests/unit/sys/test_output_bout_types.cxx index 82eb1754d0..dc7292f555 100644 --- a/tests/unit/sys/test_output_bout_types.cxx +++ b/tests/unit/sys/test_output_bout_types.cxx @@ -47,7 +47,7 @@ TEST_F(FormatFieldTest, Field2DSpec) { fillField(f, {{0., 1., 2., 3., 4.}, {5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14.}}); - const auto out = fmt::format("{:3.1e}", f); + const auto out = fmt::format("{::3.1e}", f); const std::string expected = R"((0, 0): 0.0e+00; @@ -107,7 +107,7 @@ TEST_F(FormatFieldTest, Field3DSpec) { {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); - const auto out = fmt::format("{:3.1e}", f); + const auto out = fmt::format("{::3.1e}", f); const std::string expected = R"((0, 0, 0): 0.0e+00; (0, 0, 1): 1.0e+00; @@ -130,6 +130,25 @@ TEST_F(FormatFieldTest, Field3DSpec) { EXPECT_EQ(out, expected); } + +TEST_F(FormatFieldTest, Field3DRegionSpec) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + const auto out = fmt::format("{:r'RGN_NOX':3.1e}", f); + + const std::string expected = + R"((1, 0, 0): 1.0e+01; (1, 0, 1): 1.1e+01; +(1, 1, 0): 1.2e+01; (1, 1, 1): 1.3e+01; +(1, 2, 0): 1.4e+01; (1, 2, 1): 1.5e+01; +(1, 3, 0): 1.6e+01; (1, 3, 1): 1.7e+01; +(1, 4, 0): 1.8e+01; (1, 4, 1): 1.9e+01;)"; + EXPECT_EQ(out, expected); +} + TEST_F(FormatFieldTest, FieldPerp) { Field3D f{bout::globals::mesh}; @@ -159,7 +178,7 @@ TEST_F(FormatFieldTest, FieldPerpSpec) { FieldPerp g = sliceXZ(f, 0); - const auto out = fmt::format("{:3.1e}", g); + const auto out = fmt::format("{::3.1e}", g); const std::string expected = R"((0, 0): 0.0e+00; (0, 1): 1.0e+00; From a9dac31e56551f8fa5e0fe38f3b3e5f5a4110beb Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 25 Feb 2026 13:46:47 +0000 Subject: [PATCH 413/461] Add field format spec to not print indices --- include/bout/output_bout_types.hxx | 51 +++++++++++++++-------- tests/unit/sys/test_output_bout_types.cxx | 30 +++++++++++++ 2 files changed, 64 insertions(+), 17 deletions(-) diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index a30830f934..ccdaf0dc08 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -61,6 +61,11 @@ struct fmt::formatter> { }; /// Formatter for Fields +/// +/// Format specification: +/// +/// - ``n``: Don't show indices +/// - ``r''``: Use given region (default: ``RGN_ALL``) template struct fmt::formatter, char>> { private: @@ -69,6 +74,8 @@ private: static constexpr auto default_region = "RGN_ALL"; std::string_view region = default_region; + bool show_indices = true; + public: constexpr auto parse(format_parse_context& ctx) { const auto* it = ctx.begin(); @@ -78,26 +85,34 @@ public: return underlying.parse(ctx); } - // Other cases handled explicitly below - // NOLINTNEXTLINE(bugprone-switch-missing-default-case) - switch (*it) { - case 'r': - ++it; - if (*it != '\'') { - throw fmt::format_error("invalid format for Field"); - } - const auto* rgn_start = ++it; - std::size_t size = 0; - while (*it != '\'') { - ++size; + while (it != end and *it != ':' and *it != '}') { + // Other cases handled explicitly below + // NOLINTNEXTLINE(bugprone-switch-missing-default-case) + switch (*it) { + case 'r': ++it; + if (*it != '\'') { + throw fmt::format_error("invalid format for Field"); + } + { + const auto* rgn_start = ++it; + std::size_t size = 0; + while (*it != '\'') { + ++size; + ++it; + } + region = std::string_view(rgn_start, size); + } + ++it; + break; + case 'n': + show_indices = false; + ++it; + break; } - region = std::string_view(rgn_start, size); - ++it; - break; } - if (it != end && *it != '}') { + if (it != end and *it != '}') { if (*it != ':') { throw fmt::format_error("invalid format specifier"); } @@ -132,7 +147,9 @@ public: format_to(ctx.out(), "\n\n"); } - format_to(ctx.out(), "{:c}: ", i); + if (show_indices) { + format_to(ctx.out(), "{:c}: ", i); + } underlying.format(f[i], ctx); format_to(ctx.out(), ";"); previous_x = ix; diff --git a/tests/unit/sys/test_output_bout_types.cxx b/tests/unit/sys/test_output_bout_types.cxx index dc7292f555..a9c447091c 100644 --- a/tests/unit/sys/test_output_bout_types.cxx +++ b/tests/unit/sys/test_output_bout_types.cxx @@ -149,6 +149,36 @@ TEST_F(FormatFieldTest, Field3DRegionSpec) { EXPECT_EQ(out, expected); } +TEST_F(FormatFieldTest, NoIndices) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + const auto out = fmt::format("{:n}", f); + + const std::string expected = + R"(0; 1; +2; 3; +4; 5; +6; 7; +8; 9; + +10; 11; +12; 13; +14; 15; +16; 17; +18; 19; + +20; 21; +22; 23; +24; 25; +26; 27; +28; 29;)"; + EXPECT_EQ(out, expected); +} + TEST_F(FormatFieldTest, FieldPerp) { Field3D f{bout::globals::mesh}; From eb9f06349cd2265a69f6cf358fef947616f1d64a Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 25 Feb 2026 16:06:29 +0000 Subject: [PATCH 414/461] Add field format spec to transpose field --- CMakeLists.txt | 1 + include/bout/output_bout_types.hxx | 24 ++++++- src/sys/output_bout_types.cxx | 39 +++++++++++ tests/unit/sys/test_output_bout_types.cxx | 82 +++++++++++++++++++++-- 4 files changed, 137 insertions(+), 9 deletions(-) create mode 100644 src/sys/output_bout_types.cxx diff --git a/CMakeLists.txt b/CMakeLists.txt index 502d635f1c..ed8044f27b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -374,6 +374,7 @@ set(BOUT_SOURCES ./src/sys/options/options_adios.hxx ./src/sys/optionsreader.cxx ./src/sys/output.cxx + ./src/sys/output_bout_types.cxx ./src/sys/petsclib.cxx ./src/sys/range.cxx ./src/sys/slepclib.cxx diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index ccdaf0dc08..fb24688d43 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -60,12 +60,23 @@ struct fmt::formatter> { } }; +namespace bout { +namespace details { +template +/// Transpose a region so that it iterates in Z first, then Y, then X +/// +/// Caution: this is the most inefficient memory order! +auto region_transpose(const Region& region) -> Region; +} +} // namespace bout + /// Formatter for Fields /// /// Format specification: /// /// - ``n``: Don't show indices /// - ``r''``: Use given region (default: ``RGN_ALL``) +/// - ``T``: Transpose field so X is first dimension template struct fmt::formatter, char>> { private: @@ -75,6 +86,7 @@ private: std::string_view region = default_region; bool show_indices = true; + bool transpose = false; public: constexpr auto parse(format_parse_context& ctx) { @@ -109,6 +121,10 @@ public: show_indices = false; ++it; break; + case 'T': + transpose = true; + ++it; + break; } } @@ -126,7 +142,9 @@ public: auto format(const T& f, format_context& ctx) const -> format_context::iterator { const auto* mesh = f.getMesh(); - const auto rgn = f.getRegion(std::string(region)); + const auto rgn_ = f.getRegion(std::string(region)); + const auto rgn = transpose ? bout::details::region_transpose(rgn_) : rgn_; + const auto i = rgn.begin(); int previous_x = i->x(); int previous_y = i->y(); @@ -138,13 +156,13 @@ public: const auto iz = i.z(); if (iz > previous_z) { - format_to(ctx.out(), " "); + format_to(ctx.out(), transpose ? "\n\n" : " "); } if (iy > previous_y) { format_to(ctx.out(), "\n"); } if (ix > previous_x) { - format_to(ctx.out(), "\n\n"); + format_to(ctx.out(), transpose ? " " : "\n\n"); } if (show_indices) { diff --git a/src/sys/output_bout_types.cxx b/src/sys/output_bout_types.cxx new file mode 100644 index 0000000000..ed76cd2b13 --- /dev/null +++ b/src/sys/output_bout_types.cxx @@ -0,0 +1,39 @@ +#include "bout/output_bout_types.hxx" + +#include "bout/mesh.hxx" +#include "bout/region.hxx" + +#include + +namespace bout::details { + +template +auto region_transpose(const Region& region) -> Region { + auto indices = region.getIndices(); + + std::sort(indices.begin(), indices.end(), [](const T& lhs, const T& rhs) { + const auto lx = lhs.x(); + const auto ly = lhs.y(); + const auto lz = lhs.z(); + + const auto rx = rhs.x(); + const auto ry = rhs.y(); + const auto rz = rhs.z(); + + // Z is now outer scale, so put it in largest blocks + if (lz != rz) { + return lz < rz; + } + if (ly != ry) { + return ly < ry; + } + return lx < rx; + }); + + return Region{indices}; +} + +template auto region_transpose(const Region& region) -> Region; +template auto region_transpose(const Region& region) -> Region; +template auto region_transpose(const Region& region) -> Region; +} // namespace bout::details diff --git a/tests/unit/sys/test_output_bout_types.cxx b/tests/unit/sys/test_output_bout_types.cxx index a9c447091c..8a9da618bf 100644 --- a/tests/unit/sys/test_output_bout_types.cxx +++ b/tests/unit/sys/test_output_bout_types.cxx @@ -8,12 +8,43 @@ #include "bout/globals.hxx" #include "bout/output_bout_types.hxx" // IWYU pragma: keep -#include - #include +#include +#include + using FormatFieldTest = FakeMeshFixture_tmpl<3, 5, 2>; +TEST_F(FormatFieldTest, DetailsRegionTranspose) { + const auto rgn_all = bout::globals::mesh->getRegion("RGN_ALL"); + const auto rgn_transpose = bout::details::region_transpose(rgn_all); + + std::vector> points{}; + points.reserve(rgn_all.size()); + + for (const auto i : rgn_transpose) { + points.push_back({i.x(), i.y(), i.z()}); + } + + const std::vector> expected = { + // clang-format off + {0, 0, 0}, {1, 0, 0}, {2, 0, 0}, + {0, 1, 0}, {1, 1, 0}, {2, 1, 0}, + {0, 2, 0}, {1, 2, 0}, {2, 2, 0}, + {0, 3, 0}, {1, 3, 0}, {2, 3, 0}, + {0, 4, 0}, {1, 4, 0}, {2, 4, 0}, + + {0, 0, 1}, {1, 0, 1}, {2, 0, 1}, + {0, 1, 1}, {1, 1, 1}, {2, 1, 1}, + {0, 2, 1}, {1, 2, 1}, {2, 2, 1}, + {0, 3, 1}, {1, 3, 1}, {2, 3, 1}, + {0, 4, 1}, {1, 4, 1}, {2, 4, 1}, + // clang-format on + }; + + ASSERT_EQ(points, expected); +} + TEST_F(FormatFieldTest, Field2D) { Field2D f{bout::globals::mesh}; @@ -22,7 +53,7 @@ TEST_F(FormatFieldTest, Field2D) { const auto out = fmt::format("{}", f); const std::string expected = - R"((0, 0): 0; + R"((0, 0): 0; (0, 1): 1; (0, 2): 2; (0, 3): 3; @@ -50,7 +81,7 @@ TEST_F(FormatFieldTest, Field2DSpec) { const auto out = fmt::format("{::3.1e}", f); const std::string expected = - R"((0, 0): 0.0e+00; + R"((0, 0): 0.0e+00; (0, 1): 1.0e+00; (0, 2): 2.0e+00; (0, 3): 3.0e+00; @@ -70,6 +101,22 @@ TEST_F(FormatFieldTest, Field2DSpec) { EXPECT_EQ(out, expected); } +TEST_F(FormatFieldTest, Field2DTranspose) { + Field2D f{bout::globals::mesh}; + + fillField(f, {{0., 1., 2., 3., 4.}, {5., 6., 7., 8., 9.}, {10., 11., 12., 13., 14.}}); + + const auto out = fmt::format("{:T}", f); + + const std::string expected = + R"((0, 0): 0; (1, 0): 5; (2, 0): 10; +(0, 1): 1; (1, 1): 6; (2, 1): 11; +(0, 2): 2; (1, 2): 7; (2, 2): 12; +(0, 3): 3; (1, 3): 8; (2, 3): 13; +(0, 4): 4; (1, 4): 9; (2, 4): 14;)"; + EXPECT_EQ(out, expected); +} + TEST_F(FormatFieldTest, Field3D) { Field3D f{bout::globals::mesh}; @@ -130,7 +177,6 @@ TEST_F(FormatFieldTest, Field3DSpec) { EXPECT_EQ(out, expected); } - TEST_F(FormatFieldTest, Field3DRegionSpec) { Field3D f{bout::globals::mesh}; @@ -149,7 +195,7 @@ TEST_F(FormatFieldTest, Field3DRegionSpec) { EXPECT_EQ(out, expected); } -TEST_F(FormatFieldTest, NoIndices) { +TEST_F(FormatFieldTest, Field3DNoIndices) { Field3D f{bout::globals::mesh}; fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, @@ -179,6 +225,30 @@ TEST_F(FormatFieldTest, NoIndices) { EXPECT_EQ(out, expected); } +TEST_F(FormatFieldTest, Field3DTranspose) { + Field3D f{bout::globals::mesh}; + + fillField(f, {{{0., 1}, {2., 3}, {4., 5}, {6., 7}, {8., 9}}, + {{10., 11}, {12., 13}, {14., 15}, {16., 17}, {18., 19}}, + {{20., 21}, {22., 23}, {24., 25}, {26., 27}, {28., 29}}}); + + const auto out = fmt::format("{:T}", f); + + const std::string expected = + R"((0, 0, 0): 0; (1, 0, 0): 10; (2, 0, 0): 20; +(0, 1, 0): 2; (1, 1, 0): 12; (2, 1, 0): 22; +(0, 2, 0): 4; (1, 2, 0): 14; (2, 2, 0): 24; +(0, 3, 0): 6; (1, 3, 0): 16; (2, 3, 0): 26; +(0, 4, 0): 8; (1, 4, 0): 18; (2, 4, 0): 28; + +(0, 0, 1): 1; (1, 0, 1): 11; (2, 0, 1): 21; +(0, 1, 1): 3; (1, 1, 1): 13; (2, 1, 1): 23; +(0, 2, 1): 5; (1, 2, 1): 15; (2, 2, 1): 25; +(0, 3, 1): 7; (1, 3, 1): 17; (2, 3, 1): 27; +(0, 4, 1): 9; (1, 4, 1): 19; (2, 4, 1): 29;)"; + EXPECT_EQ(out, expected); +} + TEST_F(FormatFieldTest, FieldPerp) { Field3D f{bout::globals::mesh}; From caccbc4f77ec5c8795380514ffe36ef5cc683fd4 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 25 Feb 2026 17:14:58 +0000 Subject: [PATCH 415/461] Add very basic plotting for fields --- include/bout/output_bout_types.hxx | 43 ++++- src/sys/output_bout_types.cxx | 287 +++++++++++++++++++++++++++++ 2 files changed, 323 insertions(+), 7 deletions(-) diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index fb24688d43..8f7233b613 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -12,6 +12,7 @@ #include "bout/traits.hxx" #include +#include #include #include @@ -67,7 +68,9 @@ template /// /// Caution: this is the most inefficient memory order! auto region_transpose(const Region& region) -> Region; -} + +auto colour(BoutReal value, BoutReal min, BoutReal max) -> fmt::text_style; +} // namespace details } // namespace bout /// Formatter for Fields @@ -77,6 +80,7 @@ auto region_transpose(const Region& region) -> Region; /// - ``n``: Don't show indices /// - ``r''``: Use given region (default: ``RGN_ALL``) /// - ``T``: Transpose field so X is first dimension +/// - ``#``: Plot slices as 2D heatmap template struct fmt::formatter, char>> { private: @@ -87,6 +91,7 @@ private: bool show_indices = true; bool transpose = false; + bool plot = false; public: constexpr auto parse(format_parse_context& ctx) { @@ -125,6 +130,11 @@ public: transpose = true; ++it; break; + case '#': + plot = true; + show_indices = false; + ++it; + break; } } @@ -140,36 +150,55 @@ public: } auto format(const T& f, format_context& ctx) const -> format_context::iterator { + using namespace bout::details; + const auto* mesh = f.getMesh(); - const auto rgn_ = f.getRegion(std::string(region)); - const auto rgn = transpose ? bout::details::region_transpose(rgn_) : rgn_; + const auto rgn_str = std::string{region}; + const auto rgn_ = f.getRegion(rgn_str); + const auto rgn = transpose ? region_transpose(rgn_) : rgn_; const auto i = rgn.begin(); int previous_x = i->x(); int previous_y = i->y(); int previous_z = i->z(); + // Range of the data for plotting + BoutReal plot_min = 0.0; + BoutReal plot_max = 0.0; + if (plot) { + plot_min = min(f, false, rgn_str); + plot_max = max(f, false, rgn_str); + } + + // Separators + const auto* const block_sep = "\n\n"; + const auto* const item_sep = plot ? "" : " "; + BOUT_FOR_SERIAL(i, rgn) { const auto ix = i.x(); const auto iy = i.y(); const auto iz = i.z(); if (iz > previous_z) { - format_to(ctx.out(), transpose ? "\n\n" : " "); + format_to(ctx.out(), transpose ? block_sep : item_sep); } if (iy > previous_y) { format_to(ctx.out(), "\n"); } if (ix > previous_x) { - format_to(ctx.out(), transpose ? " " : "\n\n"); + format_to(ctx.out(), transpose ? item_sep : block_sep); } if (show_indices) { format_to(ctx.out(), "{:c}: ", i); } - underlying.format(f[i], ctx); - format_to(ctx.out(), ";"); + if (plot) { + format_to(ctx.out(), "{}", styled("█", colour(f[i], plot_min, plot_max))); + } else { + underlying.format(f[i], ctx); + format_to(ctx.out(), ";"); + } previous_x = ix; previous_y = iy; previous_z = iz; diff --git a/src/sys/output_bout_types.cxx b/src/sys/output_bout_types.cxx index ed76cd2b13..41a2055dfc 100644 --- a/src/sys/output_bout_types.cxx +++ b/src/sys/output_bout_types.cxx @@ -1,9 +1,16 @@ #include "bout/output_bout_types.hxx" +#include "bout/bout_types.hxx" #include "bout/mesh.hxx" #include "bout/region.hxx" +#include + #include +#include +#include +#include +#include namespace bout::details { @@ -36,4 +43,284 @@ auto region_transpose(const Region& region) -> Region { template auto region_transpose(const Region& region) -> Region; template auto region_transpose(const Region& region) -> Region; template auto region_transpose(const Region& region) -> Region; + +// Matplotlib viridis colourmap +// Copyright Matplotlib Development Team +// SPDX-License-Identifier: BSD +constexpr std::array, 256> viridis_data = {{ + // clang-format off + {68, 1, 84}, + {68, 2, 85}, + {69, 3, 87}, + {69, 5, 88}, + {69, 6, 90}, + {70, 8, 91}, + {70, 9, 93}, + {70, 11, 94}, + {70, 12, 96}, + {71, 14, 97}, + {71, 15, 98}, + {71, 17, 100}, + {71, 18, 101}, + {71, 20, 102}, + {72, 21, 104}, + {72, 22, 105}, + {72, 24, 106}, + {72, 25, 108}, + {72, 26, 109}, + {72, 28, 110}, + {72, 29, 111}, + {72, 30, 112}, + {72, 32, 113}, + {72, 33, 115}, + {72, 34, 116}, + {72, 36, 117}, + {72, 37, 118}, + {72, 38, 119}, + {72, 39, 120}, + {71, 41, 121}, + {71, 42, 121}, + {71, 43, 122}, + {71, 44, 123}, + {71, 46, 124}, + {70, 47, 125}, + {70, 48, 126}, + {70, 49, 126}, + {70, 51, 127}, + {69, 52, 128}, + {69, 53, 129}, + {69, 54, 129}, + {68, 56, 130}, + {68, 57, 131}, + {68, 58, 131}, + {67, 59, 132}, + {67, 60, 132}, + {67, 62, 133}, + {66, 63, 133}, + {66, 64, 134}, + {65, 65, 134}, + {65, 66, 135}, + {65, 67, 135}, + {64, 69, 136}, + {64, 70, 136}, + {63, 71, 136}, + {63, 72, 137}, + {62, 73, 137}, + {62, 74, 137}, + {61, 75, 138}, + {61, 77, 138}, + {60, 78, 138}, + {60, 79, 138}, + {59, 80, 139}, + {59, 81, 139}, + {58, 82, 139}, + {58, 83, 139}, + {57, 84, 140}, + {57, 85, 140}, + {56, 86, 140}, + {56, 87, 140}, + {55, 88, 140}, + {55, 89, 140}, + {54, 91, 141}, + {54, 92, 141}, + {53, 93, 141}, + {53, 94, 141}, + {52, 95, 141}, + {52, 96, 141}, + {51, 97, 141}, + {51, 98, 141}, + {51, 99, 141}, + {50, 100, 142}, + {50, 101, 142}, + {49, 102, 142}, + {49, 103, 142}, + {48, 104, 142}, + {48, 105, 142}, + {47, 106, 142}, + {47, 107, 142}, + {47, 108, 142}, + {46, 109, 142}, + {46, 110, 142}, + {45, 111, 142}, + {45, 112, 142}, + {45, 112, 142}, + {44, 113, 142}, + {44, 114, 142}, + {43, 115, 142}, + {43, 116, 142}, + {43, 117, 142}, + {42, 118, 142}, + {42, 119, 142}, + {41, 120, 142}, + {41, 121, 142}, + {41, 122, 142}, + {40, 123, 142}, + {40, 124, 142}, + {40, 125, 142}, + {39, 126, 142}, + {39, 127, 142}, + {38, 128, 142}, + {38, 129, 142}, + {38, 130, 142}, + {37, 131, 142}, + {37, 131, 142}, + {37, 132, 142}, + {36, 133, 142}, + {36, 134, 142}, + {35, 135, 142}, + {35, 136, 142}, + {35, 137, 142}, + {34, 138, 141}, + {34, 139, 141}, + {34, 140, 141}, + {33, 141, 141}, + {33, 142, 141}, + {33, 143, 141}, + {32, 144, 141}, + {32, 145, 140}, + {32, 146, 140}, + {32, 147, 140}, + {31, 147, 140}, + {31, 148, 140}, + {31, 149, 139}, + {31, 150, 139}, + {31, 151, 139}, + {30, 152, 139}, + {30, 153, 138}, + {30, 154, 138}, + {30, 155, 138}, + {30, 156, 137}, + {30, 157, 137}, + {30, 158, 137}, + {30, 159, 136}, + {30, 160, 136}, + {31, 161, 136}, + {31, 162, 135}, + {31, 163, 135}, + {31, 163, 134}, + {32, 164, 134}, + {32, 165, 134}, + {33, 166, 133}, + {33, 167, 133}, + {34, 168, 132}, + {35, 169, 131}, + {35, 170, 131}, + {36, 171, 130}, + {37, 172, 130}, + {38, 173, 129}, + {39, 174, 129}, + {40, 175, 128}, + {41, 175, 127}, + {42, 176, 127}, + {43, 177, 126}, + {44, 178, 125}, + {46, 179, 124}, + {47, 180, 124}, + {48, 181, 123}, + {50, 182, 122}, + {51, 183, 121}, + {53, 183, 121}, + {54, 184, 120}, + {56, 185, 119}, + {57, 186, 118}, + {59, 187, 117}, + {61, 188, 116}, + {62, 189, 115}, + {64, 190, 114}, + {66, 190, 113}, + {68, 191, 112}, + {70, 192, 111}, + {72, 193, 110}, + {73, 194, 109}, + {75, 194, 108}, + {77, 195, 107}, + {79, 196, 106}, + {81, 197, 105}, + {83, 198, 104}, + {85, 198, 102}, + {88, 199, 101}, + {90, 200, 100}, + {92, 201, 99}, + {94, 201, 98}, + {96, 202, 96}, + {98, 203, 95}, + {101, 204, 94}, + {103, 204, 92}, + {105, 205, 91}, + {108, 206, 90}, + {110, 206, 88}, + {112, 207, 87}, + {115, 208, 85}, + {117, 208, 84}, + {119, 209, 82}, + {122, 210, 81}, + {124, 210, 79}, + {127, 211, 78}, + {129, 212, 76}, + {132, 212, 75}, + {134, 213, 73}, + {137, 213, 72}, + {139, 214, 70}, + {142, 215, 68}, + {144, 215, 67}, + {147, 216, 65}, + {149, 216, 63}, + {152, 217, 62}, + {155, 217, 60}, + {157, 218, 58}, + {160, 218, 57}, + {163, 219, 55}, + {165, 219, 53}, + {168, 220, 51}, + {171, 220, 50}, + {173, 221, 48}, + {176, 221, 46}, + {179, 221, 45}, + {181, 222, 43}, + {184, 222, 41}, + {187, 223, 39}, + {189, 223, 38}, + {192, 223, 36}, + {195, 224, 35}, + {197, 224, 33}, + {200, 225, 32}, + {203, 225, 30}, + {205, 225, 29}, + {208, 226, 28}, + {211, 226, 27}, + {213, 226, 26}, + {216, 227, 25}, + {219, 227, 24}, + {221, 227, 24}, + {224, 228, 24}, + {226, 228, 24}, + {229, 228, 24}, + {232, 229, 25}, + {234, 229, 25}, + {237, 229, 26}, + {239, 230, 27}, + {242, 230, 28}, + {244, 230, 30}, + {247, 230, 31}, + {249, 231, 33}, + {251, 231, 35}, + {254, 231, 36}, +}}; +// clang-format on + +constexpr auto colour_map_scaling = viridis_data.size() - 1; + +auto colour(BoutReal value, BoutReal min, BoutReal max) -> fmt::text_style { + if (std::isnan(value)) { + return fmt::fg(fmt::color::black); + } + + // Get value in range [0, 1] + const auto x = (value - min) / (max - min); + // Convert to range [0, 255] + const auto index = static_cast(x * colour_map_scaling); + const auto colour = viridis_data[index]; // NOLINT + + return fmt::fg(fmt::rgb(colour[0], colour[1], colour[2])); +} } // namespace bout::details From 55998654e4476840104471d819f4c70351a352ff Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 25 Feb 2026 18:13:46 +0000 Subject: [PATCH 416/461] Add field format truncation, like numpy --- include/bout/output_bout_types.hxx | 85 +++++++++++++++++++++-- tests/unit/sys/test_output_bout_types.cxx | 30 ++++++++ 2 files changed, 109 insertions(+), 6 deletions(-) diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index 8f7233b613..bd53fc7ed1 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -15,6 +15,7 @@ #include #include +#include #include #include #include @@ -70,6 +71,37 @@ template auto region_transpose(const Region& region) -> Region; auto colour(BoutReal value, BoutReal min, BoutReal max) -> fmt::text_style; + +// Parses the range [begin, end) as an unsigned integer. This function assumes +// that the range is non-empty and the first character is a digit. +// +// Taken from fmt +// Copyright (c) 2012 - present, Victor Zverovich +// SPDX-License-Identifier: MIT +constexpr auto parse_nonnegative_int(const char*& begin, const char* end, + int error_value) noexcept -> int { + const auto* p = begin; + unsigned value = *p - '0'; + unsigned prev = 0; + ++p; + + while (p != end && '0' <= *p && *p <= '9') { + prev = value; + value = (value * 10) + unsigned(*p - '0'); + ++p; + } + auto num_digits = p - begin; + begin = p; + const auto digits10 = static_cast(sizeof(int) * CHAR_BIT * 3 / 10); + if (num_digits <= digits10) { + return static_cast(value); + } + // Check for overflow. + const unsigned max = INT_MAX; + return num_digits == digits10 + 1 and (prev * 10ULL) + unsigned(p[-1] - '0') <= max + ? static_cast(value) + : error_value; +} } // namespace details } // namespace bout @@ -81,6 +113,8 @@ auto colour(BoutReal value, BoutReal min, BoutReal max) -> fmt::text_style; /// - ``r''``: Use given region (default: ``RGN_ALL``) /// - ``T``: Transpose field so X is first dimension /// - ``#``: Plot slices as 2D heatmap +/// - ``e``: Number of elements at each edge to show +/// - ``f``: Show full field template struct fmt::formatter, char>> { private: @@ -92,6 +126,8 @@ private: bool show_indices = true; bool transpose = false; bool plot = false; + int edgeitems = 4; + bool show_full = false; public: constexpr auto parse(format_parse_context& ctx) { @@ -106,6 +142,17 @@ public: // Other cases handled explicitly below // NOLINTNEXTLINE(bugprone-switch-missing-default-case) switch (*it) { + case 'e': + ++it; + edgeitems = bout::details::parse_nonnegative_int(it, end, -1); + if (edgeitems == -1) { + throw fmt::format_error("number is too big"); + } + break; + case 'f': + show_full = true; + ++it; + break; case 'r': ++it; if (*it != '\'') { @@ -163,6 +210,19 @@ public: int previous_y = i->y(); int previous_z = i->z(); + const auto last_i = rgn.getIndices().rbegin(); + const int last_x = last_i->x(); + const int last_y = last_i->y(); + const int last_z = last_i->z(); + + // Indices of edges + const int start_x = previous_x + edgeitems; + const int start_y = previous_y + edgeitems; + const int start_z = previous_z + edgeitems; + const int end_x = last_x - edgeitems; + const int end_y = last_y - edgeitems; + const int end_z = last_z - edgeitems; + // Range of the data for plotting BoutReal plot_min = 0.0; BoutReal plot_max = 0.0; @@ -172,32 +232,45 @@ public: } // Separators - const auto* const block_sep = "\n\n"; + constexpr auto block_sep = "\n\n"; const auto* const item_sep = plot ? "" : " "; + // If we've shown the skip sep already this dim + bool shown_skip = false; + constexpr auto skip_sep = "..."; + BOUT_FOR_SERIAL(i, rgn) { const auto ix = i.x(); const auto iy = i.y(); const auto iz = i.z(); - if (iz > previous_z) { + const bool should_show = + show_full + or ((ix < start_x or ix > end_x) and (iy < start_y or iy > end_y) + and (iz < start_z or iz > end_z)); + + if ((not shown_skip or should_show) and iz > previous_z) { format_to(ctx.out(), transpose ? block_sep : item_sep); } - if (iy > previous_y) { + if ((not shown_skip or should_show) and iy > previous_y) { format_to(ctx.out(), "\n"); } - if (ix > previous_x) { + if ((not shown_skip or should_show) and ix > previous_x) { format_to(ctx.out(), transpose ? item_sep : block_sep); } - if (show_indices) { + if (show_indices and should_show) { format_to(ctx.out(), "{:c}: ", i); } if (plot) { format_to(ctx.out(), "{}", styled("█", colour(f[i], plot_min, plot_max))); - } else { + } else if (should_show) { underlying.format(f[i], ctx); format_to(ctx.out(), ";"); + shown_skip = false; + } else if (not shown_skip) { + format_to(ctx.out(), skip_sep); + shown_skip = true; } previous_x = ix; previous_y = iy; diff --git a/tests/unit/sys/test_output_bout_types.cxx b/tests/unit/sys/test_output_bout_types.cxx index 8a9da618bf..1c4d6bdfe0 100644 --- a/tests/unit/sys/test_output_bout_types.cxx +++ b/tests/unit/sys/test_output_bout_types.cxx @@ -1,5 +1,6 @@ #include "fake_mesh_fixture.hxx" #include "test_extras.hxx" +#include "gmock/gmock.h" #include "gtest/gtest.h" #include "bout/field2d.hxx" @@ -288,3 +289,32 @@ TEST_F(FormatFieldTest, FieldPerpSpec) { (2, 0): 2.0e+01; (2, 1): 2.1e+01;)"; EXPECT_EQ(out, expected); } + +using FormatFieldTestLargerMesh = FakeMeshFixture_tmpl<10, 10, 10>; + +TEST_F(FormatFieldTestLargerMesh, Field3DEdges) { + Field3D f{1., bout::globals::mesh}; + + const auto out = fmt::format("{:e1}", f); + + const std::string expected = + R"((0, 0, 0): 1; ... (0, 0, 9): 1; +... +(0, 9, 0): 1; ... (0, 9, 9): 1; + +... + +(9, 0, 0): 1; ... (9, 0, 9): 1; +... +(9, 9, 0): 1; ... (9, 9, 9): 1;)"; + EXPECT_EQ(out, expected); +} + +TEST_F(FormatFieldTestLargerMesh, Field3DFull) { + Field3D f{1., bout::globals::mesh}; + + const auto out = fmt::format("{:f}", f); + + using namespace ::testing; + EXPECT_THAT(out, Not(HasSubstr("..."))); +} From 78c6fdfa878333ee9d7a1de57f132e19bd4f4bdc Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Wed, 11 Mar 2026 17:40:39 +0000 Subject: [PATCH 417/461] Fix `Field` formatting for C++20 Argument to `format_to` must be either compiletime constant or `fmt::runtime` --- include/bout/output_bout_types.hxx | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/include/bout/output_bout_types.hxx b/include/bout/output_bout_types.hxx index bd53fc7ed1..77ea8ebf2d 100644 --- a/include/bout/output_bout_types.hxx +++ b/include/bout/output_bout_types.hxx @@ -232,8 +232,10 @@ public: } // Separators - constexpr auto block_sep = "\n\n"; - const auto* const item_sep = plot ? "" : " "; + const auto block_sep = fmt::runtime("\n\n"); + const auto item_sep = fmt::runtime(plot ? "" : " "); + const auto x_sep = transpose ? item_sep : block_sep; + const auto z_sep = transpose ? block_sep : item_sep; // If we've shown the skip sep already this dim bool shown_skip = false; @@ -250,13 +252,13 @@ public: and (iz < start_z or iz > end_z)); if ((not shown_skip or should_show) and iz > previous_z) { - format_to(ctx.out(), transpose ? block_sep : item_sep); + format_to(ctx.out(), z_sep); } if ((not shown_skip or should_show) and iy > previous_y) { format_to(ctx.out(), "\n"); } if ((not shown_skip or should_show) and ix > previous_x) { - format_to(ctx.out(), transpose ? item_sep : block_sep); + format_to(ctx.out(), x_sep); } if (show_indices and should_show) { From e952090de92bef5cdb7c14b0aa2119efeaf99f6a Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 11 Mar 2026 11:06:15 -0700 Subject: [PATCH 418/461] Options: Convert Tensor to Tensor and similar Read Tensor from NetCDF files, and allow conversion to Tensor and then Field3D. --- include/bout/array.hxx | 8 ++++++++ include/bout/utils.hxx | 18 +++++++++++++++++- src/sys/options.cxx | 29 ++++++++++++++++++++++++----- src/sys/options/options_netcdf.cxx | 6 ++++++ 4 files changed, 55 insertions(+), 6 deletions(-) diff --git a/include/bout/array.hxx b/include/bout/array.hxx index 2305fe3b22..000ded9616 100644 --- a/include/bout/array.hxx +++ b/include/bout/array.hxx @@ -227,6 +227,14 @@ public: ptr = get(new_size); } + /*! + * Change shape of the container. + * Invalidates contents. + */ + void reshape(std::tuple new_shape) { + reallocate(std::get<0>(new_shape)); + } + /*! * Holds a static variable which controls whether * memory blocks (dataBlock) are put into a store diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index 560f97f954..5088a025c1 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -5,7 +5,7 @@ * simple but common calculations * ************************************************************************** - * Copyright 2010 - 2025 BOUT++ contributors + * Copyright 2010 - 2026 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -231,6 +231,14 @@ public: data.reallocate(new_size_1 * new_size_2); } + /*! + * Change shape of the container. + * Invalidates contents. + */ + void reshape(std::tuple new_shape) { + reallocate(std::get<0>(new_shape), std::get<1>(new_shape)); + } + Matrix& operator=(const Matrix& other) { n1 = other.n1; n2 = other.n2; @@ -331,6 +339,14 @@ public: data.reallocate(new_size_1 * new_size_2 * new_size_3); } + /*! + * Change shape of the container. + * Invalidates contents. + */ + void reshape(std::tuple new_shape) { + reallocate(std::get<0>(new_shape), std::get<1>(new_shape), std::get<2>(new_shape)); + } + Tensor& operator=(const Tensor& other) { n1 = other.n1; n2 = other.n2; diff --git a/src/sys/options.cxx b/src/sys/options.cxx index d60e91079d..8bd83c0bfb 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -744,27 +744,46 @@ FieldPerp Options::as(const FieldPerp& similar_to) const { } namespace { -/// Visitor to convert an int, BoutReal or Array/Matrix/Tensor to the -/// appropriate container +/// Primary declaration of ConvertContainer, for specialization below. +/// No definition needed unless it is used. template -struct ConvertContainer { +struct ConvertContainer; + +/// Visitor to convert an int, BoutReal or Array/Matrix/Tensor to the +/// appropriate container. Templated on both the container class C +/// and scalar type Scalar. +template