diff --git a/include/bout/bout_types.hxx b/include/bout/bout_types.hxx index 1d8b459c0c..41ca37c99d 100644 --- a/include/bout/bout_types.hxx +++ b/include/bout/bout_types.hxx @@ -51,6 +51,7 @@ constexpr CELL_LOC CELL_VSHIFT = CELL_LOC::vshift; std::string toString(CELL_LOC location); CELL_LOC CELL_LOCFromString(const std::string& location_string); +inline std::string format_as(const CELL_LOC& loc) { return toString(loc); } /// Differential methods. Both central and upwind enum class DIFF_METHOD { deflt, u1, u2, c2, w2, w3, c4, u3, fft, split, s2 }; @@ -67,6 +68,7 @@ constexpr DIFF_METHOD DIFF_SPLIT = DIFF_METHOD::split; constexpr DIFF_METHOD DIFF_S2 = DIFF_METHOD::s2; std::string toString(DIFF_METHOD location); +inline std::string format_as(const DIFF_METHOD& method) { return toString(method); } /// Specify grid region for looping enum class REGION { all, nobndry, nox, noy, noz }; @@ -77,6 +79,7 @@ constexpr REGION RGN_NOY = REGION::noy; constexpr REGION RGN_NOZ = REGION::noz; std::string toString(REGION region); +inline std::string format_as(const REGION& region) { return toString(region); } /// To identify particular directions (in index space): /// - X, Y, Z are the coordinate directions @@ -88,6 +91,7 @@ std::string toString(REGION region); enum class DIRECTION { X, Y, Z, YAligned, YOrthogonal }; std::string toString(DIRECTION direction); +inline std::string format_as(const DIRECTION& dir) { return toString(dir); } /// Identify kind of a field's y-direction /// - Standard is the default for the Mesh/Coordinates/ParallelTransform @@ -97,6 +101,7 @@ enum class YDirectionType { Standard, Aligned }; std::string toString(YDirectionType d); YDirectionType YDirectionTypeFromString(const std::string& y_direction_string); +inline std::string format_as(const YDirectionType& d) { return toString(d); } /// Identify kind of a field's z-direction /// - Standard is the default @@ -107,6 +112,7 @@ enum class ZDirectionType { Standard, Average }; std::string toString(ZDirectionType d); ZDirectionType ZDirectionTypeFromString(const std::string& z_direction_string); +inline std::string format_as(const ZDirectionType& d) { return toString(d); } /// Container for direction types struct DirectionTypes { @@ -114,6 +120,9 @@ struct DirectionTypes { ZDirectionType z; }; +std::string toString(const DirectionTypes& dir); +inline std::string format_as(const DirectionTypes& dir) { return toString(dir); } + /// Check whether direction types are compatible, so two fields with attributes /// d1 and d2 respectively can be added, subtracted, etc. bool areDirectionsCompatible(const DirectionTypes& d1, const DirectionTypes& d2); @@ -124,11 +133,13 @@ void swap(const DirectionTypes& first, const DirectionTypes& second); enum class STAGGER { None, C2L, L2C }; std::string toString(STAGGER stagger); +inline std::string format_as(const STAGGER& stagger) { return toString(stagger); } /// To identify types of derivative method combinations enum class DERIV { Standard, StandardSecond, StandardFourth, Upwind, Flux }; std::string toString(DERIV deriv); +inline std::string format_as(const DERIV& deriv) { return toString(deriv); } // A small struct that can be used to wrap a specific enum value, giving // it a unique type that can be passed as a valid type to templates and diff --git a/include/bout/deriv_store.hxx b/include/bout/deriv_store.hxx index 7c08802cae..36412ec531 100644 --- a/include/bout/deriv_store.hxx +++ b/include/bout/deriv_store.hxx @@ -135,7 +135,7 @@ struct DerivativeStore { if (standard.count(key) != 0) { throw BoutException("Trying to override standard derivative : " "direction {:s}, stagger {:s}, key {:s}", - toString(direction), toString(stagger), methodName); + direction, stagger, methodName); } standard[key] = func; break; @@ -143,7 +143,7 @@ struct DerivativeStore { if (standardSecond.count(key) != 0) { throw BoutException("Trying to override standardSecond derivative : " "direction {:s}, stagger {:s}, key {:s}", - toString(direction), toString(stagger), methodName); + direction, stagger, methodName); } standardSecond[key] = func; break; @@ -151,14 +151,14 @@ struct DerivativeStore { if (standardFourth.count(key) != 0) { throw BoutException("Trying to override standardFourth derivative : " "direction {:s}, stagger {:s}, key {:s}", - toString(direction), toString(stagger), methodName); + direction, stagger, methodName); } standardFourth[key] = func; break; default: throw BoutException("Invalid function signature in registerDerivative : Function " "signature 'standard' but derivative type {:s} passed", - toString(derivType)); + derivType); }; // Register this method name in lookup of known methods @@ -177,7 +177,7 @@ struct DerivativeStore { if (upwind.count(key) != 0) { throw BoutException("Trying to override upwind derivative : " "direction {:s}, stagger {:s}, key {:s}", - toString(direction), toString(stagger), methodName); + direction, stagger, methodName); } upwind[key] = func; break; @@ -185,14 +185,14 @@ struct DerivativeStore { if (flux.count(key) != 0) { throw BoutException("Trying to override flux derivative : " "direction {:s}, stagger {:s}, key {:s}", - toString(direction), toString(stagger), methodName); + direction, stagger, methodName); } flux[key] = func; break; default: throw BoutException("Invalid function signature in registerDerivative : Function " "signature 'upwind/flux' but derivative type {:s} passed", - toString(derivType)); + derivType); }; // Register this method name in lookup of known methods @@ -239,7 +239,7 @@ struct DerivativeStore { } else { throw BoutException("getStandardDerivative only works for derivType in {{Standard, " "StandardSecond, StandardFourth}} but receieved {:s}", - toString(derivType)); + derivType); }; const auto resultOfFind = theMap->find(key); @@ -249,7 +249,7 @@ struct DerivativeStore { throw BoutException("Couldn't find requested method {:s} in map for standard " "derivative of type {:s}.", - getMethodName(realName, direction, stagger), toString(derivType)); + getMethodName(realName, direction, stagger), derivType); }; standardFunc getStandard2ndDerivative(std::string name, DIRECTION direction, @@ -282,7 +282,7 @@ struct DerivativeStore { throw BoutException( "getFlowDerivative only works for derivType in {{Upwind, Flux}} " "but received {:s}", - toString(derivType)); + derivType); }; const auto resultOfFind = theMap->find(key); @@ -292,7 +292,7 @@ struct DerivativeStore { throw BoutException( "Couldn't find requested method {:s} in map for standard flow of type {:s}.", - getMethodName(realName, direction, stagger), toString(derivType)); + getMethodName(realName, direction, stagger), derivType); } upwindFunc getUpwindDerivative(std::string name, DIRECTION direction, diff --git a/include/bout/difops.hxx b/include/bout/difops.hxx index 0b3320c1f1..e20aef2459 100644 --- a/include/bout/difops.hxx +++ b/include/bout/difops.hxx @@ -219,6 +219,10 @@ Field3D Div_par_K_Grad_par(const Field3DParallel& kY, const Field3DParallel& f, /// different results when coefficients or cell sizes vary strongly. BOUT_ENUM_CLASS_NS(bout, ConductionMethod, Original, ProductJK, Harmonic); +namespace bout { +inline std::string format_as(const ConductionMethod& method) { return toString(method); } +} // namespace bout + /// Version with energy flow diagnostic /// For FCI fields, `flow_ylow` is currently returned as zero. Field3D diff --git a/include/bout/field.hxx b/include/bout/field.hxx index fc24020ff6..7e97f71d29 100644 --- a/include/bout/field.hxx +++ b/include/bout/field.hxx @@ -160,53 +160,53 @@ inline bool areFieldsCompatible(const Field& field1, const Field& field2) { } #if CHECKLEVEL >= 1 -#define ASSERT1_FIELDS_COMPATIBLE(field1, field2) \ - if ((field1).getLocation() != (field2).getLocation()) { \ - throw BoutException("Error in {:s}:{:d}\nFields at different position:" \ - "`{:s}` at {:s}, `{:s}` at {:s}", \ - __FILE__, __LINE__, #field1, toString((field1).getLocation()), \ - #field2, toString((field2).getLocation())); \ - } \ - if ((field1).getCoordinates() != (field2).getCoordinates()) { \ - throw BoutException("Error in {:s}:{:d}\nFields have different coordinates:" \ - "`{:s}` at {:p}, `{:s}` at {:p}", \ - __FILE__, __LINE__, #field1, \ - static_cast((field1).getCoordinates()), #field2, \ - static_cast((field2).getCoordinates())); \ - } \ - if ((field1).getMesh() != (field2).getMesh()) { \ - throw BoutException("Error in {:s}:{:d}\nFields are on different Meshes:" \ - "`{:s}` at {:p}, `{:s}` at {:p}", \ - __FILE__, __LINE__, #field1, \ - static_cast((field1).getMesh()), #field2, \ - static_cast((field2).getMesh())); \ - } \ - if (!areDirectionsCompatible((field1).getDirections(), (field2).getDirections())) { \ - throw BoutException("Error in {:s}:{:d}\nFields at different directions:" \ - "`{:s}` at {:s}, `{:s}` at {:s}", \ - __FILE__, __LINE__, #field1, toString((field1).getDirections()), \ - #field2, toString((field2).getDirections())); \ +#define ASSERT1_FIELDS_COMPATIBLE(field1, field2) \ + if ((field1).getLocation() != (field2).getLocation()) { \ + throw BoutException("Error in {:s}:{:d}\nFields at different position:" \ + "`{:s}` at {:s}, `{:s}` at {:s}", \ + __FILE__, __LINE__, #field1, (field1).getLocation(), #field2, \ + (field2).getLocation()); \ + } \ + if ((field1).getCoordinates() != (field2).getCoordinates()) { \ + throw BoutException("Error in {:s}:{:d}\nFields have different coordinates:" \ + "`{:s}` at {:p}, `{:s}` at {:p}", \ + __FILE__, __LINE__, #field1, \ + static_cast((field1).getCoordinates()), #field2, \ + static_cast((field2).getCoordinates())); \ + } \ + if ((field1).getMesh() != (field2).getMesh()) { \ + throw BoutException("Error in {:s}:{:d}\nFields are on different Meshes:" \ + "`{:s}` at {:p}, `{:s}` at {:p}", \ + __FILE__, __LINE__, #field1, \ + static_cast((field1).getMesh()), #field2, \ + static_cast((field2).getMesh())); \ + } \ + if (!areDirectionsCompatible((field1).getDirections(), (field2).getDirections())) { \ + throw BoutException("Error in {:s}:{:d}\nFields at different directions:" \ + "`{:s}` at {:s}, `{:s}` at {:s}", \ + __FILE__, __LINE__, #field1, (field1).getDirections(), #field2, \ + (field2).getDirections()); \ } -#define ASSERT1_EXPR_COMPATIBLE(expr1, expr2) \ - if ((expr1).getLocation() != (expr2).getLocation()) { \ - throw BoutException("Error in {:s}:{:d}\nFields at different position:" \ - "`{:s}` at {:s}, `{:s}` at {:s}", \ - __FILE__, __LINE__, #expr1, toString((expr1).getLocation()), \ - #expr2, toString((expr2).getLocation())); \ - } \ - if ((expr1).getMesh() != (expr2).getMesh()) { \ - throw BoutException("Error in {:s}:{:d}\nFields are on different Meshes:" \ - "`{:s}` at {:p}, `{:s}` at {:p}", \ - __FILE__, __LINE__, #expr1, \ - static_cast((expr1).getMesh()), #expr2, \ - static_cast((expr2).getMesh())); \ - } \ - if (!areDirectionsCompatible((expr1).getDirections(), (expr2).getDirections())) { \ - throw BoutException("Error in {:s}:{:d}\nFields at different directions:" \ - "`{:s}` at {:s}, `{:s}` at {:s}", \ - __FILE__, __LINE__, #expr1, toString((expr1).getDirections()), \ - #expr2, toString((expr2).getDirections())); \ +#define ASSERT1_EXPR_COMPATIBLE(expr1, expr2) \ + if ((expr1).getLocation() != (expr2).getLocation()) { \ + throw BoutException("Error in {:s}:{:d}\nFields at different position:" \ + "`{:s}` at {:s}, `{:s}` at {:s}", \ + __FILE__, __LINE__, #expr1, (expr1).getLocation(), #expr2, \ + (expr2).getLocation()); \ + } \ + if ((expr1).getMesh() != (expr2).getMesh()) { \ + throw BoutException("Error in {:s}:{:d}\nFields are on different Meshes:" \ + "`{:s}` at {:p}, `{:s}` at {:p}", \ + __FILE__, __LINE__, #expr1, \ + static_cast((expr1).getMesh()), #expr2, \ + static_cast((expr2).getMesh())); \ + } \ + if (!areDirectionsCompatible((expr1).getDirections(), (expr2).getDirections())) { \ + throw BoutException("Error in {:s}:{:d}\nFields at different directions:" \ + "`{:s}` at {:s}, `{:s}` at {:s}", \ + __FILE__, __LINE__, #expr1, (expr1).getDirections(), #expr2, \ + (expr2).getDirections()); \ } #else @@ -291,19 +291,8 @@ namespace bout { /// used during initialization, where we always want to check inputs, even for optimized /// builds. template -inline void checkFinite(const T& f, const std::string& name = "field", - const std::string& rgn = "RGN_ALL") { - - if (!f.isAllocated()) { - throw BoutException("{:s} is not allocated", name); - } - - BOUT_FOR_SERIAL(i, f.getRegion(rgn)) { - if (!std::isfinite(f[i])) { - throw BoutException("{:s} is not finite at {:s}", name, toString(i)); - } - } -} +void checkFinite(const T& f, const std::string& name = "field", + const std::string& rgn = "RGN_ALL"); /// Check if all values of a field \p var are positive. Loops over all points including /// the boundaries by default (can be changed using the \p rgn argument) @@ -314,21 +303,8 @@ inline void checkFinite(const T& f, const std::string& name = "field", /// be used during initialization, where we always want to check inputs, even for /// optimized builds. template -inline void checkPositive(const T& f, const std::string& name = "field", - const std::string& rgn = "RGN_ALL") { - - if (!f.isAllocated()) { - throw BoutException("{:s} is not allocated", name); - } - - BOUT_FOR_SERIAL(i, f.getRegion(rgn)) { - if (f[i] <= 0.) { - throw BoutException("{:s} ({:s} {:s}) is {:e} (not positive) at {:s}", name, - toString(f.getLocation()), toString(f.getDirections()), f[i], - toString(i)); - } - } -} +void checkPositive(const T& f, const std::string& name = "field", + const std::string& rgn = "RGN_ALL"); } // namespace bout //////////////// NON-MEMBER FUNCTIONS ////////////////// diff --git a/include/bout/interpolation.hxx b/include/bout/interpolation.hxx index 40ff603712..bf9b1de466 100644 --- a/include/bout/interpolation.hxx +++ b/include/bout/interpolation.hxx @@ -195,7 +195,7 @@ interp_to(const T& var, CELL_LOC loc, const std::string& region = "RGN_ALL") { // This should never happen throw BoutException("Unsupported direction of interpolation\n" " - don't know how to interpolate to {:s}", - toString(loc)); + loc); } }; diff --git a/include/bout/mesh.hxx b/include/bout/mesh.hxx index dd7ccbfc0b..3d630b5ce3 100644 --- a/include/bout/mesh.hxx +++ b/include/bout/mesh.hxx @@ -88,6 +88,8 @@ public: BOUT_ENUM_CLASS(BoundaryParType, all, xin, xout, fwd, bwd, xin_fwd, xout_fwd, xin_bwd, xout_bwd, SIZE); +inline auto format_as(const BoundaryParType& type) { return toString(type); } + template using RegisterMesh = MeshFactory::RegisterInFactory; diff --git a/include/bout/operatorstencil.hxx b/include/bout/operatorstencil.hxx index 1135a79fc7..4ea7579465 100644 --- a/include/bout/operatorstencil.hxx +++ b/include/bout/operatorstencil.hxx @@ -33,10 +33,14 @@ #include #include #include +#include #include #include +#include "bout/boutexception.hxx" +#include "bout/unused.hxx" #include +#include #include /// A representation of offsets for indices, which can be added and @@ -48,12 +52,12 @@ struct IndexOffset { "IndexOffset only works with SpecificInd types"); int dx = 0, dy = 0, dz = 0; - IndexOffset xp(int delta_x = 1) const { return {dx + delta_x, dy, dz}; } - IndexOffset xm(int delta_x = 1) const { return xp(-delta_x); } - IndexOffset yp(int delta_y = 1) const { return {dx, dy + delta_y, dz}; } - IndexOffset ym(int delta_y = 1) const { return yp(-delta_y); } - IndexOffset zp(int delta_z = 1) const { return {dx, dy, dz + delta_z}; } - IndexOffset zm(int delta_z = 1) const { return zp(-delta_z); } + IndexOffset xp(const int delta_x = 1) const { return {dx + delta_x, dy, dz}; } + IndexOffset xm(const int delta_x = 1) const { return xp(-delta_x); } + IndexOffset yp(const int delta_y = 1) const { return {dx, dy + delta_y, dz}; } + IndexOffset ym(const int delta_y = 1) const { return yp(-delta_y); } + IndexOffset zp(const int delta_z = 1) const { return {dx, dy, dz + delta_z}; } + IndexOffset zm(const int delta_z = 1) const { return zp(-delta_z); } IndexOffset& operator+=(const IndexOffset& n) { dx += n.dx; @@ -146,13 +150,13 @@ public: std::find_if(std::begin(stencils), std::end(stencils), [&i](const auto& stencil) -> bool { return stencil.test(i); }); if (result == std::end(stencils)) { - throw BoutException("No stencil was specified for element " + toString(i)); + throw BoutException("No stencil was specified for element {}", i); } return result->part; } /// Get the number of elements in the ith stencil-part - int getStencilSize(int i) const { return getStencilPart(i).size(); } + int getStencilSize(const int i) const { return getStencilPart(i).size(); } /// Get the number of elements in the stencil part to be used at /// this index. @@ -163,7 +167,7 @@ public: /// Returns a list of indices for which the stencils contain the /// argument - const std::vector getIndicesWithStencilIncluding(const T& i) const { + std::vector getIndicesWithStencilIncluding(const T& i) const { std::vector indices; int count = 0; for (const auto& item : stencils) { @@ -179,10 +183,10 @@ public: } /// Iterators for the underlying vector data type - using iterator = typename std::vector::iterator; - using const_iterator = typename std::vector::const_iterator; - using reverse_iterator = typename std::vector::reverse_iterator; - using const_reverse_iterator = typename std::vector::const_reverse_iterator; + using iterator = std::vector::iterator; + using const_iterator = std::vector::const_iterator; + using reverse_iterator = std::vector::reverse_iterator; + using const_reverse_iterator = std::vector::const_reverse_iterator; iterator begin() { return std::begin(stencils); } const_iterator begin() const { return std::begin(stencils); } @@ -219,7 +223,7 @@ private: /// depend on all of their neighbours to a depth of one, including /// corners. template -OperatorStencil squareStencil(Mesh* localmesh) { +OperatorStencil squareStencil(const Mesh* localmesh) { OperatorStencil stencil; IndexOffset zero; std::set> offsets = { @@ -267,7 +271,7 @@ OperatorStencil squareStencil(Mesh* localmesh) { /// depend on all of their neighbours to a depth of one, excluding /// corners. template -OperatorStencil starStencil(Mesh* localmesh) { +OperatorStencil starStencil(const Mesh* localmesh) { OperatorStencil stencil; IndexOffset zero; std::set> offsets = { diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index 0679324723..420ded10d6 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -558,10 +558,6 @@ inline std::string toString(const bool& val) { return "false"; } -inline std::string toString(const DirectionTypes& dir) { - return "{" + toString(dir.y) + ", " + toString(dir.z) + "}"; -} - /// Convert a time stamp to a string /// This uses std::localtime and std::put_time std::string toString(const time_t& time); diff --git a/src/field/field.cxx b/src/field/field.cxx index 8c7e8bbfee..7db3fd65fd 100644 --- a/src/field/field.cxx +++ b/src/field/field.cxx @@ -27,10 +27,11 @@ #include #include #include +#include +#include #include -#include -#include -#include +#include +#include Field::Field(Mesh* localmesh, CELL_LOC location_in, DirectionTypes directions_in) : FieldData(localmesh, location_in), directions(directions_in) {} @@ -51,3 +52,54 @@ bool Field::isFci() const { } return not coords->getParallelTransform().canToFromFieldAligned(); } + +namespace bout { + +template +void checkFinite(const T& f, const std::string& name, const std::string& rgn) { + + if (!f.isAllocated()) { + throw BoutException("{:s} is not allocated", name); + } + + BOUT_FOR_SERIAL(i, f.getRegion(rgn)) { + if (!std::isfinite(f[i])) { + throw BoutException("{:s} is not finite at {}", name, i); + } + } +} + +template +void checkPositive(const T& f, const std::string& name, const std::string& rgn) { + + if (!f.isAllocated()) { + throw BoutException("{:s} is not allocated", name); + } + + BOUT_FOR_SERIAL(i, f.getRegion(rgn)) { + if (f[i] <= 0.) { + throw BoutException("{:s} ({:s} {:s}) is {:e} (not positive) at {}", name, + f.getLocation(), f.getDirections(), f[i], i); + } + } +} + +template void checkFinite(const Field2D&, const std::string&, + const std::string&); + +template void checkFinite(const Field3D&, const std::string&, + const std::string&); + +template void checkFinite(const FieldPerp&, const std::string&, + const std::string&); + +template void checkPositive(const Field2D&, const std::string&, + const std::string&); + +template void checkPositive(const Field3D&, const std::string&, + const std::string&); + +template void checkPositive(const FieldPerp&, const std::string&, + const std::string&); + +} // namespace bout diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 0fe165a04c..5fbc1d3d5d 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -279,7 +279,7 @@ bool GridFile::getField(Mesh* m, T& var, const std::string& name, BoutReal def, if (location != CELL_DEFAULT and var.getLocation() != location) { throw BoutException("Incorrect location of field {:s} in grid file, expected {:s}, " "got {:s}.", - name, toString(location), toString(var.getLocation())); + name, location, var.getLocation()); } if (var.isAllocated()) { diff --git a/src/mesh/difops.cxx b/src/mesh/difops.cxx index 1b4323cb92..30047647ec 100644 --- a/src/mesh/difops.cxx +++ b/src/mesh/difops.cxx @@ -543,8 +543,7 @@ 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); } throw BoutException( - "Unknown method `{}` - choose from `Original`, `ProductJK` or `Harmonic`.", - toString(method)); + "Unknown method `{}` - choose from `Original`, `ProductJK` or `Harmonic`.", method); } /******************************************************************************* * Delp2 diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index e1cb27cd73..80752a9f4a 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -3276,7 +3276,7 @@ void BoutMesh::addBoundaryPar(std::shared_ptr bndry, par_boundary[static_cast(BoundaryParType::bwd)].push_back(bndry); break; default: - throw BoutException("Unexpected type of boundary {}", toString(type)); + throw BoutException("Unexpected type of boundary {}", type); } par_boundary[static_cast(type)].push_back(bndry); par_boundary[static_cast(BoundaryParType::all)].push_back(bndry); diff --git a/src/mesh/parallel/shiftedmetric.cxx b/src/mesh/parallel/shiftedmetric.cxx index 17b62257f4..c98333edc3 100644 --- a/src/mesh/parallel/shiftedmetric.cxx +++ b/src/mesh/parallel/shiftedmetric.cxx @@ -169,7 +169,7 @@ Field3D ShiftedMetric::shiftZ(const Field3D& f, const Tensor& phs, Field3D result{emptyFrom(f).setDirectionY(y_direction_out)}; - BOUT_FOR(i, mesh.getRegion2D(toString(region))) { + BOUT_FOR(i, mesh.getRegion2D(region)) { shiftZ(&f(i, 0), &phs(i.x(), i.y(), 0), &result(i, 0)); } diff --git a/src/solver/solver.cxx b/src/solver/solver.cxx index 29b50cd915..df0013dd3b 100644 --- a/src/solver/solver.cxx +++ b/src/solver/solver.cxx @@ -1191,8 +1191,7 @@ void Solver::save_derivs(BoutReal* dudata) { if (f.var->getLocation() != (f.F_var)->getLocation()) { 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); + f.var->getLocation(), f.F_var->getLocation(), f.name); } } diff --git a/src/sys/bout_types.cxx b/src/sys/bout_types.cxx index b27fab7ae7..cbcf19e996 100644 --- a/src/sys/bout_types.cxx +++ b/src/sys/bout_types.cxx @@ -73,6 +73,10 @@ std::string toString(DIRECTION direction) { return safeAt(DIRECTIONtoString, direction); } +std::string toString(const DirectionTypes& dir) { + return "{" + toString(dir.y) + ", " + toString(dir.z) + "}"; +} + void swap(DirectionTypes& first, DirectionTypes& second) { DirectionTypes temp = first; first = second;