From d911d51c457e745af4839134c4aa3e904205c16b Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 16:43:44 -0700 Subject: [PATCH 01/10] Extend ADIOSStream to read modes --- include/bout/adios_object.hxx | 25 ++++++++++--------------- src/sys/adios_object.cxx | 21 +++++++++++++++++++-- src/sys/options/options_adios.cxx | 23 ++++++++--------------- 3 files changed, 37 insertions(+), 32 deletions(-) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 63a2d4f85f..901857e7d4 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -21,6 +21,7 @@ #include #include #include +#include namespace bout { @@ -43,7 +44,8 @@ public: int adiosStep = 0; /** create or return the ADIOSStream based on the target file name */ - static ADIOSStream& ADIOSGetStream(const std::string& fname, adios2::Mode mode); + static ADIOSStream& ADIOSGetStream(const std::string& fname, adios2::Mode mode, + const std::string& engineType = "BP5"); ~ADIOSStream(); @@ -78,7 +80,7 @@ public: if (not engine_) { engine_ = io.Open(fname, file_mode); if (not engine_) { - throw BoutException("Could not open ADIOS file '{:s}' for writing", fname); + throw BoutException("Could not open ADIOS file '{:s}'", fname); } } return engine_; @@ -101,24 +103,17 @@ public: void finish() { if (engine_) { - engine().EndStep(); + if (isInStep) { + engine().EndStep(); + isInStep = false; + } engine().Close(); + engine_ = adios2::Engine(); } } private: - ADIOSStream(const std::string& fname, adios2::Mode mode) - : fname(fname), file_mode(mode) { - - ADIOSPtr adiosp = GetADIOSPtr(); - std::string ioname = "write_" + fname; - try { - io = adiosp->AtIO(ioname); - } catch (const std::invalid_argument& e) { - io = adiosp->DeclareIO(ioname); - io.SetEngine("BP5"); - } - }; + ADIOSStream(const std::string& fname, adios2::Mode mode, const std::string& engineType); std::string fname; adios2::Mode file_mode; diff --git a/src/sys/adios_object.cxx b/src/sys/adios_object.cxx index 1b3e0a12e2..d6aa862493 100644 --- a/src/sys/adios_object.cxx +++ b/src/sys/adios_object.cxx @@ -7,6 +7,7 @@ #include +#include #include namespace bout { @@ -57,10 +58,26 @@ ADIOSStream::~ADIOSStream() { } } -ADIOSStream& ADIOSStream::ADIOSGetStream(const std::string& fname, adios2::Mode mode) { +ADIOSStream::ADIOSStream(const std::string& fname, adios2::Mode mode, + const std::string& engineType) + : fname(fname), file_mode(mode) { + + ADIOSPtr adiosp = GetADIOSPtr(); + try { + io = adiosp->AtIO(fname); + } catch (const std::invalid_argument& e) { + io = adiosp->DeclareIO(fname); + if (!engineType.empty()) { + io.SetEngine(engineType); + } + } +} + +ADIOSStream& ADIOSStream::ADIOSGetStream(const std::string& fname, adios2::Mode mode, + const std::string& engineType) { auto it = adiosStreams.find(fname); if (it == adiosStreams.end()) { - it = adiosStreams.emplace(fname, ADIOSStream(fname, mode)).first; + it = adiosStreams.emplace(fname, ADIOSStream(fname, mode, engineType)).first; } return it->second; } diff --git a/src/sys/options/options_adios.cxx b/src/sys/options/options_adios.cxx index 844284a400..6e0227d7b9 100644 --- a/src/sys/options/options_adios.cxx +++ b/src/sys/options/options_adios.cxx @@ -356,16 +356,9 @@ Options OptionsADIOS::read([[maybe_unused]] bool lazy) { const Timer timer("io"); // Open file - const ADIOSPtr adiosp = GetADIOSPtr(); - adios2::IO io; - const std::string ioname = "read_" + filename; - try { - io = adiosp->AtIO(ioname); - } catch (const std::invalid_argument& e) { - io = adiosp->DeclareIO(ioname); - } - - adios2::Engine reader = io.Open(filename, adios2::Mode::ReadRandomAccess); + ADIOSStream& stream = + ADIOSStream::ADIOSGetStream(filename, adios2::Mode::ReadRandomAccess); + adios2::Engine& reader = stream.engine(); if (!reader) { throw BoutException("Could not open ADIOS file '{:s}' for reading\n", filename); } @@ -373,7 +366,7 @@ Options OptionsADIOS::read([[maybe_unused]] bool lazy) { Options result; // Iterate over all variables - for (const auto& varpair : io.AvailableVariables()) { + for (const auto& varpair : stream.io.AvailableVariables()) { const auto& var_name = varpair.first; // Name of the variable auto it = varpair.second.find("Type"); @@ -388,21 +381,21 @@ Options OptionsADIOS::read([[maybe_unused]] bool lazy) { // Note: Copying the value rather than simple assignment is used // because the Options assignment operator overwrites full_name. var = 0; // Setting is_section to false - var.value = readVariable(reader, io, var_name, var_type).value; + var.value = readVariable(reader, stream.io, var_name, var_type).value; var.attributes["source"] = filename; // Get variable attributes - for (const auto& attpair : io.AvailableAttributes(var_name, "/", true)) { + for (const auto& attpair : stream.io.AvailableAttributes(var_name, "/", true)) { const auto& att_name = attpair.first; // Attribute name const auto& att = attpair.second; // attribute params auto it = att.find("Type"); const std::string& att_type = it->second; - readAttribute(io, att_name, att_type, var); + readAttribute(stream.io, att_name, att_type, var); } } - reader.Close(); + stream.finish(); return result; } From 86e6ba88dbc08a94f59edc949a36def650ca1232 Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 17:27:11 -0700 Subject: [PATCH 02/10] Add simple Get/Put --- include/bout/adios_object.hxx | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 901857e7d4..a52703c08a 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -16,6 +16,8 @@ #if BOUT_HAS_ADIOS2 +#include "bout/assert.hxx" +#include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" #include @@ -58,6 +60,21 @@ public: return v; } + template + void Get(const std::string& varname, T& value, adios2::Mode mode = adios2::Mode::Sync) { + auto variable = GetValueVariable(varname); + ASSERT1(variable.ShapeID() == adios2::ShapeID::GlobalValue); + engine().Get(variable, &value, mode); + } + + template + void Put(const std::string& varname, T value) { + if (BoutComm::rank() != 0) { + return; + } + engine().Put(GetValueVariable(varname), value); + } + template adios2::Variable GetArrayVariable(const std::string& varname, const adios2::Dims& shape, From dcf3091d8d86ff5cbe3b461f67b3eac5f00f41d6 Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 17:52:12 -0700 Subject: [PATCH 03/10] Add get for Array/Matrix/Tensor --- include/bout/adios_object.hxx | 67 ++++++++++++++++++++++++++++++++++- 1 file changed, 66 insertions(+), 1 deletion(-) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index a52703c08a..80d055aa67 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -19,11 +19,14 @@ #include "bout/assert.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/utils.hxx" #include #include #include #include +#include +#include namespace bout { @@ -62,11 +65,30 @@ public: template void Get(const std::string& varname, T& value, adios2::Mode mode = adios2::Mode::Sync) { - auto variable = GetValueVariable(varname); + auto variable = io.InquireVariable(varname); + ASSERT1(variable); ASSERT1(variable.ShapeID() == adios2::ShapeID::GlobalValue); engine().Get(variable, &value, mode); } + template + void Get(const std::string& varname, Array& value, + adios2::Mode mode = adios2::Mode::Sync) { + GetArrayLike(varname, value, mode); + } + + template + void Get(const std::string& varname, Matrix& value, + adios2::Mode mode = adios2::Mode::Sync) { + GetArrayLike(varname, value, mode); + } + + template + void Get(const std::string& varname, Tensor& value, + adios2::Mode mode = adios2::Mode::Sync) { + GetArrayLike(varname, value, mode); + } + template void Put(const std::string& varname, T value) { if (BoutComm::rank() != 0) { @@ -132,6 +154,49 @@ public: private: ADIOSStream(const std::string& fname, adios2::Mode mode, const std::string& engineType); + template + void GetArrayLike(const std::string& varname, Container& value, adios2::Mode mode) { + using T = typename Container::data_type; + auto variable = io.InquireVariable(varname); + ASSERT1(variable); + ASSERT1(variable.ShapeID() == adios2::ShapeID::GlobalArray); + + const auto shape = variable.Shape(); + auto dims_attr = io.InquireAttribute(varname + "/__xarray_dimensions__"); + std::size_t offset = 0; + + if (dims_attr) { + const auto dim_names = dims_attr.Data(); + if (!dim_names.empty() && dim_names[0] == "rank") { + ASSERT1(!shape.empty()); + ASSERT1(static_cast(BoutComm::rank()) < shape[0]); + + adios2::Dims start{static_cast(BoutComm::rank())}; + adios2::Dims count{1}; + for (std::size_t i = 1; i < shape.size(); i++) { + start.push_back(0); + count.push_back(shape[i]); + } + + variable.SetSelection(adios2::Box{start, count}); + variable.SetMemorySelection(adios2::Box{start, count}); + offset = 1; + } + } + + constexpr auto ndims = std::tuple_size_v; + ASSERT1(shape.size() == ndims + offset); + + resizeForShape(value, shape, offset, std::make_index_sequence{}); + engine().Get(variable, value.begin(), mode); + } + + template + void resizeForShape(Container& value, const adios2::Dims& shape, std::size_t offset, + std::index_sequence /*indices*/) { + value.reallocate(static_cast(shape[offset + I])...); + } + std::string fname; adios2::Mode file_mode; adios2::Engine engine_; From 50465fefedb3662653148a05ec7ceb717d997493 Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 18:11:52 -0700 Subject: [PATCH 04/10] Add put for Array/Matrix/Tensor --- include/bout/adios_object.hxx | 53 +++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 80d055aa67..4d3efcf620 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -27,6 +27,7 @@ #include #include #include +#include namespace bout { @@ -97,6 +98,21 @@ public: engine().Put(GetValueVariable(varname), value); } + template + void Put(const std::string& varname, const Array& value) { + PutArrayLike(varname, value); + } + + template + void Put(const std::string& varname, const Matrix& value) { + PutArrayLike(varname, value); + } + + template + void Put(const std::string& varname, const Tensor& value) { + PutArrayLike(varname, value); + } + template adios2::Variable GetArrayVariable(const std::string& varname, const adios2::Dims& shape, @@ -191,6 +207,43 @@ private: engine().Get(variable, value.begin(), mode); } + template + void PutArrayLike(const std::string& varname, const Container& value) { + using T = typename Container::data_type; + auto var = GetArrayVariable(varname, makeShape(BoutComm::size(), value), + makeDimNames(value), BoutComm::rank()); + var.SetSelection(adios2::Box{makeStart(value), makeShape(1, value)}); + engine().Put(var, value.begin()); + } + + template + adios2::Dims makeShape(std::size_t first, const Container& value) const { + return std::apply( + [first](auto... sizes) { + return adios2::Dims{first, static_cast(sizes)...}; + }, + value.shape()); + } + + template + adios2::Dims makeStart(const Container& value) const { + constexpr auto ndims = std::tuple_size_v; + adios2::Dims start(ndims + 1, 0); + start[0] = static_cast(BoutComm::rank()); + return start; + } + + template + std::vector makeDimNames(const Container& value) const { + constexpr auto ndims = std::tuple_size_v; + std::vector dim_names{"rank"}; + dim_names.reserve(ndims + 1); + for (std::size_t i = 0; i < ndims; i++) { + dim_names.push_back("dim_" + std::to_string(i)); + } + return dim_names; + } + template void resizeForShape(Container& value, const adios2::Dims& shape, std::size_t offset, std::index_sequence /*indices*/) { From d2ad823e1bef7729953da3610c59b1481354611b Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 18:16:25 -0700 Subject: [PATCH 05/10] Add get for Fields --- include/bout/adios_object.hxx | 85 +++++++++++++++++++++++++++++++++++ 1 file changed, 85 insertions(+) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 4d3efcf620..0330e2e97e 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -19,6 +19,10 @@ #include "bout/assert.hxx" #include "bout/boutcomm.hxx" #include "bout/boutexception.hxx" +#include "bout/field2d.hxx" +#include "bout/field3d.hxx" +#include "bout/fieldperp.hxx" +#include "bout/mesh.hxx" #include "bout/utils.hxx" #include @@ -90,6 +94,24 @@ public: GetArrayLike(varname, value, mode); } + void Get(const std::string& varname, Field2D& value, + adios2::Mode mode = adios2::Mode::Sync) { + value.allocate(); + GetField(varname, {"x", "y"}, *value.getMesh(), &value(0, 0), mode); + } + + void Get(const std::string& varname, Field3D& value, + adios2::Mode mode = adios2::Mode::Sync) { + value.allocate(); + GetField(varname, {"x", "y", "z"}, *value.getMesh(), &value(0, 0, 0), mode); + } + + void Get(const std::string& varname, FieldPerp& value, + adios2::Mode mode = adios2::Mode::Sync) { + value.allocate(); + GetField(varname, {"x", "z"}, *value.getMesh(), &value(0, 0), mode); + } + template void Put(const std::string& varname, T value) { if (BoutComm::rank() != 0) { @@ -170,6 +192,69 @@ public: private: ADIOSStream(const std::string& fname, adios2::Mode mode, const std::string& engineType); + struct FieldSelection { + adios2::Dims start; + adios2::Dims count; + adios2::Dims mem_start; + adios2::Dims mem_count; + + auto selection() const { return adios2::Box{start, count}; } + auto memorySelection() const { + return adios2::Box{mem_start, mem_count}; + } + }; + + void GetField(const std::string& varname, const std::vector& dim_names, + const Mesh& mesh, BoutReal* data, adios2::Mode mode) { + auto variable = io.InquireVariable(varname); + ASSERT1(variable); + ASSERT1(variable.ShapeID() == adios2::ShapeID::GlobalArray); + + auto selection = makeFieldSelection(dim_names, mesh); + variable.SetSelection(selection.selection()); + variable.SetMemorySelection(selection.memorySelection()); + engine().Get(variable, data, mode); + } + + FieldSelection makeFieldSelection(const std::vector& dim_names, + const Mesh& mesh) const { + ASSERT1(!dim_names.empty()); + ASSERT1(dim_names.size() <= 3); + ASSERT1(dim_names[0] == "x"); + + FieldSelection selection; + selection.start.push_back(static_cast(mesh.MapGlobalX)); + selection.count.push_back(static_cast(mesh.MapCountX)); + selection.mem_start.push_back(static_cast(mesh.MapLocalX)); + selection.mem_count.push_back(static_cast(mesh.LocalNx)); + + if (dim_names.size() > 1) { + if (dim_names[1] == "y") { + selection.start.push_back(static_cast(mesh.MapGlobalY)); + selection.count.push_back(static_cast(mesh.MapCountY)); + selection.mem_start.push_back(static_cast(mesh.MapLocalY)); + selection.mem_count.push_back(static_cast(mesh.LocalNy)); + } else if (dim_names[1] == "z") { + selection.start.push_back(static_cast(mesh.MapGlobalZ)); + selection.count.push_back(static_cast(mesh.MapCountZ)); + selection.mem_start.push_back(static_cast(mesh.MapLocalZ)); + selection.mem_count.push_back(static_cast(mesh.LocalNz)); + } else { + ASSERT1(false); + } + } + + if (dim_names.size() > 2) { + ASSERT1(dim_names[2] == "z"); + selection.start.push_back(static_cast(mesh.MapGlobalZ)); + selection.count.push_back(static_cast(mesh.MapCountZ)); + selection.mem_start.push_back(static_cast(mesh.MapLocalZ)); + selection.mem_count.push_back(static_cast(mesh.LocalNz)); + } + + return selection; + } + template void GetArrayLike(const std::string& varname, Container& value, adios2::Mode mode) { using T = typename Container::data_type; From 357ec23a67cbdb0832d9fa37c647e21f37102ac5 Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 18:18:18 -0700 Subject: [PATCH 06/10] Add put for Fields --- include/bout/adios_object.hxx | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 0330e2e97e..25924346d6 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -135,6 +135,18 @@ public: PutArrayLike(varname, value); } + void Put(const std::string& varname, const Field2D& value) { + PutField(varname, {"x", "y"}, *value.getMesh(), &value(0, 0)); + } + + void Put(const std::string& varname, const Field3D& value) { + PutField(varname, {"x", "y", "z"}, *value.getMesh(), &value(0, 0, 0)); + } + + void Put(const std::string& varname, const FieldPerp& value) { + PutField(varname, {"x", "z"}, *value.getMesh(), &value(0, 0)); + } + template adios2::Variable GetArrayVariable(const std::string& varname, const adios2::Dims& shape, @@ -193,6 +205,7 @@ private: ADIOSStream(const std::string& fname, adios2::Mode mode, const std::string& engineType); struct FieldSelection { + adios2::Dims shape; adios2::Dims start; adios2::Dims count; adios2::Dims mem_start; @@ -216,6 +229,16 @@ private: engine().Get(variable, data, mode); } + void PutField(const std::string& varname, const std::vector& dim_names, + const Mesh& mesh, const BoutReal* data) { + auto selection = makeFieldSelection(dim_names, mesh); + auto variable = + GetArrayVariable(varname, selection.shape, dim_names, BoutComm::rank()); + variable.SetSelection(selection.selection()); + variable.SetMemorySelection(selection.memorySelection()); + engine().Put(variable, data); + } + FieldSelection makeFieldSelection(const std::vector& dim_names, const Mesh& mesh) const { ASSERT1(!dim_names.empty()); @@ -223,6 +246,7 @@ private: ASSERT1(dim_names[0] == "x"); FieldSelection selection; + selection.shape.push_back(static_cast(mesh.GlobalNx)); selection.start.push_back(static_cast(mesh.MapGlobalX)); selection.count.push_back(static_cast(mesh.MapCountX)); selection.mem_start.push_back(static_cast(mesh.MapLocalX)); @@ -230,11 +254,13 @@ private: if (dim_names.size() > 1) { if (dim_names[1] == "y") { + selection.shape.push_back(static_cast(mesh.GlobalNy)); selection.start.push_back(static_cast(mesh.MapGlobalY)); selection.count.push_back(static_cast(mesh.MapCountY)); selection.mem_start.push_back(static_cast(mesh.MapLocalY)); selection.mem_count.push_back(static_cast(mesh.LocalNy)); } else if (dim_names[1] == "z") { + selection.shape.push_back(static_cast(mesh.GlobalNz)); selection.start.push_back(static_cast(mesh.MapGlobalZ)); selection.count.push_back(static_cast(mesh.MapCountZ)); selection.mem_start.push_back(static_cast(mesh.MapLocalZ)); @@ -245,7 +271,9 @@ private: } if (dim_names.size() > 2) { + ASSERT1(dim_names[1] == "y"); ASSERT1(dim_names[2] == "z"); + selection.shape.push_back(static_cast(mesh.GlobalNz)); selection.start.push_back(static_cast(mesh.MapGlobalZ)); selection.count.push_back(static_cast(mesh.MapCountZ)); selection.mem_start.push_back(static_cast(mesh.MapLocalZ)); From 84d0788f9842048d9cd68adbe1aa52de685074ae Mon Sep 17 00:00:00 2001 From: Steven Roberts Date: Thu, 30 Jul 2026 18:45:42 -0700 Subject: [PATCH 07/10] Use new adios_object functionality in options_adios --- include/bout/adios_object.hxx | 15 +- src/sys/options/options_adios.cxx | 349 +++++------------------------- 2 files changed, 64 insertions(+), 300 deletions(-) diff --git a/include/bout/adios_object.hxx b/include/bout/adios_object.hxx index 25924346d6..62a05e00f2 100755 --- a/include/bout/adios_object.hxx +++ b/include/bout/adios_object.hxx @@ -22,6 +22,7 @@ #include "bout/field2d.hxx" #include "bout/field3d.hxx" #include "bout/fieldperp.hxx" +#include "bout/globals.hxx" #include "bout/mesh.hxx" #include "bout/utils.hxx" @@ -292,7 +293,7 @@ private: const auto shape = variable.Shape(); auto dims_attr = io.InquireAttribute(varname + "/__xarray_dimensions__"); - std::size_t offset = 0; + auto read_shape = shape; if (dims_attr) { const auto dim_names = dims_attr.Data(); @@ -309,14 +310,20 @@ private: variable.SetSelection(adios2::Box{start, count}); variable.SetMemorySelection(adios2::Box{start, count}); - offset = 1; + read_shape = adios2::Dims(shape.begin() + 1, shape.end()); + } else if (!dim_names.empty() && dim_names[0] == "x") { + ASSERT1(globals::mesh); + auto selection = makeFieldSelection(dim_names, *globals::mesh); + variable.SetSelection(selection.selection()); + variable.SetMemorySelection(selection.memorySelection()); + read_shape = selection.mem_count; } } constexpr auto ndims = std::tuple_size_v; - ASSERT1(shape.size() == ndims + offset); + ASSERT1(read_shape.size() == ndims); - resizeForShape(value, shape, offset, std::make_index_sequence{}); + resizeForShape(value, read_shape, 0, std::make_index_sequence{}); engine().Get(variable, value.begin(), mode); } diff --git a/src/sys/options/options_adios.cxx b/src/sys/options/options_adios.cxx index 6e0227d7b9..4abf55bcd5 100644 --- a/src/sys/options/options_adios.cxx +++ b/src/sys/options/options_adios.cxx @@ -10,157 +10,41 @@ #include "bout/bout_types.hxx" #include "bout/boutexception.hxx" #include "bout/field2d.hxx" -#include "bout/globals.hxx" -#include "bout/mesh.hxx" #include "bout/options.hxx" #include "bout/options_io.hxx" #include "bout/output.hxx" #include "bout/sys/timer.hxx" #include "bout/sys/variant.hxx" -#include "bout/traits.hxx" #include "bout/utils.hxx" #include // IWYU pragma: keep #include #include -#include #include #include #include -#include #include #include -#include -#include namespace { -auto to_int_dims(const adios2::Dims& dims) { - std::vector int_dims; - int_dims.reserve(dims.size()); - std::transform(dims.begin(), dims.end(), std::back_inserter(int_dims), - [](auto dim) { return static_cast(dim); }); - return int_dims; -} - -// Helper class to construct Adios hyperslices -struct Selection { - // Offset of this processor's data into the global array - adios2::Dims start; - // The size of the mapped region - adios2::Dims count; - // Where the actual data starts in data pointer (to exclude ghost cells) - adios2::Dims mem_start; - // The actual size of data pointer in memory (including ghost cells) - adios2::Dims mem_count; - // Global shape, including boundaries but not guard cells - adios2::Dims shape; - // Shape of the local variable to read into - std::vector dims; - - // Distributed Field/Array/Matrix/Tensor - bool should_set_selection{false}; - - Selection(const std::vector& dim_names, const std::vector& dim_sizes, - const Mesh& mesh) { - const auto ndims = dim_names.size(); - const bool dim0_is_rank = ndims > 0 ? (dim_names[0] == "rank") : false; - const bool dim0_is_x = ndims > 0 ? (dim_names[0] == "x") : false; - const bool dim1_is_y = ndims > 1 ? (dim_names[1] == "y") : false; - const bool dim1_is_z = ndims > 1 ? (dim_names[1] == "z") : false; - const bool dim2_is_z = ndims > 2 ? (dim_names[2] == "z") : false; - - should_set_selection = dim0_is_rank or (ndims == 2 and dim0_is_x and dim1_is_y) - or (ndims == 2 and dim0_is_x and dim1_is_z) - or (ndims == 3 and dim0_is_x and dim1_is_y and dim2_is_z); - - if (dim0_is_rank) { - const auto ndim_sizes = dim_sizes.size(); - ASSERT3(ndim_sizes > 1); - - // This is a distributed array, so the local variable is going - // to be shape (dim_sizes[1]...) (that is, drop the rank) - dims.push_back(dim_sizes[1]); - // but we tell adios to read our rank's bit with the full ndims - start = {static_cast(BoutComm::rank()), 0}; - count = {std::size_t{1}, static_cast(dim_sizes[0])}; - - if (ndim_sizes > 2) { - dims.push_back(dim_sizes[2]); - start.push_back(0); - count.push_back(dim_sizes[1]); - } - if (ndim_sizes > 3) { - dims.push_back(dim_sizes[3]); - start.push_back(0); - count.push_back(dim_sizes[2]); - } - mem_count = count; - mem_start = start; - return; - } - - if (ndims > 0) { - dims.push_back(dim0_is_x ? mesh.LocalNx : dim_sizes[0]); - } - if (ndims > 1) { - if (dim1_is_y) { - dims.push_back(mesh.LocalNy); - } else if (dim1_is_z) { - dims.push_back(mesh.LocalNz); - } else { - dims.push_back(dim_sizes[1]); - } - } - if (ndims > 2) { - dims.push_back(dim2_is_z ? mesh.LocalNz : dim_sizes[2]); - } - - shape.push_back(static_cast(mesh.GlobalNx)); - start.push_back(static_cast(mesh.MapGlobalX)); - count.push_back(static_cast(mesh.MapCountX)); - mem_start.push_back(static_cast(mesh.MapLocalX)); - mem_count.push_back(static_cast(mesh.LocalNx)); - - if (dim1_is_y) { - shape.push_back(static_cast(mesh.GlobalNy)); - start.push_back(static_cast(mesh.MapGlobalY)); - count.push_back(static_cast(mesh.MapCountY)); - mem_start.push_back(static_cast(mesh.MapLocalY)); - mem_count.push_back(static_cast(mesh.LocalNy)); - } else if (dim1_is_z) { - shape.push_back(static_cast(mesh.GlobalNz)); - start.push_back(static_cast(mesh.MapGlobalZ)); - count.push_back(static_cast(mesh.MapCountZ)); - mem_start.push_back(static_cast(mesh.MapLocalZ)); - mem_count.push_back(static_cast(mesh.LocalNz)); - } - - if (dim2_is_z) { - shape.push_back(static_cast(mesh.GlobalNz)); - start.push_back(static_cast(mesh.MapGlobalZ)); - count.push_back(static_cast(mesh.MapCountZ)); - mem_start.push_back(static_cast(mesh.MapLocalZ)); - mem_count.push_back(static_cast(mesh.LocalNz)); +std::size_t getLocalNDims(adios2::IO& io, const std::string& name, + const adios2::Dims& shape) { + auto dims_attr = io.InquireAttribute(name + "/__xarray_dimensions__"); + if (dims_attr) { + const auto dim_names = dims_attr.Data(); + if (!dim_names.empty() && dim_names[0] == "rank") { + ASSERT1(!shape.empty()); + return shape.size() - 1; } } + return shape.size(); +} - auto selection() const { return adios2::Box{start, count}; } - auto memorySelection() const { return adios2::Box{mem_start, mem_count}; } -}; - -template