diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 12da8ce4c7..07482a0dcb 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -57,9 +57,9 @@ public: } template - adios2::Variable GetArrayVariable(const std::string& varname, adios2::Dims& shape, - const std::vector& dimNames, - int rank) { + adios2::Variable + GetArrayVariable(const std::string& varname, const adios2::Dims& shape, + const std::vector& dimNames, int rank) { adios2::Variable v = io.InquireVariable(varname); if (!v) { adios2::Dims start(shape.size()); diff --git a/include/bout/array.hxx b/include/bout/array.hxx index 2c42f15aad..2305fe3b22 100644 --- a/include/bout/array.hxx +++ b/include/bout/array.hxx @@ -29,6 +29,7 @@ #include #include #include +#include #include #if BOUT_USE_OPENMP @@ -283,6 +284,9 @@ public: return ptr->size(); } + /// Return shape of the array (the `size()` in a length-1 tuple) + std::tuple shape() const { return std::make_tuple(size()); }; + /*! * Returns true if the data is unique to this Array. * diff --git a/include/bout/options.hxx b/include/bout/options.hxx index dbb926838a..1c552cd4f9 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -12,7 +12,7 @@ * options and allows access to all sub-sections * ************************************************************************** -* Copyright 2010-2024 BOUT++ contributors +* Copyright 2010-2025 BOUT++ contributors * * Contact: Ben Dudson, dudson2@llnl.gov * @@ -39,6 +39,7 @@ class Options; #ifndef OPTIONS_H #define OPTIONS_H +#include "bout/array.hxx" #include "bout/bout_types.hxx" #include "bout/field2d.hxx" #include "bout/field3d.hxx" @@ -203,7 +204,8 @@ public: /// The type used to store values using ValueType = bout::utils::variant, Matrix, Tensor>; + Array, Array, Matrix, Matrix, + Tensor, Tensor>; /// Methods to iterate over `Options` auto begin() { return std::begin(children); } @@ -963,9 +965,15 @@ Options& Options::assign<>(FieldPerp val, std::string source); template <> Options& Options::assign<>(Array val, std::string source); template <> +Options& Options::assign<>(Array val, std::string source); +template <> Options& Options::assign<>(Matrix val, std::string source); template <> +Options& Options::assign<>(Matrix val, std::string source); +template <> Options& Options::assign<>(Tensor val, std::string source); +template <> +Options& Options::assign<>(Tensor val, std::string source); /// Specialised similar comparison methods template <> @@ -975,25 +983,31 @@ inline bool Options::similar(BoutReal lhs, BoutReal rhs) const { /// Specialised as routines template <> -std::string Options::as(const std::string& similar_to) const; +auto Options::as(const std::string& similar_to) const -> std::string; +template <> +auto Options::as(const int& similar_to) const -> int; +template <> +auto Options::as(const BoutReal& similar_to) const -> BoutReal; +template <> +auto Options::as(const bool& similar_to) const -> bool; template <> -int Options::as(const int& similar_to) const; +auto Options::as(const Field2D& similar_to) const -> Field2D; template <> -BoutReal Options::as(const BoutReal& similar_to) const; +auto Options::as(const Field3D& similar_to) const -> Field3D; template <> -bool Options::as(const bool& similar_to) const; +auto Options::as(const FieldPerp& similar_to) const -> FieldPerp; template <> -Field2D Options::as(const Field2D& similar_to) const; +auto Options::as(const Array& similar_to) const -> Array; template <> -Field3D Options::as(const Field3D& similar_to) const; +auto Options::as(const Array& similar_to) const -> Array; template <> -FieldPerp Options::as(const FieldPerp& similar_to) const; +auto Options::as(const Matrix& similar_to) const -> Matrix; template <> -Array Options::as>(const Array& similar_to) const; +auto Options::as(const Matrix& similar_to) const -> Matrix; template <> -Matrix Options::as>(const Matrix& similar_to) const; +auto Options::as(const Tensor& similar_to) const -> Tensor; template <> -Tensor Options::as>(const Tensor& similar_to) const; +auto Options::as(const Tensor& similar_to) const -> Tensor; /// Convert \p value to string std::string toString(const Options& value); diff --git a/include/bout/sys/type_name.hxx b/include/bout/sys/type_name.hxx index be4d096b4d..fcd251bf7a 100644 --- a/include/bout/sys/type_name.hxx +++ b/include/bout/sys/type_name.hxx @@ -4,13 +4,19 @@ #ifndef TYPE_NAME_HXX #define TYPE_NAME_HXX +#include "bout/array.hxx" #include "bout/bout_types.hxx" + #include #include class Field2D; class Field3D; class FieldPerp; +template +class Matrix; +template +class Tensor; namespace bout { namespace utils { @@ -41,6 +47,19 @@ std::string typeName(); template <> std::string typeName(); + +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); +template <> +std::string typeName>(); } // namespace utils } // namespace bout diff --git a/include/bout/traits.hxx b/include/bout/traits.hxx index 4e03d6b526..5796c65879 100644 --- a/include/bout/traits.hxx +++ b/include/bout/traits.hxx @@ -1,6 +1,7 @@ #ifndef BOUT_TRAITS_H #define BOUT_TRAITS_H +#include #include class Field; @@ -112,6 +113,10 @@ using EnableIfFieldPerp = /// Enable a function if T is a subclass of Options template using EnableIfOptions = std::enable_if_t>; + +/// Create a `std::index_sequence` with the length of a tuple ``T`` +template +using tuple_index_sequence = std::make_index_sequence>; } // namespace utils } // namespace bout diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index b8c5a5c944..f8d6f08856 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -5,9 +5,9 @@ * simple but common calculations * ************************************************************************** - * 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++. * @@ -517,18 +517,18 @@ std::string toString(const T& val) { /// where the type may be std::string. inline std::string toString(const std::string& val) { return val; } -template <> -inline std::string toString<>(const Array& UNUSED(val)) { +template +inline std::string toString(const Array& UNUSED(val)) { return ""; } -template <> -inline std::string toString<>(const Matrix& UNUSED(val)) { +template +inline std::string toString(const Matrix& UNUSED(val)) { return ""; } -template <> -inline std::string toString<>(const Tensor& UNUSED(val)) { +template +inline std::string toString(const Tensor& UNUSED(val)) { return ""; } diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 5625522795..5078882136 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -2,6 +2,7 @@ #include "bout/traits.hxx" #include +#include #include #include #include @@ -11,6 +12,9 @@ #include #include #include + +#include +#include #include GridFile::GridFile(std::string gridfilename) @@ -471,7 +475,26 @@ bool GridFile::get([[maybe_unused]] Mesh* m, [[maybe_unused]] std::vector& [[maybe_unused]] GridDataSource::Direction dir) { TRACE("GridFile::get(vector)"); - return false; + if (not data.isSet(name)) { + return false; + } + + const auto full_var = data[name].as>(); + + // Check size + if (full_var.size() < len + offset) { + throw BoutException("{} has length {}. Expected {} elements + {} offset", name, + full_var.size(), len, offset); + } + + // Ensure that output variable has the correct size + var.resize(len); + + const auto* it = std::begin(full_var); + std::advance(it, offset); + std::copy_n(it, len, std::begin(var)); + + return true; } bool GridFile::get(Mesh* UNUSED(m), std::vector& var, const std::string& name, diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 574902ea7b..9f366d9456 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -2141,6 +2141,37 @@ void BoutMesh::topology() { add_target(ny_inner - 1, 0, nx); } + // Additional limiters + // Each limiter needs 3 indices: A Y index, start and end X indices + int limiter_count = 0; + Mesh::get(limiter_count, "limiter_count", 0); + if (limiter_count > 0) { + std::vector limiter_yinds; + if (!source->get(this, limiter_yinds, "limiter_yinds", limiter_count)) { + throw BoutException("Couldn't read limiter_yinds vector of length {} from mesh", + limiter_count); + } + std::vector limiter_xstarts; + if (!source->get(this, limiter_xstarts, "limiter_xstarts", limiter_count)) { + throw BoutException("Couldn't read limiter_xstarts vector of length {} from mesh", + limiter_count); + } + std::vector limiter_xends; + if (!source->get(this, limiter_xends, "limiter_xends", limiter_count)) { + throw BoutException("Couldn't read limiter_xend vector of length {} from mesh", + limiter_count); + } + + for (int i = 0; i < limiter_count; ++i) { + int const yind = limiter_yinds[i]; + int const xstart = limiter_xstarts[i]; + int const xend = limiter_xends[i]; + output_info.write("Adding a limiter between y={} and {}. X indices {} to {}\n", + yind, yind + 1, xstart, xend); + add_target(yind, xstart, xend); + } + } + if ((ixseps_inner > 0) && (((PE_YIND * MYSUB > jyseps1_1) && (PE_YIND * MYSUB <= jyseps2_1)) || ((PE_YIND * MYSUB > jyseps1_2) && (PE_YIND * MYSUB <= jyseps2_2)))) { diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 24c9e933c8..077b2a6d85 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -333,15 +333,30 @@ Options& Options::assign<>(Array val, std::string source) { return *this; } template <> +Options& Options::assign<>(Array val, std::string source) { + _set_no_check(std::move(val), std::move(source)); + return *this; +} +template <> Options& Options::assign<>(Matrix val, std::string source) { _set_no_check(std::move(val), std::move(source)); return *this; } template <> +Options& Options::assign<>(Matrix val, std::string source) { + _set_no_check(std::move(val), std::move(source)); + return *this; +} +template <> Options& Options::assign<>(Tensor val, std::string source) { _set_no_check(std::move(val), std::move(source)); return *this; } +template <> +Options& Options::assign<>(Tensor val, std::string source) { + _set_no_check(std::move(val), std::move(source)); + return *this; +} namespace { /// Use FieldFactory to evaluate expression @@ -739,70 +754,69 @@ struct ConvertContainer { }; } // namespace -template <> -Array Options::as>(const Array& similar_to) const { - if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); +namespace { +// `Options::as` helper for `Array`, `Matrix`, `Tensor` (amt) +template +T as_amt(const Options& self, const T& similar_to) { + if (self.isSection()) { + throw BoutException(_("Option {:s} has no value"), self.str()); } - Array result = bout::utils::visit( - ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Array"), - full_name), + const T result = bout::utils::visit( + ConvertContainer{ + fmt::format(_("Value for option {:s} cannot be converted to an {}"), self.str(), + bout::utils::typeName()), similar_to}, - value); + self.value); - // Mark this option as used - value_used = true; + printNameValueSourceLine(self, bout::utils::typeName()); - printNameValueSourceLine(*this, "Array"); + output_info.write("{} {} = {}", _("\tOption "), self.str(), bout::utils::typeName()); + if (self.hasAttribute("source")) { + // Specify the source of the setting + output_info.write(" ({})", + bout::utils::variantToString(self.attributes.at("source"))); + } + output_info.write("\n"); return result; } +} // namespace template <> -Matrix Options::as>(const Matrix& similar_to) const { - if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); - } - - auto result = bout::utils::visit( - ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Matrix"), - full_name), - similar_to}, - value); - - // Mark this option as used +auto Options::as(const Array& similar_to) const -> Array { value_used = true; - - printNameValueSourceLine(*this, "Matrix"); - - return result; + return as_amt(*this, similar_to); } template <> -Tensor Options::as>(const Tensor& similar_to) const { - if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); - } +auto Options::as(const Array& similar_to) const -> Array { + value_used = true; + return as_amt(*this, similar_to); +} - auto result = bout::utils::visit( - ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Tensor"), - full_name), - similar_to}, - value); +template <> +auto Options::as(const Matrix& similar_to) const -> Matrix { + value_used = true; + return as_amt(*this, similar_to); +} - // Mark this option as used +template <> +auto Options::as(const Matrix& similar_to) const -> Matrix { value_used = true; + return as_amt(*this, similar_to); +} - printNameValueSourceLine(*this, "Tensor"); +template <> +auto Options::as(const Tensor& similar_to) const -> Tensor { + value_used = true; + return as_amt(*this, similar_to); +} - return result; +template <> +auto Options::as(const Tensor& similar_to) const -> Tensor { + value_used = true; + return as_amt(*this, similar_to); } // Note: This is defined here rather than in the header @@ -946,12 +960,17 @@ struct GetDimensions { std::vector operator()([[maybe_unused]] int value) { return {1}; } std::vector operator()([[maybe_unused]] BoutReal value) { return {1}; } std::vector operator()([[maybe_unused]] const std::string& value) { return {1}; } - std::vector operator()(const Array& array) { return {array.size()}; } - std::vector operator()(const Matrix& array) { + template + std::vector operator()(const Array& array) { + return {array.size()}; + } + template + std::vector operator()(const Matrix& array) { const auto shape = array.shape(); return {std::get<0>(shape), std::get<1>(shape)}; } - std::vector operator()(const Tensor& array) { + template + std::vector operator()(const Tensor& array) { const auto shape = array.shape(); return {std::get<0>(shape), std::get<1>(shape), std::get<2>(shape)}; } diff --git a/src/sys/options/options_adios.cxx b/src/sys/options/options_adios.cxx index 09797647ed..4a41a014eb 100644 --- a/src/sys/options/options_adios.cxx +++ b/src/sys/options/options_adios.cxx @@ -1,4 +1,5 @@ #include "bout/build_defines.hxx" +#include "bout/traits.hxx" #if BOUT_HAS_ADIOS2 @@ -12,8 +13,11 @@ #include "bout/sys/timer.hxx" #include "adios2.h" + +#include #include -#include +#include +#include #include namespace bout { @@ -313,6 +317,37 @@ const std::vector DIMS_XY = {"x", "y"}; const std::vector DIMS_XZ = {"x", "z"}; const std::vector DIMS_XYZ = {"x", "y", "z"}; +namespace { +using bout::utils::tuple_index_sequence; + +template +auto make_shape_impl(std::size_t first, Tuple&& t, + std::index_sequence /* index */) { + return adios2::Dims{first, + static_cast(std::get(std::forward(t)))...}; +} +// Return an `adios2::Dims` with value ``{first, value.shape()[0]...}`` +template +auto make_shape(std::size_t first, const T& value) { + const auto shape = value.shape(); + return make_shape_impl(first, shape, tuple_index_sequence{}); +} + +template +auto make_start_impl(Tuple&& t, std::index_sequence /* index */) { + // Hey look, a legitimate use of the comma operator to get a bunch + // of zeros the length of the index_sequence! + return adios2::Dims{static_cast(BoutComm::rank()), + (std::get(std::forward(t)), std::size_t{0})...}; +} +// Return an `adios2::Dims` with value ``{rank, 0...}``, with as many zeros as the dimension of ``T`` +template +auto make_start(const T& value) { + const auto shape = value.shape(); + return make_start_impl(shape, tuple_index_sequence{}); +} +} // namespace + /// Visit a variant type, and put the data into a NcVar struct ADIOSPutVarVisitor { ADIOSPutVarVisitor(const std::string& name, ADIOSStream& stream) @@ -323,9 +358,25 @@ struct ADIOSPutVarVisitor { stream.engine.Put(var, value); } + void operator()(const Array& value) { put_helper(value); } + void operator()(const Array& value) { put_helper(value); } + void operator()(const Matrix& value) { put_helper(value); } + void operator()(const Matrix& value) { put_helper(value); } + void operator()(const Tensor& value) { put_helper(value); } + void operator()(const Tensor& value) { put_helper(value); } + private: const std::string& varname; ADIOSStream& stream; + + // helper for `Array`, `Matrix`, `Tensor` + template