From 00dbb6e2d47c0fa8eccddb7adbad8c40aca075e6 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 13 Oct 2026 13:05:50 +0200 Subject: [PATCH 01/13] Ensure only Original is used for FCI --- src/mesh/difops.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index aa8b7cdee6..3c4bd79b7e 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -378,6 +378,10 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, // -> Only B, dy and g_22 can be used in yup/ydown // Others (e.g J) may not be averaged between y planes. + if constexpr (conduction_method != bout::ConductionMethod::Original) { + throw BoutException("Only bout::ConductionMethod::Original is supported for FCI"); + } + const auto& K_up = Kin.yup(); const auto& K_down = Kin.ydown(); From 4d8abcb385e0651ba9064b05a36bf22e7730dca3 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 13 Oct 2026 13:58:15 +0200 Subject: [PATCH 02/13] Add PetscOperator as option to Div_par_K_Grad_par_mod --- include/bout/difops.hxx | 5 ++++- src/mesh/difops.cxx | 36 +++++++++++++++++++++++++++++++++--- 2 files changed, 37 insertions(+), 4 deletions(-) diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index 0b3320c1f1..17f04ec435 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -217,7 +217,10 @@ Field3D Div_par_K_Grad_par(const Field3DParallel& kY, const Field3DParallel& f, /// :math:`K J / (g_{22} dy)`. This better matches a series-resistance /// interpretation of two adjacent half-cells and can give noticeably /// different results when coefficients or cell sizes vary strongly. -BOUT_ENUM_CLASS_NS(bout, ConductionMethod, Original, ProductJK, Harmonic); +/// +/// PetscOperator: +/// Users the PETSc support operator method. For FCI only. +BOUT_ENUM_CLASS_NS(bout, ConductionMethod, Original, ProductJK, Harmonic, PetscOperator); /// Version with energy flow diagnostic /// For FCI fields, `flow_ylow` is currently returned as zero. diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 3c4bd79b7e..a8f549aaf1 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -40,6 +40,7 @@ #include #include // Delp2 uses same coefficients as inversion code #include +#include #include #include #include @@ -361,6 +362,22 @@ Field3D Div_par_K_Grad_par(const Field3DParallel& kY, const Field3DParallel& f, } namespace { +#if BOUT_USE_PETSC +std::map> petsc_par_op_cache; + +std::shared_ptr getParallelOp(Mesh* mesh) { + //petsc_par_op_cache + if (auto search = petsc_par_op_cache.find(mesh); search != petsc_par_op_cache.end()) { + if (std::shared_ptr spt = search->second.lock()) { + return spt; + } + } + auto par = std::make_shared(*mesh); + petsc_par_op_cache[mesh] = par; + return par; +} +#endif + template Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, const Field3DParallel& fin, Field3D& flow_ylow, @@ -378,6 +395,15 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, // -> Only B, dy and g_22 can be used in yup/ydown // Others (e.g J) may not be averaged between y planes. + if constexpr (conduction_method == bout::ConductionMethod::PetscOperator) { +#if BOUT_USE_PETSC + auto par = getParallelOp(Kin.getMesh()); + return par.Div_par_K_Grad_par(Kin, fin); +#else + throw BoutException("BOUT++ was compiled without PETSc."); +#endif + } + if constexpr (conduction_method != bout::ConductionMethod::Original) { throw BoutException("Only bout::ConductionMethod::Original is supported for FCI"); } @@ -477,6 +503,8 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, : 0.0; flux = C_edge * (f[iyp] - f[i]); + } else { + throw BoutException("Unsupported option {}", toString(conduction_method)); } result[i] += flux / (coord->dy()[i] * coord->J()[i]); } @@ -549,10 +577,12 @@ Field3D Div_par_K_Grad_par_mod(const Field3DParallel& Kin, const Field3DParallel return Div_par_K_Grad_par_mod_impl(Kin, fin, flow_ylow, bndry_flux); case Harmonic: return Div_par_K_Grad_par_mod_impl(Kin, fin, flow_ylow, bndry_flux); + case PetscOperator: + return Div_par_K_Grad_par_mod_impl(Kin, fin, flow_ylow, bndry_flux); } - throw BoutException( - "Unknown method `{}` - choose from `Original`, `ProductJK` or `Harmonic`.", - toString(method)); + throw BoutException("Unknown method `{}` - choose from `Original`, `ProductJK`, " + "`Harmonic` or `PetscOperator`.", + toString(method)); } /******************************************************************************* * Delp2 From 18e5f1e95c9643220aac0129df5705456db397ed Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 13 Oct 2026 14:03:17 +0200 Subject: [PATCH 03/13] Ensure flow_ylow is always allocated --- src/mesh/difops.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index a8f549aaf1..9088803ab1 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -395,6 +395,8 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, // -> Only B, dy and g_22 can be used in yup/ydown // Others (e.g J) may not be averaged between y planes. + flow_ylow = zeroFrom(fin); + if constexpr (conduction_method == bout::ConductionMethod::PetscOperator) { #if BOUT_USE_PETSC auto par = getParallelOp(Kin.getMesh()); @@ -415,7 +417,6 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, const auto& f_down = fin.ydown(); Field3D result{zeroFrom(fin)}; - flow_ylow = zeroFrom(fin); const auto yboundary = coord->getYBoundary(); From 013cf67eb4fa1aeb87bf36cc3301fcd22b10e891 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 13 Oct 2026 14:17:52 +0200 Subject: [PATCH 04/13] Fix typo --- src/mesh/difops.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 9088803ab1..17464e4ebc 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -362,7 +362,7 @@ Field3D Div_par_K_Grad_par(const Field3DParallel& kY, const Field3DParallel& f, } namespace { -#if BOUT_USE_PETSC +#if BOUT_HAS_PETSC std::map> petsc_par_op_cache; std::shared_ptr getParallelOp(Mesh* mesh) { @@ -398,7 +398,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, flow_ylow = zeroFrom(fin); if constexpr (conduction_method == bout::ConductionMethod::PetscOperator) { -#if BOUT_USE_PETSC +#if BOUT_HAS_PETSC auto par = getParallelOp(Kin.getMesh()); return par.Div_par_K_Grad_par(Kin, fin); #else From e7f5713c34b999ee86300efab44495d0a9e21255 Mon Sep 17 00:00:00 2001 From: David Bold Date: Tue, 13 Oct 2026 14:25:16 +0200 Subject: [PATCH 05/13] Resolve compile issues --- src/mesh/difops.cxx | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 17464e4ebc..f4b624f3ab 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -372,7 +372,8 @@ std::shared_ptr getParallelOp(Mesh* mesh) { return spt; } } - auto par = std::make_shared(*mesh); + auto op = PetscOperators(mesh); + auto par = std::make_shared(op.getParallel()); petsc_par_op_cache[mesh] = par; return par; } @@ -400,7 +401,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, if constexpr (conduction_method == bout::ConductionMethod::PetscOperator) { #if BOUT_HAS_PETSC auto par = getParallelOp(Kin.getMesh()); - return par.Div_par_K_Grad_par(Kin, fin); + return par->Div_par_K_Grad_par(Kin, fin); #else throw BoutException("BOUT++ was compiled without PETSc."); #endif From 4e19c35c3899502b78b40b21e70c7aafaca0a724 Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 14 Oct 2026 09:30:46 +0200 Subject: [PATCH 06/13] Use static member functions for caching --- include/bout/petsc_operators.hxx | 10 ++++++++++ src/mesh/difops.cxx | 19 +------------------ src/mesh/petsc_operators.cxx | 25 +++++++++++++++++++++++++ 3 files changed, 36 insertions(+), 18 deletions(-) diff --git a/include/bout/petsc_operators.hxx b/include/bout/petsc_operators.hxx index 5a7df5b088..09ef4090b5 100644 --- a/include/bout/petsc_operators.hxx +++ b/include/bout/petsc_operators.hxx @@ -975,6 +975,16 @@ public: /// @returns A @ref Parallel struct containing all assembled operators. Parallel getParallel() const; + class Cache { + public: + static std::shared_ptr get(Mesh* mesh); + static std::shared_ptr getParallel(Mesh* mesh); + + private: + static std::map> petscOp; + static std::map> parOp; + }; + private: /// @brief Read a Field3D from the mesh, throwing if the field is absent. /// diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index f4b624f3ab..910248ea50 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -362,23 +362,6 @@ Field3D Div_par_K_Grad_par(const Field3DParallel& kY, const Field3DParallel& f, } namespace { -#if BOUT_HAS_PETSC -std::map> petsc_par_op_cache; - -std::shared_ptr getParallelOp(Mesh* mesh) { - //petsc_par_op_cache - if (auto search = petsc_par_op_cache.find(mesh); search != petsc_par_op_cache.end()) { - if (std::shared_ptr spt = search->second.lock()) { - return spt; - } - } - auto op = PetscOperators(mesh); - auto par = std::make_shared(op.getParallel()); - petsc_par_op_cache[mesh] = par; - return par; -} -#endif - template Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, const Field3DParallel& fin, Field3D& flow_ylow, @@ -400,7 +383,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, if constexpr (conduction_method == bout::ConductionMethod::PetscOperator) { #if BOUT_HAS_PETSC - auto par = getParallelOp(Kin.getMesh()); + auto par = PetscOperators::Cache::getParallel(Kin.getMesh()); return par->Div_par_K_Grad_par(Kin, fin); #else throw BoutException("BOUT++ was compiled without PETSc."); diff --git a/src/mesh/petsc_operators.cxx b/src/mesh/petsc_operators.cxx index b05f013023..9d4e01f6d9 100644 --- a/src/mesh/petsc_operators.cxx +++ b/src/mesh/petsc_operators.cxx @@ -559,4 +559,29 @@ PetscCellOperator makeNeumannOperator(const PetscCellMappingPtr& mapping, return PetscCellOperator(mapping, mapping, std::move(out)); } +std::shared_ptr PetscOperators::Cache::get(Mesh* mesh) { + if (auto search = petscOp.find(mesh); search != petscOp.end()) { + if (std::shared_ptr spt = search->second.lock()) { + return spt; + } + } + auto op = std::make_shared(mesh); + petscOp[mesh] = op; + return op; +} + +std::shared_ptr PetscOperators::Cache::getParallel(Mesh* mesh) { + if (auto search = parOp.find(mesh); search != parOp.end()) { + if (std::shared_ptr spt = search->second.lock()) { + return spt; + } + } + auto op = get(mesh); + auto par = std::make_shared(op->getParallel()); + parOp[mesh] = par; + return par; +} + +std::map> PetscOperators::Cache::parOp; +std::map> PetscOperators::Cache::petscOp; #endif From e4c6b61a091a2637032865160655d3f7cee33b3f Mon Sep 17 00:00:00 2001 From: David Bold Date: Wed, 14 Oct 2026 12:43:09 +0200 Subject: [PATCH 07/13] Add missing headers --- include/bout/petsc_operators.hxx | 1 + src/mesh/petsc_operators.cxx | 1 + 2 files changed, 2 insertions(+) diff --git a/include/bout/petsc_operators.hxx b/include/bout/petsc_operators.hxx index 09ef4090b5..c397fe0302 100644 --- a/include/bout/petsc_operators.hxx +++ b/include/bout/petsc_operators.hxx @@ -1,5 +1,6 @@ #pragma once +#include #ifndef BOUT_PETSC_OPERATORS #define BOUT_PETSC_OPERATORS diff --git a/src/mesh/petsc_operators.cxx b/src/mesh/petsc_operators.cxx index 9d4e01f6d9..66d16ad465 100644 --- a/src/mesh/petsc_operators.cxx +++ b/src/mesh/petsc_operators.cxx @@ -1,4 +1,5 @@ #include "bout/build_defines.hxx" +#include #if BOUT_HAS_PETSC From facf4f9bf40052f165be89aad1033c17a31446da Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 16 Oct 2026 09:33:44 +0200 Subject: [PATCH 08/13] Apply batched suggestions from code review Co-authored-by: Peter Hill --- include/bout/difops.hxx | 2 +- src/mesh/difops.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index 17f04ec435..039b57a5ea 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -219,7 +219,7 @@ Field3D Div_par_K_Grad_par(const Field3DParallel& kY, const Field3DParallel& f, /// different results when coefficients or cell sizes vary strongly. /// /// PetscOperator: -/// Users the PETSc support operator method. For FCI only. +/// Uses the PETSc support operator method. For FCI only. BOUT_ENUM_CLASS_NS(bout, ConductionMethod, Original, ProductJK, Harmonic, PetscOperator); /// Version with energy flow diagnostic diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 910248ea50..28ff28db43 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -386,7 +386,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, auto par = PetscOperators::Cache::getParallel(Kin.getMesh()); return par->Div_par_K_Grad_par(Kin, fin); #else - throw BoutException("BOUT++ was compiled without PETSc."); + throw BoutException("Cannot use `bout::ConductionMethod::PetscOperator`: BOUT++ was compiled without PETSc."); #endif } @@ -489,7 +489,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, flux = C_edge * (f[iyp] - f[i]); } else { - throw BoutException("Unsupported option {}", toString(conduction_method)); + throw BoutException("Unsupported option {}", conduction_method); } result[i] += flux / (coord->dy()[i] * coord->J()[i]); } From 0681732c1c14045d176f34c2d8835fc34eb0c6d2 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 16 Oct 2026 10:06:54 +0200 Subject: [PATCH 09/13] Apply comments from code review. Co-authored-by: Peter Hill --- include/bout/petsc_operators.hxx | 2 +- src/mesh/difops.cxx | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/include/bout/petsc_operators.hxx b/include/bout/petsc_operators.hxx index c397fe0302..8373cfeefe 100644 --- a/include/bout/petsc_operators.hxx +++ b/include/bout/petsc_operators.hxx @@ -1,6 +1,5 @@ #pragma once -#include #ifndef BOUT_PETSC_OPERATORS #define BOUT_PETSC_OPERATORS @@ -24,6 +23,7 @@ #include #include +#include #include #include #include diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 28ff28db43..71c652d7e5 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -386,12 +386,15 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, auto par = PetscOperators::Cache::getParallel(Kin.getMesh()); return par->Div_par_K_Grad_par(Kin, fin); #else - throw BoutException("Cannot use `bout::ConductionMethod::PetscOperator`: BOUT++ was compiled without PETSc."); + throw BoutException("Cannot use `bout::ConductionMethod::PetscOperator`: BOUT++ " + "was compiled without PETSc."); #endif } if constexpr (conduction_method != bout::ConductionMethod::Original) { - throw BoutException("Only bout::ConductionMethod::Original is supported for FCI"); + throw BoutException( + "Only bout::ConductionMethod::Original and " + "\nbout::ConductionMethod::PetscOperator (with PETSc) are supported for FCI"); } const auto& K_up = Kin.yup(); From f9468add708ca16925bb42e0e00d804072474ad9 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 16 Oct 2026 10:08:10 +0200 Subject: [PATCH 10/13] Fall back to toString() --- src/mesh/difops.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 71c652d7e5..10c7b8c7f5 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -394,7 +394,9 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, if constexpr (conduction_method != bout::ConductionMethod::Original) { throw BoutException( "Only bout::ConductionMethod::Original and " - "\nbout::ConductionMethod::PetscOperator (with PETSc) are supported for FCI"); + "\nbout::ConductionMethod::PetscOperator (with PETSc) are supported for FCI" + "\nbut found {}.", + toString(conduction_method)); } const auto& K_up = Kin.yup(); @@ -492,7 +494,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, flux = C_edge * (f[iyp] - f[i]); } else { - throw BoutException("Unsupported option {}", conduction_method); + throw BoutException("Unsupported option {}", toString(conduction_method)); } result[i] += flux / (coord->dy()[i] * coord->J()[i]); } From 55121cb81bfd79b1806bd07b784ad8e421919943 Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 16 Oct 2026 10:21:15 +0200 Subject: [PATCH 11/13] Run PetscOperator test instead for FCI --- tests/MMS/spatial/fci/data/BOUT.inp | 3 +-- tests/MMS/spatial/fci/fci_mms.cxx | 7 ++----- tests/MMS/spatial/fci/runtest | 3 +-- 3 files changed, 4 insertions(+), 9 deletions(-) diff --git a/tests/MMS/spatial/fci/data/BOUT.inp b/tests/MMS/spatial/fci/data/BOUT.inp index 75204de221..94cd50aa46 100644 --- a/tests/MMS/spatial/fci/data/BOUT.inp +++ b/tests/MMS/spatial/fci/data/BOUT.inp @@ -9,8 +9,7 @@ FV_div_par_mod_solution = (0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(( FV_div_par_fvv_solution = (0.01*x + 0.045)*(6.28318530717959*(0.01*x + 0.045)*(2*(sin(y - 2*z) + sin(y - z))*sin(y - z)*cos(y - z)/(0.01*x + 0.045) + (-2*cos(y - 2*z) - cos(y - z))*cos(y - z)^2/(0.01*x + 0.045)) - 1.25663706143592*(sin(y - 2*z) + sin(y - z))*sin(y - z)*cos(y - z)/(0.01*x + 0.045) + 0.628318530717959*(cos(y - 2*z) + cos(y - z))*cos(y - z)^2/(0.01*x + 0.045))/sqrt((0.01*x + 0.045)^2 + 1.0) div_par_K_grad_par_mod_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) div_par_K_grad_par_mod_Original_solution = div_par_K_grad_par_mod_solution -div_par_K_grad_par_mod_ProductJK_solution = div_par_K_grad_par_mod_solution -div_par_K_grad_par_mod_Harmonic_solution = div_par_K_grad_par_mod_solution +div_par_K_grad_par_mod_PetscOperator_solution = div_par_K_grad_par_mod_solution [mesh] symmetricglobalx = true diff --git a/tests/MMS/spatial/fci/fci_mms.cxx b/tests/MMS/spatial/fci/fci_mms.cxx index a1bc20e0a5..9f06b79114 100644 --- a/tests/MMS/spatial/fci/fci_mms.cxx +++ b/tests/MMS/spatial/fci/fci_mms.cxx @@ -66,11 +66,8 @@ int main(int argc, char** argv) { "div_par_K_grad_par_mod_Original", dump, input, Div_par_K_Grad_par_mod(K, input, flow_ylow, true, ConductionMethod::Original)); fci_op_test( - "div_par_K_grad_par_mod_ProductJK", dump, input, - Div_par_K_Grad_par_mod(K, input, flow_ylow, true, ConductionMethod::ProductJK)); - fci_op_test( - "div_par_K_grad_par_mod_Harmonic", dump, input, - Div_par_K_Grad_par_mod(K, input, flow_ylow, true, ConductionMethod::Harmonic)); + "div_par_K_grad_par_mod_PetscOperator", dump, input, + Div_par_K_Grad_par_mod(K, input, flow_ylow, true, ConductionMethod::PetscOperator)); fci_op_test("laplace_par", dump, input, Laplace_par(input)); // Finite volume methods diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 4c776c7364..7b8ff5e821 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -25,8 +25,7 @@ OPERATORS = ( "div_par", "div_par_K_grad_par", "div_par_K_grad_par_mod_Original", - "div_par_K_grad_par_mod_ProductJK", - "div_par_K_grad_par_mod_Harmonic", + "div_par_K_grad_par_mod_PetscOperator", "laplace_par", "FV_div_par_mod", "FV_div_par_fvv", From 0449b48b5a2715b04b2b41da7035cbe02c7496dd Mon Sep 17 00:00:00 2001 From: David Bold Date: Fri, 16 Oct 2026 10:23:12 +0200 Subject: [PATCH 12/13] Revert "Fall back to toString()" This reverts commit f9468add708ca16925bb42e0e00d804072474ad9. --- src/mesh/difops.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index e811d2f5ba..ab50f684c7 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -396,7 +396,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, "Only bout::ConductionMethod::Original and " "\nbout::ConductionMethod::PetscOperator (with PETSc) are supported for FCI" "\nbut found {}.", - toString(conduction_method)); + conduction_method); } const auto& K_up = Kin.yup(); @@ -494,7 +494,7 @@ Field3D Div_par_K_Grad_par_mod_impl(const Field3DParallel& Kin, flux = C_edge * (f[iyp] - f[i]); } else { - throw BoutException("Unsupported option {}", toString(conduction_method)); + throw BoutException("Unsupported option {}", conduction_method); } result[i] += flux / (coord->dy()[i] * coord->J()[i]); } From c638520061f77461dd318a002b08d339b1537828 Mon Sep 17 00:00:00 2001 From: David Bold Date: Mon, 19 Oct 2026 13:04:08 +0200 Subject: [PATCH 13/13] Add precomputed weights for support operator --- tests/MMS/spatial/fci/runtest | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/MMS/spatial/fci/runtest b/tests/MMS/spatial/fci/runtest index 7b8ff5e821..cd84cd6805 100755 --- a/tests/MMS/spatial/fci/runtest +++ b/tests/MMS/spatial/fci/runtest @@ -101,6 +101,7 @@ def make_grid(nz: int): # Create the grid grid = zb.grid.Grid(poloidal_grid, ycoords, ylength, yperiodic=True) maps = zb.make_maps(grid, field, nslice=1, quiet=True, MXG=1) + zb.weights.modify_maps(maps, partial_boundaries=False) zb.write_maps( grid, field,