diff --git a/CHANGELOG.md b/CHANGELOG.md index decd4296056c6..e334a9ca8d3ae 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -29,6 +29,7 @@ ### Changed (changing behavior/API/variables/...) +- [[PR 1449]](https://github.com/parthenon-hpc-lab/parthenon/pull/1449) Require all containers with the same base name in a DataCollection be made from the same field set - [[PR 1438]](https://github.com/parthenon-hpc-lab/parthenon/pull/1438) Performance tuning for the loop abstraction machinery and add loop abstraction OpenMP support - [[PR 1416][(https://github.com/parthenon-hpc-lab/parthenon/pull/1416) Remove virtual tag from destructors in sparse and swarm pack base classes - [[PR 1401]](https://github.com/parthenon-hpc-lab/parthenon/pull/1401) Sparse Field Component Names @@ -68,6 +69,7 @@ ### Incompatibilities (i.e. breaking changes) +- [[PR 1449]](https://github.com/parthenon-hpc-lab/parthenon/pull/1449) Require all containers with the same base name in a DataCollection be made from the same field set - [[PR 1385]](https://github.com/parthenon-hpc-lab/parthenon/pull/1385) ParameterInput internal storage refactor removes direct access to linked list (`pfirst_block`). Use `GetBlocksWithPrefix()` or `GetBlockNames()` instead. - [[PR 1351]](https://github.com/parthenon-hpc-lab/parthenon/pull/1351) Bump Kokkos 5 & C++20 - [[PR 1377]](https://github.com/parthenon-hpc-lab/parthenon/pull/1377) Extend Initialization Hierarchy diff --git a/src/interface/data_collection.hpp b/src/interface/data_collection.hpp index ce3fbc0d1c4d9..6e1154108447f 100644 --- a/src/interface/data_collection.hpp +++ b/src/interface/data_collection.hpp @@ -15,14 +15,17 @@ #include #include +#include #include #include #include #include "basic_types.hpp" #include "globals.hpp" +#include "interface/variable.hpp" #include "utils/concepts_lite.hpp" #include "utils/error_checking.hpp" +#include "utils/unique_id.hpp" namespace parthenon { class Mesh; @@ -53,36 +56,37 @@ class DataCollection { void SetMeshPointer(Mesh *pmesh) { pmy_mesh_ = pmesh; } - template - std::shared_ptr &Add(const std::string &name, const std::shared_ptr &src, - const std::vector &fields, const bool shallow) { - auto key = GetKey(name, src); - auto it = containers_.find(key); - if (it != containers_.end()) { - if (fields.size() && !(it->second)->CreatedFrom(fields)) { - PARTHENON_THROW(key + " already exists in collection but fields do not match."); - } - return it->second; - } - - auto c = std::make_shared(name); - c->Initialize(src, fields, shallow); - - containers_[key] = c; - return containers_[key]; - } - template std::shared_ptr &Add(const std::string &label, const std::shared_ptr &src, const std::vector &fields = {}) { - return Add(label, src, fields, false); + return AddImpl(label, src, fields, false); + } + + template + std::shared_ptr &Add(const std::string &label, const std::shared_ptr &src, + const std::vector &fields, const bool shallow) { + return AddImpl(label, src, fields, shallow); } template std::shared_ptr &AddShallow(const std::string &label, const std::shared_ptr &src, const std::vector &fields = {}) { - return Add(label, src, fields, true); + return AddImpl(label, src, fields, true); + } + + template + std::shared_ptr &AddFromSet(const std::string &label, + const std::shared_ptr &src, + const std::set &fields) { + return AddImpl(label, src, fields, false); + } + + template + std::shared_ptr &AddShallowFromSet(const std::string &label, + const std::shared_ptr &src, + const std::set &fields) { + return AddImpl(label, src, fields, true); } auto &Stages() { return containers_; } @@ -112,6 +116,15 @@ class DataCollection { std::shared_ptr &Get(const std::string &name = "base"); const std::shared_ptr &Get(const std::string &name = "base") const; + // The field list (as a canonical variable-uid set) that the named container was created + // from. Every container sharing a base name is created from the same list (see the + // warning in Add). If the name has never been added, returns a static empty set. + const std::set &GetCreationFields(const std::string &name) const { + static const std::set empty; + const auto nit = name_creation_fields_.find(name); + return nit == name_creation_fields_.end() ? empty : nit->second; + } + void Set(const std::string &name, std::shared_ptr &d) { containers_[name] = d; } // Legacy methods that are specific to MeshData @@ -122,6 +135,54 @@ class DataCollection { void clear() { containers_.clear(); } private: + template + std::shared_ptr &AddImpl(const std::string &name, const std::shared_ptr &src, + const Fields_t &fields, const bool shallow) { + auto key = GetKey(name, src); + auto it = containers_.find(key); + if (it != containers_.end()) { + // Existing container. An explicit field list must match what the container was + // actually created from (checked against the container itself, which also catches + // containers built by hand or through a different DataCollection); an empty list + // means "all fields"/"don't check" and always passes. + if (fields.size() && !(it->second)->CreatedFrom(fields)) + PARTHENON_THROW(key + " already exists in collection but fields do not match."); + return it->second; + } + + using ID_t = typename Fields_t::value_type; + auto to_uid = [](const ID_t &f) -> Uid_t { + if constexpr (std::is_same_v) + return Variable::GetUniqueID(f); + else + return f; + }; + + // Track the field list (as a canonical uid set) each container base name is created + // from, so every container with a given base name contains the same set of fields. + // Containers sharing a base name but built from different sources get distinct + // internal names, so the check above cannot compare them; this does. All instances of + // a name must be created from the same list. + std::set created; + for (const auto &f : fields) + created.insert(to_uid(f)); + auto nit = name_creation_fields_.find(name); + if (nit == name_creation_fields_.end()) { + name_creation_fields_[name] = created; + } else if (nit->second != created) { + PARTHENON_THROW( + "Container \"" + name + + "\" is being created from different field lists on different sources. All " + "instances sharing a name must be created from the same field list."); + } + + std::vector uids(created.begin(), created.end()); + auto c = std::make_shared(name); + c->Initialize(src, uids, shallow); + containers_[key] = c; + return containers_[key]; + } + std::string GetKey(const std::string &stage_label, const std::shared_ptr &in) const; std::string GetKey(const std::string &stage_label, @@ -133,6 +194,7 @@ class DataCollection { Mesh *pmy_mesh_; std::map> containers_; + std::map> name_creation_fields_; }; } // namespace parthenon diff --git a/src/interface/meshblock_data.hpp b/src/interface/meshblock_data.hpp index c23fa87fe5ab9..a03f1a31bb3cd 100644 --- a/src/interface/meshblock_data.hpp +++ b/src/interface/meshblock_data.hpp @@ -559,6 +559,11 @@ class MeshBlockData { std::all_of(vars.begin(), vars.end(), [this](const auto &v) { return this->varUidIn_.count(v); }); } + bool CreatedFrom(const std::set &vars) { + return (vars.size() == varUidIn_.size()) && + std::all_of(vars.begin(), vars.end(), + [this](const auto &v) { return this->varUidIn_.count(v); }); + } bool CreatedFrom(const std::vector &vars) { return (vars.size() == varUidIn_.size()) && std::all_of(vars.begin(), vars.end(), [this](const auto &v) { diff --git a/src/interface/state_descriptor.hpp b/src/interface/state_descriptor.hpp index 2e27307f88b21..0b82b71a4b6bd 100644 --- a/src/interface/state_descriptor.hpp +++ b/src/interface/state_descriptor.hpp @@ -34,6 +34,7 @@ #include "interface/params.hpp" #include "interface/sparse_pool.hpp" #include "interface/var_id.hpp" +#include "interface/variable.hpp" #include "outputs/output_parameters.hpp" #include "pack/scratch_variables.hpp" #include "parameter_input.hpp" @@ -313,9 +314,15 @@ class StateDescriptor { const auto &GetFieldVarID(const std::string &label) const { return labelToVidMap_.at(label); } + const auto &GetFieldVarID(const Uid_t &uid) const { + return labelToVidMap_.at(Variable::GetLabel(uid)); + } const auto &GetFieldMetadata(const std::string &label) const { return metadataMap_.at(labelToVidMap_.at(label)); } + const auto &GetFieldMetadata(const Uid_t &uid) const { + return metadataMap_.at(labelToVidMap_.at(Variable::GetLabel(uid))); + } const auto &GetFieldMetadata(const VarID &id) const { return metadataMap_.at(id); } const auto &AllFields() const noexcept { return metadataMap_; } const auto &AllSparsePools() const noexcept { return sparsePoolMap_; } diff --git a/src/solvers/bicgstab_solver.hpp b/src/solvers/bicgstab_solver.hpp index 0f4d6bae1f962..e7e58632990d0 100644 --- a/src/solvers/bicgstab_solver.hpp +++ b/src/solvers/bicgstab_solver.hpp @@ -146,7 +146,9 @@ class BiCGSTABSolver : public SolverBase, BiCGSTABSolverCounter { return preconditioner.AddSetupTasks(tl, dependence, partition, pmesh); } else if (params_.precondition_type == Preconditioner::Diagonal) { auto partitions = pmesh->GetDefaultBlockPartitions(); - auto &md = pmesh->mesh_data.Add(container_base, partitions[partition]); + auto &md = + pmesh->mesh_data.AddFromSet(container_base, partitions[partition], + pmesh->mesh_data.GetCreationFields(container_base)); auto &md_diag = pmesh->mesh_data.Add(container_diag, md, sol_fields); return tl.AddTask(dependence, &equations_t::SetDiagonal, &eqs_, md, md_diag); } else { @@ -161,7 +163,9 @@ class BiCGSTABSolver : public SolverBase, BiCGSTABSolverCounter { auto partitions = pmesh->GetDefaultBlockPartitions(); // Should contain all fields necessary for applying the matrix to a give state vector, // e.g. diffusion coefficients and diagonal, these will not be modified by the solvers - auto &md_base = pmesh->mesh_data.Add(container_base, partitions[partition]); + auto &md_base = + pmesh->mesh_data.AddFromSet(container_base, partitions[partition], + pmesh->mesh_data.GetCreationFields(container_base)); // Container in which the solution is stored and with which the downstream user can // interact. This container only requires the fields in sol_fields auto &md_u = pmesh->mesh_data.Add(container_u, partitions[partition], sol_fields); diff --git a/src/solvers/mg_solver.hpp b/src/solvers/mg_solver.hpp index 421f3b0b15a66..bbdb028304408 100644 --- a/src/solvers/mg_solver.hpp +++ b/src/solvers/mg_solver.hpp @@ -182,7 +182,8 @@ class MGSolver : public SolverBase, MGSolverCounter { PARTHENON_FAIL("Does not work with non-default partitioning."); auto partition = partitions[default_partition_idx]; - auto &md = pmesh->mesh_data.Add(container_base, partition); + auto &md = pmesh->mesh_data.AddFromSet( + container_base, partition, pmesh->mesh_data.GetCreationFields(container_base)); auto &md_u = pmesh->mesh_data.Add(container_u, partition, sol_fields); auto &md_res_err = pmesh->mesh_data.Add(container_res_err, partition, sol_fields); auto &md_rhs = pmesh->mesh_data.Add(container_rhs, partition, sol_fields); @@ -340,7 +341,8 @@ class MGSolver : public SolverBase, MGSolverCounter { bool input_is_zero) { using namespace utils; - auto &md_base = pmesh->mesh_data.Add(container_base, partition); + auto &md_base = pmesh->mesh_data.AddFromSet( + container_base, partition, pmesh->mesh_data.GetCreationFields(container_base)); auto &md_rhs = pmesh->mesh_data.Add(container_rhs, partition, sol_fields); auto &md_diag = pmesh->mesh_data.Add(container_diag, partition, sol_fields); auto &md_ax = pmesh->mesh_data.Add(container_temp, partition, sol_fields); @@ -418,7 +420,8 @@ class MGSolver : public SolverBase, MGSolverCounter { const int level = partition->grid.multigrid_level(); const auto [min_level, max_level] = GetMinMaxLevel(pmesh); - auto &md = pmesh->mesh_data.Add(container_base, partition); + auto &md = pmesh->mesh_data.AddFromSet( + container_base, partition, pmesh->mesh_data.GetCreationFields(container_base)); auto &md_diag = pmesh->mesh_data.Add(container_diag, partition, sol_fields); auto task_out = dependence; @@ -457,7 +460,8 @@ class MGSolver : public SolverBase, MGSolverCounter { bool do_FAS = params_.do_FAS; - auto &md = pmesh->mesh_data.Add(container_base, partition); + auto &md = pmesh->mesh_data.AddFromSet( + container_base, partition, pmesh->mesh_data.GetCreationFields(container_base)); auto &md_u = pmesh->mesh_data.Add(container_u, partition, sol_fields); auto &md_rhs = pmesh->mesh_data.Add(container_rhs, partition, sol_fields); auto &md_res_err = pmesh->mesh_data.Add(container_res_err, partition, sol_fields);