From cc1672d1b9e28af98089fcdff59d6c2f8aa8594d Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 14 Jun 2023 12:06:27 -0700 Subject: [PATCH 01/15] Add Array to Options Enable 1D arrays of integers to be read from NetCDF files, and made available via `Mesh::get()`. Mainly useful for mesh construction. --- include/bout/options.hxx | 6 +++++- include/bout/utils.hxx | 4 ++-- src/mesh/data/gridfromfile.cxx | 20 +++++++++++++------ src/sys/options.cxx | 31 ++++++++++++++++++++++++++++++ src/sys/options/options_netcdf.cxx | 4 ++++ 5 files changed, 56 insertions(+), 9 deletions(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index bf1704100f..fae05b8ecc 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -196,7 +196,7 @@ public: /// The type used to store values using ValueType = bout::utils::variant, Matrix, Tensor>; + Array, Array, Matrix, Tensor>; /// The type used to store attributes /// Extends the variant class so that cast operator can be implemented @@ -874,6 +874,8 @@ void Options::assign<>(FieldPerp val, std::string source); template <> void Options::assign<>(Array val, std::string source); template <> +void Options::assign<>(Array val, std::string source); +template <> void Options::assign<>(Matrix val, std::string source); template <> void Options::assign<>(Tensor val, std::string source); @@ -902,6 +904,8 @@ FieldPerp Options::as(const FieldPerp& similar_to) const; template <> Array Options::as>(const Array& similar_to) const; template <> +Array Options::as>(const Array& similar_to) const; +template <> Matrix Options::as>(const Matrix& similar_to) const; template <> Tensor Options::as>(const Tensor& similar_to) const; diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index 450d3eaf87..e2ec5b24db 100644 --- a/include/bout/utils.hxx +++ b/include/bout/utils.hxx @@ -543,8 +543,8 @@ 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 ""; } diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 7e875bd109..7fab486e54 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -138,7 +138,8 @@ 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()}; } + template + std::vector operator()(const Array& array) { return {array.size()}; } std::vector operator()(const Matrix& array) { const auto shape = array.shape(); return {std::get<0>(shape), std::get<1>(shape)}; @@ -471,13 +472,20 @@ void GridFile::readField(Mesh* m, const std::string& name, int UNUSED(ys), int U } } -bool GridFile::get(MAYBE_UNUSED(Mesh* m), MAYBE_UNUSED(std::vector& var), - MAYBE_UNUSED(const std::string& name), MAYBE_UNUSED(int len), - MAYBE_UNUSED(int offset), - MAYBE_UNUSED(GridDataSource::Direction dir)) { +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)"); - return false; + if (not data.isSet(name)) { + return false; + } + + const auto full_var = data[name].as>(); + 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/sys/options.cxx b/src/sys/options.cxx index 8b49b1f3f1..fec4073682 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -288,6 +288,10 @@ void Options::assign<>(Array val, std::string source) { _set_no_check(std::move(val), std::move(source)); } template <> +void Options::assign<>(Array val, std::string source) { + _set_no_check(std::move(val), std::move(source)); +} +template <> void Options::assign<>(Matrix val, std::string source) { _set_no_check(std::move(val), std::move(source)); } @@ -723,6 +727,33 @@ Array Options::as>(const Array& similar_to) return result; } +template <> +Array Options::as>(const Array& similar_to) const { + if (is_section) { + throw BoutException(_("Option {:s} has no value"), full_name); + } + + Array result = bout::utils::visit( + ConvertContainer>{ + fmt::format( + _("Value for option {:s} cannot be converted to an Array"), + full_name), + similar_to}, + value); + + // Mark this option as used + value_used = true; + + output_info << _("\tOption ") << full_name << " = Array"; + if (hasAttribute("source")) { + // Specify the source of the setting + output_info << " (" << bout::utils::variantToString(attributes.at("source")) << ")"; + } + output_info << endl; + + return result; +} + template <> Matrix Options::as>(const Matrix& similar_to) const { if (is_section) { diff --git a/src/sys/options/options_netcdf.cxx b/src/sys/options/options_netcdf.cxx index d7ceeaea60..7c21b309bc 100644 --- a/src/sys/options/options_netcdf.cxx +++ b/src/sys/options/options_netcdf.cxx @@ -88,6 +88,10 @@ void readGroup(const std::string& filename, const NcGroup& group, Options& resul Array value(static_cast(dims[0].getSize())); var.getVar(value.begin()); result[var_name] = value; + } else if (var_type == ncInt or var_type == ncShort) { + Array value(static_cast(dims[0].getSize())); + var.getVar(value.begin()); + result[var_name] = value; } else if ((var_type == ncString) or (var_type == ncChar)) { std::string value; value.resize(dims[0].getSize()); From e654af65c6ea32a0ee4f6f07659e246ac70ac88b Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 14 Jun 2023 18:06:49 -0700 Subject: [PATCH 02/15] GridFile: Check sizes, allocate vector ints When reading 1D vector of ints, check that the input is long enough, and resize the output vector. --- src/mesh/data/gridfromfile.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 7fab486e54..7c02cf4ab7 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -481,6 +481,16 @@ bool GridFile::get(Mesh* UNUSED(m), std::vector& var, const std::string& na } 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)); From 359766313a8b878f056008358192cb4653130384 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 14 Jun 2023 18:08:32 -0700 Subject: [PATCH 03/15] Add limiters in input mesh A short-term fix, allowing extra limiters to be added with inputs: limiter_count : int limiter_yinds : [int], length limiter_count limiter_xstarts : [int], length limiter_count limiter_xends : [int], length limiter_count The limiter(s) are added between yinds[i] and yinds[i] + 1 --- src/mesh/impls/bout/boutmesh.cxx | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index a802d3f5b3..448832ea57 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -2073,6 +2073,32 @@ 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, limiter_xstarts, limiter_xends; + if (!source->get(this, limiter_yinds, "limiter_yinds", limiter_count)) { + throw BoutException("Couldn't read limiter_yinds vector of length {} from mesh", limiter_count); + } + if (!source->get(this, limiter_xstarts, "limiter_xstarts", limiter_count)) { + throw BoutException("Couldn't read limiter_xstarts vector of length {} from mesh", limiter_count); + } + 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 yind = limiter_yinds[i]; + int xstart = limiter_xstarts[i]; + int 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)))) { From 1c1d834c822857239efa5f8189b187845cebe073 Mon Sep 17 00:00:00 2001 From: bendudson Date: Thu, 15 Jun 2023 01:17:21 +0000 Subject: [PATCH 04/15] Apply clang-format changes --- include/bout/options.hxx | 6 +++--- src/mesh/data/gridfromfile.cxx | 10 ++++++---- src/mesh/impls/bout/boutmesh.cxx | 11 +++++++---- src/sys/options.cxx | 5 ++--- 4 files changed, 18 insertions(+), 14 deletions(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index fae05b8ecc..e790b405b5 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -194,9 +194,9 @@ public: static void cleanup(); /// The type used to store values - using ValueType = - bout::utils::variant, Array, Matrix, Tensor>; + using ValueType = bout::utils::variant, Array, + Matrix, Tensor>; /// The type used to store attributes /// Extends the variant class so that cast operator can be implemented diff --git a/src/mesh/data/gridfromfile.cxx b/src/mesh/data/gridfromfile.cxx index 7c02cf4ab7..eed70776b3 100644 --- a/src/mesh/data/gridfromfile.cxx +++ b/src/mesh/data/gridfromfile.cxx @@ -138,8 +138,10 @@ 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}; } - template - std::vector operator()(const Array& array) { return {array.size()}; } + template + std::vector operator()(const Array& array) { + return {array.size()}; + } std::vector operator()(const Matrix& array) { const auto shape = array.shape(); return {std::get<0>(shape), std::get<1>(shape)}; @@ -484,8 +486,8 @@ bool GridFile::get(Mesh* UNUSED(m), std::vector& var, const std::string& na // Check size if (full_var.size() < len + offset) { - throw BoutException("{} has length {}. Expected {} elements + {} offset", - name, 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 diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 448832ea57..c2c9b51ba3 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -2080,13 +2080,16 @@ void BoutMesh::topology() { if (limiter_count > 0) { std::vector limiter_yinds, limiter_xstarts, limiter_xends; if (!source->get(this, limiter_yinds, "limiter_yinds", limiter_count)) { - throw BoutException("Couldn't read limiter_yinds vector of length {} from mesh", limiter_count); + throw BoutException("Couldn't read limiter_yinds vector of length {} from mesh", + limiter_count); } if (!source->get(this, limiter_xstarts, "limiter_xstarts", limiter_count)) { - throw BoutException("Couldn't read limiter_xstarts vector of length {} from mesh", limiter_count); + throw BoutException("Couldn't read limiter_xstarts vector of length {} from mesh", + limiter_count); } if (!source->get(this, limiter_xends, "limiter_xends", limiter_count)) { - throw BoutException("Couldn't read limiter_xend vector of length {} from mesh", limiter_count); + throw BoutException("Couldn't read limiter_xend vector of length {} from mesh", + limiter_count); } for (int i = 0; i < limiter_count; ++i) { @@ -2094,7 +2097,7 @@ void BoutMesh::topology() { int xstart = limiter_xstarts[i]; int xend = limiter_xends[i]; output_info.write("Adding a limiter between y={} and {}. X indices {} to {}\n", - yind, yind+1, xstart, xend); + yind, yind + 1, xstart, xend); add_target(yind, xstart, xend); } } diff --git a/src/sys/options.cxx b/src/sys/options.cxx index fec4073682..1c50db5896 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -735,9 +735,8 @@ Array Options::as>(const Array& similar_to) const { Array result = bout::utils::visit( ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Array"), - full_name), + fmt::format(_("Value for option {:s} cannot be converted to an Array"), + full_name), similar_to}, value); From eeb3426aebeed2cb7735e2605c36fd977930eb17 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Fri, 30 May 2025 11:35:00 -0700 Subject: [PATCH 05/15] Options: Add Matrix and Tensor Enable Options to store index arrays --- include/bout/options.hxx | 21 ++++++++------ include/bout/utils.hxx | 12 ++++---- src/sys/options.cxx | 63 ++++++++++++++++++++++++++++++++++++++-- 3 files changed, 78 insertions(+), 18 deletions(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index d6f0e460e2..6ee9beeb6d 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 * @@ -201,9 +201,9 @@ public: } /// The type used to store values - using ValueType = - bout::utils::variant, Matrix, Tensor>; + using ValueType = bout::utils::variant, Array, + Matrix, Matrix, Tensor, Tensor>; /// A tree representation with leaves containing ValueType. /// Used to construct Options from initializer lists. @@ -271,11 +271,6 @@ public: /// Free all memory static void cleanup(); - /// The type used to store values - using ValueType = bout::utils::variant, Array, - Matrix, Tensor>; - /// The type used to store attributes /// Extends the variant class so that cast operator can be implemented /// and assignment operator overloaded @@ -964,7 +959,11 @@ 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 <> @@ -994,7 +993,11 @@ Array Options::as>(const Array& similar_to) const; template <> Matrix Options::as>(const Matrix& similar_to) const; template <> +Matrix Options::as>(const Matrix& similar_to) const; +template <> Tensor Options::as>(const Tensor& similar_to) const; +template <> +Tensor Options::as>(const Tensor& similar_to) const; /// Convert \p value to string std::string toString(const Options& value); diff --git a/include/bout/utils.hxx b/include/bout/utils.hxx index 343631691c..d933129ea7 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++. * @@ -523,13 +523,13 @@ 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/sys/options.cxx b/src/sys/options.cxx index 027724eba6..951a46aca4 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -343,10 +343,20 @@ Options& Options::assign<>(Matrix val, std::string 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 @@ -814,6 +824,28 @@ Matrix Options::as>(const Matrix& similar_t return result; } +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 + value_used = true; + + printNameValueSourceLine(*this, "Matrix"); + + return result; +} + template <> Tensor Options::as>(const Tensor& similar_to) const { if (is_section) { @@ -836,6 +868,28 @@ Tensor Options::as>(const Tensor& similar_t return result; } +template <> +Tensor Options::as>(const Tensor& 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 Tensor"), + full_name), + similar_to}, + value); + + // Mark this option as used + value_used = true; + + printNameValueSourceLine(*this, "Tensor"); + + return result; +} + // Note: This is defined here rather than in the header // to avoid using as before specialising it. bool Options::operator==(const char* other) const { @@ -977,12 +1031,15 @@ 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)}; } From 979cce8dce3c46f580f6ca11b929034579b120dd Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Fri, 30 May 2025 18:55:28 +0000 Subject: [PATCH 06/15] Apply black changes --- tests/integrated/test_suite | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/integrated/test_suite b/tests/integrated/test_suite index 307a8d84b3..77ad7882c4 100755 --- a/tests/integrated/test_suite +++ b/tests/integrated/test_suite @@ -188,7 +188,7 @@ class Test(threading.Thread): self.output += "\n(It is likely that a timeout occured)" else: # ❌ Failed - print("\u274C", end="") # No newline + print("\u274c", end="") # No newline print(" %7.3f s" % (time.time() - self.local.start_time), flush=True) def _cost(self): From 21e964c8cf146a8ab67a1d224f543a279bfc5c56 Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 4 Jun 2025 11:59:28 -0700 Subject: [PATCH 07/15] ADIOS2: Add Array/Matrix/Tensor types Without these the build fails at link time when building with ADIOS2. --- src/sys/options/options_adios.cxx | 40 +++++++++++++++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/sys/options/options_adios.cxx b/src/sys/options/options_adios.cxx index 09797647ed..06353a1de8 100644 --- a/src/sys/options/options_adios.cxx +++ b/src/sys/options/options_adios.cxx @@ -472,6 +472,18 @@ void ADIOSPutVarVisitor::operator()(const FieldPerp& value) { stream.engine.Put(var, &value(0, 0)); } +template <> +void ADIOSPutVarVisitor::operator()>(const Array& value) { + // Pointer to data. Assumed to be contiguous array + adios2::Dims shape = {(size_t)BoutComm::size(), (size_t)value.size()}; + adios2::Dims start = {(size_t)BoutComm::rank(), 0}; + adios2::Dims count = {1, shape[1]}; + adios2::Variable var = + stream.GetArrayVariable(varname, shape, DIMS_NONE, BoutComm::rank()); + var.SetSelection({start, count}); + stream.engine.Put(var, value.begin()); +} + template <> void ADIOSPutVarVisitor::operator()>(const Array& value) { // Pointer to data. Assumed to be contiguous array @@ -484,6 +496,20 @@ void ADIOSPutVarVisitor::operator()>(const Array& valu stream.engine.Put(var, value.begin()); } +template <> +void ADIOSPutVarVisitor::operator()>(const Matrix& value) { + // Pointer to data. Assumed to be contiguous array + auto s = value.shape(); + adios2::Dims shape = {(size_t)BoutComm::size(), (size_t)std::get<0>(s), + (size_t)std::get<1>(s)}; + adios2::Dims start = {(size_t)BoutComm::rank(), 0, 0}; + adios2::Dims count = {1, shape[1], shape[2]}; + adios2::Variable var = + stream.GetArrayVariable(varname, shape, DIMS_NONE, BoutComm::rank()); + var.SetSelection({start, count}); + stream.engine.Put(var, value.begin()); +} + template <> void ADIOSPutVarVisitor::operator()>(const Matrix& value) { // Pointer to data. Assumed to be contiguous array @@ -498,6 +524,20 @@ void ADIOSPutVarVisitor::operator()>(const Matrix& va stream.engine.Put(var, value.begin()); } +template <> +void ADIOSPutVarVisitor::operator()>(const Tensor& value) { + // Pointer to data. Assumed to be contiguous array + auto s = value.shape(); + adios2::Dims shape = {(size_t)BoutComm::size(), (size_t)std::get<0>(s), + (size_t)std::get<1>(s), (size_t)std::get<2>(s)}; + adios2::Dims start = {(size_t)BoutComm::rank(), 0, 0, 0}; + adios2::Dims count = {1, shape[1], shape[2], shape[3]}; + adios2::Variable var = + stream.GetArrayVariable(varname, shape, DIMS_NONE, BoutComm::rank()); + var.SetSelection({start, count}); + stream.engine.Put(var, value.begin()); +} + template <> void ADIOSPutVarVisitor::operator()>(const Tensor& value) { // Pointer to data. Assumed to be contiguous array From 020d8798eef05ec8141a033d3d7d5772db41236d Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Wed, 4 Jun 2025 19:01:57 +0000 Subject: [PATCH 08/15] Apply clang-format changes --- include/bout/options.hxx | 7 ++++--- src/sys/options.cxx | 14 +++++++------- 2 files changed, 11 insertions(+), 10 deletions(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index 6ee9beeb6d..273abdbaca 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -201,9 +201,10 @@ public: } /// The type used to store values - using ValueType = bout::utils::variant, Array, - Matrix, Matrix, Tensor, Tensor>; + using ValueType = + bout::utils::variant, Array, Matrix, Matrix, + Tensor, Tensor>; /// A tree representation with leaves containing ValueType. /// Used to construct Options from initializer lists. diff --git a/src/sys/options.cxx b/src/sys/options.cxx index 951a46aca4..387a851f3b 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -832,9 +832,8 @@ Matrix Options::as>(const Matrix& similar_to) const { auto result = bout::utils::visit( ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Matrix"), - full_name), + fmt::format(_("Value for option {:s} cannot be converted to an Matrix"), + full_name), similar_to}, value); @@ -876,9 +875,8 @@ Tensor Options::as>(const Tensor& similar_to) const { auto result = bout::utils::visit( ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Tensor"), - full_name), + fmt::format(_("Value for option {:s} cannot be converted to an Tensor"), + full_name), similar_to}, value); @@ -1032,7 +1030,9 @@ struct GetDimensions { std::vector operator()([[maybe_unused]] BoutReal value) { return {1}; } std::vector operator()([[maybe_unused]] const std::string& value) { return {1}; } template - std::vector operator()(const Array& array) { return {array.size()}; } + std::vector operator()(const Array& array) { + return {array.size()}; + } template std::vector operator()(const Matrix& array) { const auto shape = array.shape(); From a32390e282be43117b4add8313e08533833b298c Mon Sep 17 00:00:00 2001 From: Ben Dudson Date: Wed, 4 Jun 2025 14:45:05 -0700 Subject: [PATCH 09/15] Apply suggestions from code review Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com> --- src/mesh/impls/bout/boutmesh.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/mesh/impls/bout/boutmesh.cxx b/src/mesh/impls/bout/boutmesh.cxx index 3ab30223f4..fe928f377d 100644 --- a/src/mesh/impls/bout/boutmesh.cxx +++ b/src/mesh/impls/bout/boutmesh.cxx @@ -2161,9 +2161,9 @@ void BoutMesh::topology() { } for (int i = 0; i < limiter_count; ++i) { - int yind = limiter_yinds[i]; - int xstart = limiter_xstarts[i]; - int xend = limiter_xends[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); From bfedcc34369c5cad2590cd7b34abda80c67bee63 Mon Sep 17 00:00:00 2001 From: bendudson <219233+bendudson@users.noreply.github.com> Date: Wed, 4 Jun 2025 21:51:44 +0000 Subject: [PATCH 10/15] Apply clang-format changes --- src/solver/impls/arkode/arkode.cxx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/solver/impls/arkode/arkode.cxx b/src/solver/impls/arkode/arkode.cxx index 23883cc043..514ff8671e 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"); } From cdaa62623ac60676e4fefefc94bbf67d3b5e8ea6 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 18 Nov 2025 13:39:46 +0000 Subject: [PATCH 11/15] Add helper function for `Options::as` for `Array/Matrix/Tensor` --- include/bout/sys/type_name.hxx | 19 +++++ src/sys/options.cxx | 139 +++++++++------------------------ src/sys/type_name.cxx | 26 ++++++ 3 files changed, 80 insertions(+), 104 deletions(-) diff --git a/include/bout/sys/type_name.hxx b/include/bout/sys/type_name.hxx index be4d096b4d..ff2ccfc8fd 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/src/sys/options.cxx b/src/sys/options.cxx index 387a851f3b..077b2a6d85 100644 --- a/src/sys/options.cxx +++ b/src/sys/options.cxx @@ -754,138 +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 <> -Array Options::as>(const Array& similar_to) const { - if (is_section) { - throw BoutException(_("Option {:s} has no value"), full_name); - } - - Array result = bout::utils::visit( - ConvertContainer>{ - fmt::format(_("Value for option {:s} cannot be converted to an Array"), - full_name), - similar_to}, - value); - - // Mark this option as used +auto Options::as(const Array& similar_to) const -> Array { value_used = true; - - output_info << _("\tOption ") << full_name << " = Array"; - if (hasAttribute("source")) { - // Specify the source of the setting - output_info << " (" << bout::utils::variantToString(attributes.at("source")) << ")"; - } - output_info << endl; - - return result; + return as_amt(*this, similar_to); } 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 <> -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 Matrix& similar_to) const -> Matrix { 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 result = bout::utils::visit( - ConvertContainer>{ - fmt::format( - _("Value for option {:s} cannot be converted to an Tensor"), - full_name), - similar_to}, - value); - - // Mark this option as used +auto Options::as(const Matrix& similar_to) const -> Matrix { value_used = true; - - printNameValueSourceLine(*this, "Tensor"); - - 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 result = bout::utils::visit( - ConvertContainer>{ - fmt::format(_("Value for option {:s} cannot be converted to an Tensor"), - full_name), - similar_to}, - value); - - // Mark this option as used +auto Options::as(const Tensor& similar_to) const -> Tensor { value_used = true; + return as_amt(*this, similar_to); +} - printNameValueSourceLine(*this, "Tensor"); - - 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 diff --git a/src/sys/type_name.cxx b/src/sys/type_name.cxx index c1082c499a..68272bd473 100644 --- a/src/sys/type_name.cxx +++ b/src/sys/type_name.cxx @@ -41,5 +41,31 @@ template <> std::string typeName() { return "FieldPerp"; } + +template <> +std::string typeName>() { + return "Array"; +} +template <> +std::string typeName>() { + return "Array"; +} +template <> +std::string typeName>() { + return "Matrix"; +} +template <> +std::string typeName>() { + return "Matrix"; +} +template <> +std::string typeName>() { + return "Tensor"; +} +template <> +std::string typeName>() { + return "Tensor"; +} + } // namespace utils } // namespace bout From da418b17fa00464cf21c8635f86412eaa14b347e Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 18 Nov 2025 13:40:30 +0000 Subject: [PATCH 12/15] Remove unnecessary template argument from specialisations --- include/bout/options.hxx | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/include/bout/options.hxx b/include/bout/options.hxx index f53faeea7c..7bad26d990 100644 --- a/include/bout/options.hxx +++ b/include/bout/options.hxx @@ -982,31 +982,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 <> -int Options::as(const int& similar_to) const; +auto Options::as(const int& similar_to) const -> int; template <> -BoutReal Options::as(const BoutReal& similar_to) const; +auto Options::as(const BoutReal& similar_to) const -> BoutReal; template <> -bool Options::as(const bool& similar_to) const; +auto Options::as(const bool& similar_to) const -> bool; template <> -Field2D Options::as(const Field2D& similar_to) const; +auto Options::as(const Field2D& similar_to) const -> Field2D; template <> -Field3D Options::as(const Field3D& similar_to) const; +auto Options::as(const Field3D& similar_to) const -> Field3D; template <> -FieldPerp Options::as(const FieldPerp& similar_to) const; +auto Options::as(const FieldPerp& similar_to) const -> FieldPerp; template <> -Array Options::as>(const Array& similar_to) const; +auto Options::as(const Array& similar_to) const -> Array; template <> -Array Options::as>(const Array& similar_to) const; +auto Options::as(const Array& similar_to) const -> Array; template <> -Matrix Options::as>(const Matrix& 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 Matrix& similar_to) const -> Matrix; template <> -Tensor Options::as>(const Tensor& 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); From 1adfb2f6061837c2c03792932b668e69613299e9 Mon Sep 17 00:00:00 2001 From: Peter Hill Date: Tue, 18 Nov 2025 15:28:13 +0000 Subject: [PATCH 13/15] Reduce duplication in `ADIOSPutVarVisitor` for `Array/Matrix/Tensor` --- include/bout/adios_object.hxx | 6 +- include/bout/array.hxx | 4 + src/sys/options/options_adios.cxx | 133 ++++++++++++------------------ 3 files changed, 59 insertions(+), 84 deletions(-) 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/src/sys/options/options_adios.cxx b/src/sys/options/options_adios.cxx index 06353a1de8..9be064ec19 100644 --- a/src/sys/options/options_adios.cxx +++ b/src/sys/options/options_adios.cxx @@ -12,8 +12,11 @@ #include "bout/sys/timer.hxx" #include "adios2.h" + +#include #include -#include +#include +#include #include namespace bout { @@ -313,6 +316,38 @@ const std::vector DIMS_XY = {"x", "y"}; const std::vector DIMS_XZ = {"x", "z"}; const std::vector DIMS_XYZ = {"x", "y", "z"}; +namespace { +template +using tuple_index_sequence = std::make_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