From 1e9ebd0e850b4c1f83bccdc43717d29b3382c338 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Wed, 20 Nov 2024 10:32:20 -0500 Subject: [PATCH 1/9] mvoing get_attribute around a little --- .../multimesh/AttributeDescription.hpp | 15 +++ .../wmtk/components/multimesh/CMakeLists.txt | 7 +- .../components/multimesh/NamedMultiMesh.hpp | 1 + .../components/multimesh/get_attribute.cpp | 10 ++ .../components/multimesh/get_attribute.hpp | 19 +++ .../multimesh/utils/get_attribute.cpp | 127 +++++++++++++++++- .../multimesh/utils/get_attribute.hpp | 18 ++- .../multimesh/tests/named_multimesh.cpp | 54 +++++++- src/wmtk/attribute/AttributeType.hpp | 56 ++++++++ src/wmtk/attribute/CMakeLists.txt | 2 + src/wmtk/attribute/MeshAttributeHandle.hpp | 24 +--- 11 files changed, 307 insertions(+), 26 deletions(-) create mode 100644 components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp create mode 100644 src/wmtk/attribute/AttributeType.hpp diff --git a/components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp b/components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp new file mode 100644 index 0000000000..f7ea5e9df4 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp @@ -0,0 +1,15 @@ +#pragma once +#include +#include + +namespace wmtk::components::multimesh { + + +// the minimal information to uniquely extract an attribute handle +struct AttributeDescription +{ + std::string path; + PrimitiveType primitive_type; + attribute::AttributeType type; +}; +} // namespace wmtk::components::multimesh diff --git a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt index 226c9f200a..c4a8e28096 100644 --- a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt +++ b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt @@ -23,12 +23,15 @@ set(SRC_FILES NamedMultiMesh.hpp NamedMultiMesh.cpp - utils/get_attribute.hpp - utils/get_attribute.cpp internal/split_path.hpp MeshCollection.hpp MeshCollection.cpp + + AttributeDescription.hpp + + utils/get_attribute.hpp + utils/get_attribute.cpp ) include(stb) diff --git a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp index 2dd276bf35..b56e79b6d2 100644 --- a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp @@ -26,6 +26,7 @@ class NamedMultiMesh // auto operator=(NamedMultiMesh&&) -> NamedMultiMesh&; auto operator=(const NamedMultiMesh&) -> NamedMultiMesh&; + /// sets just the name of the root mesh, keeping child names the same void set_name(const std::string_view& root_name = ""); void set_names(const nlohmann::json& js); void set_root(Mesh& m); diff --git a/components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp new file mode 100644 index 0000000000..a349d66251 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp @@ -0,0 +1,10 @@ +#include "get_attribute.hpp" +#include +#include +#include "AttributeDescription.hpp" +#include "MeshCollection.hpp" +#include "NamedMultiMesh.hpp" + +namespace wmtk::components::multimesh { + +} // namespace wmtk::components::multimesh diff --git a/components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp b/components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp new file mode 100644 index 0000000000..24bfc5d68a --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include +#include + +namespace wmtk::components::multimesh { +class NamedMultiMesh; +class MeshCollection; +struct AttributeDescription; + +//namespace detail { +//// turns an attribute path mesh.path/attrname to mesh.path attrname +//std::array decompose_attribute_path( +// std::string_view attribute_path); +//std::array decompose_attribute_path( +// const AttributeDescription& description); +//} // namespace detail + +} // namespace wmtk::components::multimesh diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp index ea404b6dc6..e2e67707f6 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -3,9 +3,16 @@ #include #include "..//MeshCollection.hpp" #include "wmtk/utils/Logger.hpp" +#include "get_attribute.hpp" +#include +#include +#include "../AttributeDescription.hpp" +#include "../MeshCollection.hpp" +#include "NamedMultiMesh.hpp" + namespace wmtk::components::multimesh::utils { -wmtk::attribute::MeshAttributeHandle get_attribute(const Mesh& mesh, const nlohmann::json& js) +wmtk::attribute::MeshAttributeHandle get_attribute_from_json(const Mesh& mesh, const nlohmann::json& js) { if(js.is_string()) { spdlog::trace("Parsing {} as just a name", js.dump()); @@ -76,7 +83,7 @@ wmtk::attribute::MeshAttributeHandle get_attribute(const Mesh& mesh, const nlohm } } -wmtk::attribute::MeshAttributeHandle get_attribute( +wmtk::attribute::MeshAttributeHandle get_attribute_from_json( const wmtk::components::multimesh::MeshCollection& mc, const nlohmann::json& js) { @@ -84,4 +91,120 @@ wmtk::attribute::MeshAttributeHandle get_attribute( const auto& mesh = mc.get_mesh(name); return get_attribute(mesh, js); } + +namespace detail { +// turns an attribute path mesh.path/attrname to mesh.path attrname +// std::array +auto decompose_attribute_path(std::string_view attribute_path) +{ +#if defined(WMTK_ENABLED_CPP20) + using namespace std; + auto tmp = std::ranges::views::split(attribute_path, "/"sv) | + std::views::transform([](const auto& r) noexcept -> std::string_view { + return std::string_view(r.begin(), r.end()); + }); + + std::array ret; + std::ranges::copy(tmp, ret.begin()); + return ret; +#else + + std::array ret; + std::vector tmp; + if (view.empty()) { + tmp.emplace_back(""); + tmp.emplace_back(""); + + } else { + std::string v = std::string(view); + std::istringstream iss(v); + std::string token; + if (v.size() > 0 && v[0] == '/') { + r.emplace_back(""); + } + while (std::getline(iss, token, '/')) { + if (!token.empty()) tmp.emplace_back(token); + } + // at most 2 tokens are allowed + assert(tmp.size() <= 2); + if (tmp.size() == 1) { + tmp = {"", r[0]}; + } + } + return std::array{{tmp[0], tmp[1]}}; +#endif +} + +// std::array +auto decompose_attribute_path(const AttributeDescription& description) +{ + return decompose_attribute_path(description.path); +} +template +wmtk::attribute::MeshAttributeHandle +get_attribute(const Mesh& mesh, const std::string_view& name, PrimitiveType pt) +{ + return mesh.get_attribute_handle(std::string(name), pt); +} + +wmtk::attribute::MeshAttributeHandle get_attribute( + const Mesh& mesh, + const std::string_view& name, + PrimitiveType pt, + attribute::AttributeType type) +{ + using AT = attribute::AttributeType; + switch (type) { +#define ENTRY(TYPE) \ + case TYPE: \ + return get_attribute>( \ + mesh, \ + name, \ + pt); + ENTRY(AT::Char); + ENTRY(AT::Double); + ENTRY(AT::Int64); + ENTRY(AT::Rational); +#undef ENTRY + default: assert(false); + } + return {}; +} +} // namespace detail + +wmtk::attribute::MeshAttributeHandle get_attribute( + const NamedMultiMesh& mesh, + const AttributeDescription& description) +{ + auto [mesh_path, attribute_name] = detail::decompose_attribute_path(description); + return detail::get_attribute( + mesh.get_mesh(mesh_path), + attribute_name, + description.primitive_type, + description.type); +} +wmtk::attribute::MeshAttributeHandle get_attribute( + const MeshCollection& mesh, + const AttributeDescription& description) +{ + auto [mesh_path, attribute_name] = detail::decompose_attribute_path(description); + return detail::get_attribute( + mesh.get_mesh(mesh_path), + attribute_name, + description.primitive_type, + description.type); +} + +wmtk::attribute::MeshAttributeHandle get_attribute( + const Mesh& mesh, + const AttributeDescription& description) +{ + auto [mesh_path, attribute_name] = detail::decompose_attribute_path(description); + return detail::get_attribute( + mesh, + attribute_name, + description.primitive_type, + description.type); +} + } // namespace wmtk::components::multimesh::utils diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp index 3cd780b5b7..d25c9aef4a 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp @@ -5,11 +5,14 @@ namespace wmtk { class Mesh; namespace components::multimesh { +class NamedMultiMesh; class MeshCollection; +struct AttributeDescription; } } // namespace wmtk -namespace wmtk::components::multimesh::utils { +namespace wmtk::components::multimesh { +namespace utils { // // desires json that has // * name (str for attribute name) @@ -19,8 +22,19 @@ namespace wmtk::components::multimesh::utils { // if type and simplex are missing code will search in lexicographical of (primitive_type, type) // where double < int < char < rational for type -wmtk::attribute::MeshAttributeHandle get_attribute(const Mesh& m, const nlohmann::json& js); wmtk::attribute::MeshAttributeHandle get_attribute( + const NamedMultiMesh& mesh, + const AttributeDescription& description); +wmtk::attribute::MeshAttributeHandle get_attribute( + const MeshCollection& mesh, + const AttributeDescription& description); +wmtk::attribute::MeshAttributeHandle get_attribute( + const Mesh& mesh, + const AttributeDescription& description); + +wmtk::attribute::MeshAttributeHandle get_attribute_from_json(const Mesh& m, const nlohmann::json& js); +wmtk::attribute::MeshAttributeHandle get_attribute_from_json( const wmtk::components::multimesh::MeshCollection& m, const nlohmann::json& js); +} } // namespace wmtk::components::multimesh::utils diff --git a/components/multimesh/tests/named_multimesh.cpp b/components/multimesh/tests/named_multimesh.cpp index 558c21e267..19daaae0f6 100644 --- a/components/multimesh/tests/named_multimesh.cpp +++ b/components/multimesh/tests/named_multimesh.cpp @@ -4,7 +4,8 @@ #include #include #include "tools/TriMesh_examples.hpp" -#include "wmtk/components/multimesh/NamedMultiMesh.hpp" +#include +#include #include @@ -128,3 +129,54 @@ TEST_CASE("named_multimesh_parse", "[components][multimesh]") named_mm.get_mesh(".child.c2").shared_from_this()); } } + +TEST_CASE("named_multimesh_parse", "[components][multimesh]") +{ + { + auto m = make_mesh(); + wmtk::components::multimesh::NamedMultiMesh named_mm; + named_mm.set_mesh(*m); + + named_mm.set_name("roo"); + + wmtk::components::multimesh::utils::get_attribute(named_mm, + } + + + { + auto m = make_mesh(); + make_child(*m, {0}); + + + wmtk::components::multimesh::NamedMultiMesh named_mm; + named_mm.set_mesh(*m); + { + nlohmann::json js; + js["roo"] = nlohmann::json::array({"child"}); + named_mm.set_names(js); + } + CHECK(std::vector{} == named_mm.get_id("roo")); + CHECK(std::vector{0} == named_mm.get_id("roo.child")); + CHECK(m == named_mm.root().shared_from_this()); + CHECK(m == named_mm.get_mesh("roo").shared_from_this()); + CHECK( + m->get_multi_mesh_child_mesh({0}).shared_from_this() == + named_mm.get_mesh("roo.child").shared_from_this()); + } + { + wmtk::components::multimesh::NamedMultiMesh named_mm; + nlohmann::json js; + js["roo"] = nlohmann::json("child"); + named_mm.set_names(js); + CHECK(std::vector{} == named_mm.get_id("roo")); + CHECK(std::vector{0} == named_mm.get_id("roo.child")); + } + { + wmtk::components::multimesh::NamedMultiMesh named_mm; + nlohmann::json js; + js["roo"]["child"] = {}; + named_mm.set_names(js); + CHECK(std::vector{} == named_mm.get_id("roo")); + CHECK(std::vector{0} == named_mm.get_id("roo.child")); + } +} diff --git a/src/wmtk/attribute/AttributeType.hpp b/src/wmtk/attribute/AttributeType.hpp new file mode 100644 index 0000000000..3cccfddf19 --- /dev/null +++ b/src/wmtk/attribute/AttributeType.hpp @@ -0,0 +1,56 @@ +#pragma once +#include + +namespace wmtk::attribute { +enum class AttributeType { Char = 0, Int64 = 1, Double = 2, Rational = 3 }; + +template +struct type_from_attribute_type_enum +{ +}; +template <> +struct type_from_attribute_type_enum +{ + using type = char; +}; +template <> +struct type_from_attribute_type_enum +{ + using type = double; +}; +template <> +struct type_from_attribute_type_enum +{ + using type = int64_t; +}; +template <> +struct type_from_attribute_type_enum +{ + using type = wmtk::Rational; +}; + +template +using type_from_attribute_type_enum_t = typename type_from_attribute_type_enum::type; + +template +inline constexpr auto attribute_type_enum_from_type() -> AttributeType +{ + if constexpr (std::is_same_v) { + return AttributeType::Char; + } else if constexpr (std::is_same_v) { + return AttributeType::Double; + } else if constexpr (std::is_same_v) { + return AttributeType::Int64; + } else if constexpr (std::is_same_v) { + return AttributeType::Rational; + } + // If a compiler complains about the potentiality of no return value then a type accepted by the + // HAndleVariant is not being represented properly. If the comppiler is simply unhappy to not + // see a return then we should hack a default return value in an else statement with an asswert + // :(. + else { + static_assert(std::is_same_v); + return AttributeType::Char; + } +} +} // namespace wmtk::attribute diff --git a/src/wmtk/attribute/CMakeLists.txt b/src/wmtk/attribute/CMakeLists.txt index e2e22c6ba2..cd8f3506ca 100644 --- a/src/wmtk/attribute/CMakeLists.txt +++ b/src/wmtk/attribute/CMakeLists.txt @@ -33,6 +33,8 @@ set(SRC_FILES # Accessor.hxx Accessor.hpp + + AttributeType.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/attribute/MeshAttributeHandle.hpp b/src/wmtk/attribute/MeshAttributeHandle.hpp index 3e922a4044..5faf2539e4 100644 --- a/src/wmtk/attribute/MeshAttributeHandle.hpp +++ b/src/wmtk/attribute/MeshAttributeHandle.hpp @@ -5,8 +5,10 @@ // #include // +#include "AttributeType.hpp" #include "TypedAttributeHandle.hpp" +#include #include namespace wmtk { @@ -39,7 +41,7 @@ class MeshAttributeHandle using ValueVariant = std:: variant>; - enum class HeldType { Char = 0, Int64 = 1, Double = 2, Rational = 3 }; + using HeldType = AttributeType; template using held_handle_type = std::variant_alternative_t; @@ -90,7 +92,6 @@ class MeshAttributeHandle return m_handle == o.m_handle && m_mesh == o.m_mesh; } - // reutrns if the target mesh is the same as the one represented in the handle bool is_same_mesh(const Mesh&) const; @@ -148,7 +149,7 @@ class MeshAttributeHandle // return the dimension of the attribute (i.e the number of values stored per simplex) int64_t dimension() const; - std::string name() const; + std::string name() const; private: @@ -217,22 +218,7 @@ inline constexpr auto MeshAttributeHandle::held_type_from_primitive() -> HeldTyp template inline constexpr auto MeshAttributeHandle::held_type_from_handle() -> HeldType { - if constexpr (std::is_same_v>) { - return HeldType::Char; - } else if constexpr (std::is_same_v>) { - return HeldType::Double; - } else if constexpr (std::is_same_v>) { - return HeldType::Int64; - } else if constexpr (std::is_same_v>) { - return HeldType::Rational; - } - // If a compiler complains about the potentiality of no return value then a type accepted by the - // HAndleVariant is not being represented properly. If the comppiler is simply unhappy to not - // see a return then we should hack a default return value in an else statement with an asswert - // :(. - else { - return HeldType::Char; - } + return attribute_type_enum_from_type(); } inline PrimitiveType MeshAttributeHandle::primitive_type() const From 5e0366f5392c7a440fc99cb286adbc78ac1276d9 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Thu, 21 Nov 2024 11:31:59 -0500 Subject: [PATCH 2/9] enabling fetching attributes by a descrpitor struct --- .../wmtk/components/multimesh/CMakeLists.txt | 3 +- .../components/multimesh/MeshCollection.cpp | 24 +++- .../components/multimesh/MeshCollection.hpp | 2 +- .../components/multimesh/NamedMultiMesh.cpp | 5 + .../components/multimesh/NamedMultiMesh.hpp | 8 +- .../components/multimesh/get_attribute.cpp | 10 -- .../components/multimesh/get_attribute.hpp | 19 --- .../multimesh/utils/AttributeDescription.cpp | 35 +++++ .../{ => utils}/AttributeDescription.hpp | 9 +- .../multimesh/utils/get_attribute.cpp | 44 +++--- .../multimesh/utils/get_attribute.hpp | 2 +- .../multimesh/tests/named_multimesh.cpp | 130 ++++++++++++++---- 12 files changed, 204 insertions(+), 87 deletions(-) delete mode 100644 components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp delete mode 100644 components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp rename components/multimesh/src/wmtk/components/multimesh/{ => utils}/AttributeDescription.hpp (53%) diff --git a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt index c4a8e28096..1bb7d19dc7 100644 --- a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt +++ b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt @@ -28,7 +28,8 @@ set(SRC_FILES MeshCollection.hpp MeshCollection.cpp - AttributeDescription.hpp + utils/AttributeDescription.hpp + utils/AttributeDescription.cpp utils/get_attribute.hpp utils/get_attribute.cpp diff --git a/components/multimesh/src/wmtk/components/multimesh/MeshCollection.cpp b/components/multimesh/src/wmtk/components/multimesh/MeshCollection.cpp index f9cc6cb773..509e64f8c5 100644 --- a/components/multimesh/src/wmtk/components/multimesh/MeshCollection.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/MeshCollection.cpp @@ -8,11 +8,12 @@ namespace wmtk::components::multimesh { -NamedMultiMesh& MeshCollection::add_mesh(NamedMultiMesh m) +NamedMultiMesh& MeshCollection::add_mesh(NamedMultiMesh&& m) { auto mptr = std::make_unique(std::move(m)); auto [it, did] = m_meshes.emplace(mptr->root_name(), std::move(mptr)); + return *it->second; } @@ -51,6 +52,7 @@ bool MeshCollection::has_mesh(const std::string_view& path) const const NamedMultiMesh& MeshCollection::get_named_multimesh(const std::string_view& path) const { + assert(!m_meshes.empty()); using namespace std; #if defined(WMTK_ENABLED_CPP20) std::ranges::view auto split = internal::split_path(path); @@ -63,6 +65,19 @@ const NamedMultiMesh& MeshCollection::get_named_multimesh(const std::string_view "assuming that is the right mesh"); return *m_meshes.begin()->second; } + if (auto it = m_meshes.find(nmm_name); it == m_meshes.end()) { + std::vector names; + std::transform( + m_meshes.begin(), + m_meshes.end(), + std::back_inserter(names), + [](const auto& pr) { return pr.first; }); + wmtk::logger().error( + "Was unable to find root mesh name {} among {} names [{}] in MeshCollection", + nmm_name, + m_meshes.size(), + fmt::join(names, ",")); + } return *m_meshes.at(nmm_name); } NamedMultiMesh& MeshCollection::get_named_multimesh(const std::string_view& path) @@ -80,7 +95,12 @@ NamedMultiMesh& MeshCollection::get_named_multimesh(const std::string_view& path "assuming that is the right mesh"); return *m_meshes.begin()->second; } - return *m_meshes.at(nmm_name); + try { + return *m_meshes.at(nmm_name); + } catch (const std::runtime_error& e) { + wmtk::logger().warn("Failed to find mesh named {} in mesh list. Path was ", nmm_name, path); + throw e; + } } std::map MeshCollection::all_meshes() const // std::map> MeshCollection::all_meshes() const diff --git a/components/multimesh/src/wmtk/components/multimesh/MeshCollection.hpp b/components/multimesh/src/wmtk/components/multimesh/MeshCollection.hpp index e3feb91d49..9c7271034d 100644 --- a/components/multimesh/src/wmtk/components/multimesh/MeshCollection.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/MeshCollection.hpp @@ -9,7 +9,7 @@ class InputOptions; class MeshCollection { public: - NamedMultiMesh& add_mesh(NamedMultiMesh o); + NamedMultiMesh& add_mesh(NamedMultiMesh&& o); template NamedMultiMesh& emplace_mesh(Args&&... args) { diff --git a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.cpp b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.cpp index defb15b2a8..569de1c982 100644 --- a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.cpp @@ -157,7 +157,12 @@ auto NamedMultiMesh::get_id(const std::string_view& path) const -> std::vectorname || *split.begin() == ""); for (const auto& token : std::ranges::views::drop(split, 1)) { + // try { int64_t index = cur_mesh->m_child_indexer.at(std::string(token)); + //} catch(const std::runtime_error& e) { + // wmtk::logger().warn("Failed to find mesh named {} in mesh list. Path was ", nmm_name, + // path); throw e; + //} indices.emplace_back(index); cur_mesh = cur_mesh->m_children[index].get(); } diff --git a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp index b56e79b6d2..ed25016151 100644 --- a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp @@ -17,9 +17,15 @@ class NamedMultiMesh { public: NamedMultiMesh(); - explicit NamedMultiMesh(Mesh& m, const std::string& root_name); NamedMultiMesh(Mesh& m, const std::string_view& root_name); explicit NamedMultiMesh(Mesh& m, const nlohmann::json& root_name); + + // Explicit constructors to remove ambiguities between string_view and json constructors + explicit NamedMultiMesh(Mesh& m, const std::string& root_name); + template + explicit NamedMultiMesh(Mesh& m, const char name[N]) + : NamedMultiMesh(m, std::string_view(name)) + {} // NamedMultiMesh(NamedMultiMesh&&); NamedMultiMesh(const NamedMultiMesh&); ~NamedMultiMesh(); diff --git a/components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp deleted file mode 100644 index a349d66251..0000000000 --- a/components/multimesh/src/wmtk/components/multimesh/get_attribute.cpp +++ /dev/null @@ -1,10 +0,0 @@ -#include "get_attribute.hpp" -#include -#include -#include "AttributeDescription.hpp" -#include "MeshCollection.hpp" -#include "NamedMultiMesh.hpp" - -namespace wmtk::components::multimesh { - -} // namespace wmtk::components::multimesh diff --git a/components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp b/components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp deleted file mode 100644 index 24bfc5d68a..0000000000 --- a/components/multimesh/src/wmtk/components/multimesh/get_attribute.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once -#include -#include -#include - -namespace wmtk::components::multimesh { -class NamedMultiMesh; -class MeshCollection; -struct AttributeDescription; - -//namespace detail { -//// turns an attribute path mesh.path/attrname to mesh.path attrname -//std::array decompose_attribute_path( -// std::string_view attribute_path); -//std::array decompose_attribute_path( -// const AttributeDescription& description); -//} // namespace detail - -} // namespace wmtk::components::multimesh diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp new file mode 100644 index 0000000000..49f89c740b --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp @@ -0,0 +1,35 @@ +#include "AttributeDescription.hpp" +#include + + +namespace wmtk::components::multimesh::utils { + + namespace { + NLOHMANN_JSON_SERIALIZE_ENUM(PrimitiveType ,\ + {\ + {PrimitiveType::Vertex, 0},\ + {PrimitiveType::Edge, 1},\ + {PrimitiveType::Triangle, 2},\ + {PrimitiveType::Tetrahedron, 3},\ + }) + NLOHMANN_JSON_SERIALIZE_ENUM(attribute::AttributeType,\ + {\ + {attribute::AttributeType::Char, "char"},\ + {attribute::AttributeType::Double, "double"},\ + {attribute::AttributeType::Int64, "int"},\ + {attribute::AttributeType::Rational, "rational"},\ + }) + } + WMTK_NLOHMANN_JSON_FRIEND_TO_JSON_PROTOTYPE(AttributeDescription) { + + + WMTK_NLOHMANN_ASSIGN_TYPE_TO_JSON(path, dimension, type); + } + + PrimitiveType AttributeDescription::primitive_type() const { + assert(dimension < 4); + + return get_primitive_type_from_id(dimension); + } + +} diff --git a/components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp similarity index 53% rename from components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp rename to components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp index f7ea5e9df4..f684aee7c4 100644 --- a/components/multimesh/src/wmtk/components/multimesh/AttributeDescription.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp @@ -1,15 +1,20 @@ #pragma once #include #include +#include -namespace wmtk::components::multimesh { +namespace wmtk::components::multimesh::utils { // the minimal information to uniquely extract an attribute handle struct AttributeDescription { std::string path; - PrimitiveType primitive_type; + uint8_t dimension;// internally the primitive type attribute::AttributeType type; + + PrimitiveType primitive_type() const; + + WMTK_NLOHMANN_JSON_FRIEND_DECLARATION(AttributeDescription) }; } // namespace wmtk::components::multimesh diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp index e2e67707f6..0fce202e83 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -1,28 +1,28 @@ #include "get_attribute.hpp" +#include #include #include "..//MeshCollection.hpp" -#include "wmtk/utils/Logger.hpp" +#include "../NamedMultiMesh.hpp" +#include "AttributeDescription.hpp" #include "get_attribute.hpp" -#include -#include -#include "../AttributeDescription.hpp" -#include "../MeshCollection.hpp" -#include "NamedMultiMesh.hpp" +#include "wmtk/utils/Logger.hpp" namespace wmtk::components::multimesh::utils { -wmtk::attribute::MeshAttributeHandle get_attribute_from_json(const Mesh& mesh, const nlohmann::json& js) +wmtk::attribute::MeshAttributeHandle get_attribute_from_json( + const Mesh& mesh, + const nlohmann::json& js) { - if(js.is_string()) { + if (js.is_string()) { spdlog::trace("Parsing {} as just a name", js.dump()); } const std::string name = js.is_string() ? js.get() : js["name"].get(); std::string type_name = js.contains("type") ? js["type"] : ""; int simplex_dim = js.contains("simplex") ? js["simplex"].get() : -1; - auto try_types= [&](int index) { - const PrimitiveType pt = wmtk::get_primitive_type_from_id(index); + auto try_types = [&](int index) { + const PrimitiveType pt = wmtk::get_primitive_type_from_id(index); // search if (mesh.has_attribute(name, pt)) { type_name = "double"; @@ -42,16 +42,15 @@ wmtk::attribute::MeshAttributeHandle get_attribute_from_json(const Mesh& mesh, c // if simplex dim is missing then both simplex dim and type name is populated - if(simplex_dim == -1) { - for(int j = 0 ; j < mesh.top_cell_dimension(); ++j) { - if(try_types(j)) { + if (simplex_dim == -1) { + for (int j = 0; j < mesh.top_cell_dimension(); ++j) { + if (try_types(j)) { simplex_dim = j; break; } - } // if simplex dim was tehre but type name not populated we populate - } else if(type_name.empty()) { + } else if (type_name.empty()) { try_types(simplex_dim); } // only other case is both are populated, which is fine @@ -89,7 +88,7 @@ wmtk::attribute::MeshAttributeHandle get_attribute_from_json( { const std::string name = js.contains("mesh") ? js["mesh"] : ""; const auto& mesh = mc.get_mesh(name); - return get_attribute(mesh, js); + return get_attribute_from_json(mesh, js); } namespace detail { @@ -106,6 +105,9 @@ auto decompose_attribute_path(std::string_view attribute_path) std::array ret; std::ranges::copy(tmp, ret.begin()); + if (ret[1].empty()) { + std::swap(ret[0], ret[1]); + } return ret; #else @@ -180,7 +182,7 @@ wmtk::attribute::MeshAttributeHandle get_attribute( return detail::get_attribute( mesh.get_mesh(mesh_path), attribute_name, - description.primitive_type, + description.primitive_type(), description.type); } wmtk::attribute::MeshAttributeHandle get_attribute( @@ -188,10 +190,12 @@ wmtk::attribute::MeshAttributeHandle get_attribute( const AttributeDescription& description) { auto [mesh_path, attribute_name] = detail::decompose_attribute_path(description); + + const Mesh& nmm = mesh.get_mesh(mesh_path); return detail::get_attribute( - mesh.get_mesh(mesh_path), + nmm, attribute_name, - description.primitive_type, + description.primitive_type(), description.type); } @@ -203,7 +207,7 @@ wmtk::attribute::MeshAttributeHandle get_attribute( return detail::get_attribute( mesh, attribute_name, - description.primitive_type, + description.primitive_type(), description.type); } diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp index d25c9aef4a..e11a408318 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp @@ -7,12 +7,12 @@ class Mesh; namespace components::multimesh { class NamedMultiMesh; class MeshCollection; -struct AttributeDescription; } } // namespace wmtk namespace wmtk::components::multimesh { namespace utils { +struct AttributeDescription; // // desires json that has // * name (str for attribute name) diff --git a/components/multimesh/tests/named_multimesh.cpp b/components/multimesh/tests/named_multimesh.cpp index 19daaae0f6..cfc84fa9df 100644 --- a/components/multimesh/tests/named_multimesh.cpp +++ b/components/multimesh/tests/named_multimesh.cpp @@ -3,9 +3,12 @@ #include #include #include -#include "tools/TriMesh_examples.hpp" #include +#include #include +#include +#include "tools/TriMesh_examples.hpp" +#include "wmtk/components/multimesh/MeshCollection.hpp" #include @@ -20,11 +23,13 @@ auto make_mesh() } auto make_child(wmtk::Mesh& m, const std::vector& path) + -> std::vector> { if (path.size() == 0) { // multimesh root mesh already exists so nothing to be done - return; + return {}; } + std::vector> meshes; for (size_t j = 0; j < path.size(); ++j) { std::vector p(path.begin(), path.begin() + j); auto& cur_mesh = m.get_multi_mesh_mesh(p); @@ -35,8 +40,10 @@ auto make_child(wmtk::Mesh& m, const std::vector& path) auto map = wmtk::multimesh::same_simplex_dimension_bijection(cur_mesh, *new_mesh); cur_mesh.register_child_mesh(new_mesh, map); + meshes.emplace_back(new_mesh); } } + return meshes; } } // namespace @@ -130,22 +137,81 @@ TEST_CASE("named_multimesh_parse", "[components][multimesh]") } } -TEST_CASE("named_multimesh_parse", "[components][multimesh]") +TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") { { auto m = make_mesh(); - wmtk::components::multimesh::NamedMultiMesh named_mm; - named_mm.set_mesh(*m); - - named_mm.set_name("roo"); - - wmtk::components::multimesh::utils::get_attribute(named_mm, + wmtk::components::multimesh::MeshCollection mc; + wmtk::components::multimesh::NamedMultiMesh& named_mm = + mc.emplace_mesh(*m, std::string("roo")); + + auto double_test_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); + auto char_test_handle = + m->register_attribute("char_test", wmtk::PrimitiveType::Vertex, 1); + auto int_test_handle = + m->register_attribute("int_test", wmtk::PrimitiveType::Vertex, 1); + auto rational_test_handle = + m->register_attribute("rational_test", wmtk::PrimitiveType::Vertex, 1); + + using AT = wmtk::attribute::AttributeType; + + + { // double check that path extraction is working + std::vector double_ads; + double_ads.emplace_back("double_test", 0, AT::Double); + double_ads.emplace_back("/double_test", 0, AT::Double); + double_ads.emplace_back("roo/double_test", 0, AT::Double); + + + for (const auto& ad : double_ads) { + auto h = wmtk::components::multimesh::utils::get_attribute(named_mm, ad); + CHECK(double_test_handle == h); + // just on mesh also works + auto h2 = wmtk::components::multimesh::utils::get_attribute(*m, ad); + CHECK(double_test_handle == h2); + //// meshcollection too + auto h3 = wmtk::components::multimesh::utils::get_attribute(mc, ad); + CHECK(double_test_handle == h3); + } + } + { // check that other types work + CHECK( + rational_test_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"rational_test", 0, AT::Rational})); + CHECK( + int_test_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"int_test", 0, AT::Int64})); + CHECK( + char_test_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"char_test", 0, AT::Char})); + } + { // check that other simplex types work + auto edge_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Edge, 1); + auto tri_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Triangle, 1); + CHECK( + edge_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"double_test", 1, AT::Double})); + CHECK( + tri_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"double_test", 2, AT::Double})); + // TODO: lazy about testing tet + } } { auto m = make_mesh(); - make_child(*m, {0}); + auto children = make_child(*m, {0}); + REQUIRE(children.size() == 1); + auto child = children[0]; wmtk::components::multimesh::NamedMultiMesh named_mm; @@ -157,26 +223,30 @@ TEST_CASE("named_multimesh_parse", "[components][multimesh]") } CHECK(std::vector{} == named_mm.get_id("roo")); CHECK(std::vector{0} == named_mm.get_id("roo.child")); - CHECK(m == named_mm.root().shared_from_this()); - CHECK(m == named_mm.get_mesh("roo").shared_from_this()); + auto attr_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); + auto child_attr_handle = + child->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); + using AT = wmtk::attribute::AttributeType; CHECK( - m->get_multi_mesh_child_mesh({0}).shared_from_this() == - named_mm.get_mesh("roo.child").shared_from_this()); - } - { - wmtk::components::multimesh::NamedMultiMesh named_mm; - nlohmann::json js; - js["roo"] = nlohmann::json("child"); - named_mm.set_names(js); - CHECK(std::vector{} == named_mm.get_id("roo")); - CHECK(std::vector{0} == named_mm.get_id("roo.child")); - } - { - wmtk::components::multimesh::NamedMultiMesh named_mm; - nlohmann::json js; - js["roo"]["child"] = {}; - named_mm.set_names(js); - CHECK(std::vector{} == named_mm.get_id("roo")); - CHECK(std::vector{0} == named_mm.get_id("roo.child")); + attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"double_test", 0, AT::Double})); + CHECK( + attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"/double_test", 0, AT::Double})); + CHECK( + attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"roo/double_test", 0, AT::Double})); + CHECK( + child_attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {"roo.child/double_test", 0, AT::Double})); + CHECK( + child_attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + {".child/double_test", 0, AT::Double})); } } From 22217aa1639b47cd9c3ebc1b91be12ec57e20516 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Thu, 21 Nov 2024 12:08:37 -0500 Subject: [PATCH 3/9] making unit tests pass --- .../multimesh/utils/AttributeDescription.cpp | 67 +++++++++++-------- .../multimesh/utils/AttributeDescription.hpp | 9 ++- .../multimesh/tests/named_multimesh.cpp | 42 ++++++++++++ 3 files changed, 89 insertions(+), 29 deletions(-) diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp index 49f89c740b..9e662097da 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp @@ -1,35 +1,48 @@ #include "AttributeDescription.hpp" #include +namespace wmtk::attribute { +// TODO: this definitely will cause a conflict someday if someone else wants to serialize +// attributetype +NLOHMANN_JSON_SERIALIZE_ENUM( + AttributeType, + { + {AttributeType::Char, "char"}, + {AttributeType::Double, "double"}, + {AttributeType::Int64, "int"}, + {AttributeType::Rational, "rational"}, + }) +} // namespace wmtk::attribute namespace wmtk::components::multimesh::utils { +auto AttributeDescription::operator<=>(const AttributeDescription&) const -> std::strong_ordering = + default; +auto AttributeDescription::operator==(const AttributeDescription&) const -> bool = default; + +namespace { +NLOHMANN_JSON_SERIALIZE_ENUM( + PrimitiveType, + { + {PrimitiveType::Vertex, 0}, + {PrimitiveType::Edge, 1}, + {PrimitiveType::Triangle, 2}, + {PrimitiveType::Tetrahedron, 3}, + }) +} // namespace +WMTK_NLOHMANN_JSON_FRIEND_TO_JSON_PROTOTYPE(AttributeDescription) +{ + WMTK_NLOHMANN_ASSIGN_TYPE_TO_JSON(path, dimension, type); +} +WMTK_NLOHMANN_JSON_FRIEND_FROM_JSON_PROTOTYPE(AttributeDescription) +{ + WMTK_NLOHMANN_ASSIGN_TYPE_FROM_JSON(path, dimension, type); +} - namespace { - NLOHMANN_JSON_SERIALIZE_ENUM(PrimitiveType ,\ - {\ - {PrimitiveType::Vertex, 0},\ - {PrimitiveType::Edge, 1},\ - {PrimitiveType::Triangle, 2},\ - {PrimitiveType::Tetrahedron, 3},\ - }) - NLOHMANN_JSON_SERIALIZE_ENUM(attribute::AttributeType,\ - {\ - {attribute::AttributeType::Char, "char"},\ - {attribute::AttributeType::Double, "double"},\ - {attribute::AttributeType::Int64, "int"},\ - {attribute::AttributeType::Rational, "rational"},\ - }) - } - WMTK_NLOHMANN_JSON_FRIEND_TO_JSON_PROTOTYPE(AttributeDescription) { - - - WMTK_NLOHMANN_ASSIGN_TYPE_TO_JSON(path, dimension, type); - } - - PrimitiveType AttributeDescription::primitive_type() const { - assert(dimension < 4); - - return get_primitive_type_from_id(dimension); - } +PrimitiveType AttributeDescription::primitive_type() const +{ + assert(dimension < 4); + return get_primitive_type_from_id(dimension); } + +} // namespace wmtk::components::multimesh::utils diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp index f684aee7c4..4f64fb7d3c 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -10,11 +11,15 @@ namespace wmtk::components::multimesh::utils { struct AttributeDescription { std::string path; - uint8_t dimension;// internally the primitive type + uint8_t dimension; // internally the primitive type attribute::AttributeType type; PrimitiveType primitive_type() const; + auto operator<=>(const AttributeDescription&) const -> std::strong_ordering; + auto operator==(const AttributeDescription&) const -> bool; + + WMTK_NLOHMANN_JSON_FRIEND_DECLARATION(AttributeDescription) }; -} // namespace wmtk::components::multimesh +} // namespace wmtk::components::multimesh::utils diff --git a/components/multimesh/tests/named_multimesh.cpp b/components/multimesh/tests/named_multimesh.cpp index cfc84fa9df..85287a8b1f 100644 --- a/components/multimesh/tests/named_multimesh.cpp +++ b/components/multimesh/tests/named_multimesh.cpp @@ -250,3 +250,45 @@ TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") {".child/double_test", 0, AT::Double})); } } + +TEST_CASE("multimesh_attribute_description_json", "[components][multimesh]") +{ + using AT = wmtk::attribute::AttributeType; + using AD = wmtk::components::multimesh::utils::AttributeDescription; + using JS = nlohmann::json; + auto check = [](const AD& ad, const JS& js) { + JS js2 = ad; + CHECK(js2 == js); + CHECK(ad == js.get()); + }; + { + AD ad{"double_test", 0, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 0}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"rational_test", 0, AT::Rational}; + JS js{{"path", "rational_test"}, {"dimension", 0}, {"type", "rational"}}; + check(ad, js); + } + { + AD ad{"int_test", 0, AT::Int64}; + JS js{{"path", "int_test"}, {"dimension", 0}, {"type", "int"}}; + check(ad, js); + } + { + AD ad{"char_test", 0, AT::Char}; + JS js{{"path", "char_test"}, {"dimension", 0}, {"type", "char"}}; + check(ad, js); + } + { + AD ad{"double_test", 1, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 1}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"double_test", 2, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 2}, {"type", "double"}}; + check(ad, js); + } +} From 221124c5e8e8c1474eb25005ca73f8e60a75a12c Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Thu, 21 Nov 2024 12:10:10 -0500 Subject: [PATCH 4/9] removing unused get_attribute that take in json --- .../multimesh/utils/get_attribute.cpp | 80 ------------------- .../multimesh/utils/get_attribute.hpp | 4 - 2 files changed, 84 deletions(-) diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp index 0fce202e83..e7aa18bd88 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -10,86 +10,6 @@ namespace wmtk::components::multimesh::utils { -wmtk::attribute::MeshAttributeHandle get_attribute_from_json( - const Mesh& mesh, - const nlohmann::json& js) -{ - if (js.is_string()) { - spdlog::trace("Parsing {} as just a name", js.dump()); - } - const std::string name = js.is_string() ? js.get() : js["name"].get(); - std::string type_name = js.contains("type") ? js["type"] : ""; - int simplex_dim = js.contains("simplex") ? js["simplex"].get() : -1; - - auto try_types = [&](int index) { - const PrimitiveType pt = wmtk::get_primitive_type_from_id(index); - // search - if (mesh.has_attribute(name, pt)) { - type_name = "double"; - return true; - } else if (mesh.has_attribute(name, pt)) { - type_name = "int"; - return true; - } else if (mesh.has_attribute(name, pt)) { - type_name = "char"; - return true; - } else if (mesh.has_attribute(name, pt)) { - type_name = "rational"; - return true; - } - return false; - }; - - - // if simplex dim is missing then both simplex dim and type name is populated - if (simplex_dim == -1) { - for (int j = 0; j < mesh.top_cell_dimension(); ++j) { - if (try_types(j)) { - simplex_dim = j; - break; - } - } - // if simplex dim was tehre but type name not populated we populate - } else if (type_name.empty()) { - try_types(simplex_dim); - } - // only other case is both are populated, which is fine - - assert(!type_name.empty()); - assert(simplex_dim != -1); - - const PrimitiveType pt = wmtk::get_primitive_type_from_id(simplex_dim); - - - if (type_name == "double") { - using T = double; - return mesh.get_attribute_handle(name, pt); - } else if (type_name == "int") { - using T = int64_t; - return mesh.get_attribute_handle(name, pt); - } else if (type_name == "char") { - using T = char; - return mesh.get_attribute_handle(name, pt); - } else if (type_name == "rational") { - using T = wmtk::Rational; - return mesh.get_attribute_handle(name, pt); - } else { - wmtk::log_and_throw_error( - "get_attribute got an attribute called {} of type {}, not in int/double/char/rational", - name, - type_name); - return {}; - } -} - -wmtk::attribute::MeshAttributeHandle get_attribute_from_json( - const wmtk::components::multimesh::MeshCollection& mc, - const nlohmann::json& js) -{ - const std::string name = js.contains("mesh") ? js["mesh"] : ""; - const auto& mesh = mc.get_mesh(name); - return get_attribute_from_json(mesh, js); -} namespace detail { // turns an attribute path mesh.path/attrname to mesh.path attrname diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp index e11a408318..6780e82bb3 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp @@ -32,9 +32,5 @@ wmtk::attribute::MeshAttributeHandle get_attribute( const Mesh& mesh, const AttributeDescription& description); -wmtk::attribute::MeshAttributeHandle get_attribute_from_json(const Mesh& m, const nlohmann::json& js); -wmtk::attribute::MeshAttributeHandle get_attribute_from_json( - const wmtk::components::multimesh::MeshCollection& m, - const nlohmann::json& js); } } // namespace wmtk::components::multimesh::utils From 22a0240569b9b32cbebc3068570b7e9fd35ff35a Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 22 Nov 2024 15:40:55 -0500 Subject: [PATCH 5/9] fixing typos in non-cpp20 branch --- .../src/wmtk/components/multimesh/utils/get_attribute.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp index e7aa18bd88..984ea7542d 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -33,16 +33,16 @@ auto decompose_attribute_path(std::string_view attribute_path) std::array ret; std::vector tmp; - if (view.empty()) { + if (attribute_path.empty()) { tmp.emplace_back(""); tmp.emplace_back(""); } else { - std::string v = std::string(view); + std::string v = std::string(attribute_path); std::istringstream iss(v); std::string token; if (v.size() > 0 && v[0] == '/') { - r.emplace_back(""); + tmp.emplace_back(""); } while (std::getline(iss, token, '/')) { if (!token.empty()) tmp.emplace_back(token); @@ -50,7 +50,7 @@ auto decompose_attribute_path(std::string_view attribute_path) // at most 2 tokens are allowed assert(tmp.size() <= 2); if (tmp.size() == 1) { - tmp = {"", r[0]}; + tmp = {"", tmp[0]}; } } return std::array{{tmp[0], tmp[1]}}; From 1c4baaf171cfed9b2bd84a032a007dc7619eeff5 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Fri, 22 Nov 2024 16:19:17 -0500 Subject: [PATCH 6/9] AttributeDescription: adding comment to enable aggregate initialization and cleaning up how unit test uses it for other ocmpilers --- .../multimesh/utils/AttributeDescription.hpp | 8 +++++ .../multimesh/tests/named_multimesh.cpp | 31 ++++++++++--------- 2 files changed, 24 insertions(+), 15 deletions(-) diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp index 4f64fb7d3c..05b0811889 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp @@ -10,6 +10,14 @@ namespace wmtk::components::multimesh::utils { // the minimal information to uniquely extract an attribute handle struct AttributeDescription { + // Avoiding defining any constructors to enable aggregate construction + //AttributeDescription() = default; + //AttributeDescription(const AttributeDescription&) = default; + //AttributeDescription(AttributeDescription&&) = default; + //AttributeDescription& operator=(const AttributeDescription&) = default; + //AttributeDescription& operator=(AttributeDescription&&) = default; + //~AttributeDescription() = default; + std::string path; uint8_t dimension; // internally the primitive type attribute::AttributeType type; diff --git a/components/multimesh/tests/named_multimesh.cpp b/components/multimesh/tests/named_multimesh.cpp index 85287a8b1f..0f6c05b723 100644 --- a/components/multimesh/tests/named_multimesh.cpp +++ b/components/multimesh/tests/named_multimesh.cpp @@ -156,12 +156,12 @@ TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") using AT = wmtk::attribute::AttributeType; - +using AD = wmtk::components::multimesh::utils::AttributeDescription; { // double check that path extraction is working - std::vector double_ads; - double_ads.emplace_back("double_test", 0, AT::Double); - double_ads.emplace_back("/double_test", 0, AT::Double); - double_ads.emplace_back("roo/double_test", 0, AT::Double); + std::vector double_ads; + double_ads.emplace_back(AD{"double_test", 0, AT::Double}); + double_ads.emplace_back(AD{"/double_test", 0, AT::Double}); + double_ads.emplace_back(AD{"roo/double_test", 0, AT::Double}); for (const auto& ad : double_ads) { @@ -179,15 +179,15 @@ TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") CHECK( rational_test_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"rational_test", 0, AT::Rational})); + AD{"rational_test", 0, AT::Rational})); CHECK( int_test_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"int_test", 0, AT::Int64})); + AD{"int_test", 0, AT::Int64})); CHECK( char_test_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"char_test", 0, AT::Char})); + AD{"char_test", 0, AT::Char})); } { // check that other simplex types work auto edge_handle = @@ -197,17 +197,18 @@ TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") CHECK( edge_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"double_test", 1, AT::Double})); + AD{"double_test", 1, AT::Double})); CHECK( tri_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"double_test", 2, AT::Double})); + AD{"double_test", 2, AT::Double})); // TODO: lazy about testing tet } } { + using AD = wmtk::components::multimesh::utils::AttributeDescription; auto m = make_mesh(); auto children = make_child(*m, {0}); REQUIRE(children.size() == 1); @@ -231,23 +232,23 @@ TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") CHECK( attr_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"double_test", 0, AT::Double})); + AD{"double_test", 0, AT::Double})); CHECK( attr_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"/double_test", 0, AT::Double})); + AD{"/double_test", 0, AT::Double})); CHECK( attr_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"roo/double_test", 0, AT::Double})); + AD{"roo/double_test", 0, AT::Double})); CHECK( child_attr_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {"roo.child/double_test", 0, AT::Double})); + AD{"roo.child/double_test", 0, AT::Double})); CHECK( child_attr_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - {".child/double_test", 0, AT::Double})); + AD{".child/double_test", 0, AT::Double})); } } From 266927b1153e958eb1ae8909e136417dfe17c715 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 23 Nov 2024 17:01:07 -0500 Subject: [PATCH 7/9] adding ability to predeclare enum serialization with nlohmann json --- .../wmtk/components/utils/json_macros.hpp | 6 +++++ .../components/utils/json_serialize_enum.hpp | 25 +++++++++++++++++++ 2 files changed, 31 insertions(+) create mode 100644 components/utils/wmtk/components/utils/json_serialize_enum.hpp diff --git a/components/utils/wmtk/components/utils/json_macros.hpp b/components/utils/wmtk/components/utils/json_macros.hpp index 45725f786a..546c7db2bc 100644 --- a/components/utils/wmtk/components/utils/json_macros.hpp +++ b/components/utils/wmtk/components/utils/json_macros.hpp @@ -7,6 +7,11 @@ friend void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t);\ friend void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t); +// place these in the class for which serialization is desired +#define WMTK_NLOHMANN_JSON_DECLARATION(Type)\ + void to_json(nlohmann::json& nlohmann_json_j, const Type& nlohmann_json_t);\ + void from_json(const nlohmann::json& nlohmann_json_j, Type& nlohmann_json_t); + // place this to define the prototype of the to_json function #define WMTK_NLOHMANN_JSON_FRIEND_TO_JSON_PROTOTYPE(Type)\ @@ -22,3 +27,4 @@ { NLOHMANN_JSON_EXPAND(NLOHMANN_JSON_PASTE(NLOHMANN_JSON_FROM, __VA_ARGS__)) } + diff --git a/components/utils/wmtk/components/utils/json_serialize_enum.hpp b/components/utils/wmtk/components/utils/json_serialize_enum.hpp new file mode 100644 index 0000000000..ac4bf6c76f --- /dev/null +++ b/components/utils/wmtk/components/utils/json_serialize_enum.hpp @@ -0,0 +1,25 @@ +#pragma once +// copy of nlohmann's json serialize enum (include/nlohmann/detail/macro_scope.hpp), but without inlining +#define WMTK_NLOHMANN_JSON_SERIALIZE_ENUM(ENUM_TYPE, ...) \ + void to_json(nlohmann::json& j, const ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [e](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.first == e; \ + }); \ + j = ((it != std::end(m)) ? it : std::begin(m))->second; \ + } \ + void from_json(const nlohmann::json& j, ENUM_TYPE& e) \ + { \ + static_assert(std::is_enum::value, #ENUM_TYPE " must be an enum!"); \ + static const std::pair m[] = __VA_ARGS__; \ + auto it = std::find_if(std::begin(m), std::end(m), \ + [&j](const std::pair& ej_pair) -> bool \ + { \ + return ej_pair.second == j; \ + }); \ + e = ((it != std::end(m)) ? it : std::begin(m))->first; \ + } From 78007f5df59196469772320474754e53b73e440b Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sat, 23 Nov 2024 17:38:16 -0500 Subject: [PATCH 8/9] adding get_attribute with tools for finding missing attributes --- .../multimesh/utils/AttributeDescription.cpp | 34 +++- .../multimesh/utils/AttributeDescription.hpp | 21 +- .../multimesh/utils/get_attribute.cpp | 116 ++++++++++- .../multimesh/utils/get_attribute.hpp | 9 +- components/multimesh/tests/CMakeLists.txt | 7 +- components/multimesh/tests/get_attributes.cpp | 188 +++++++++++++++++ .../multimesh/tests/named_multimesh.cpp | 191 +----------------- components/multimesh/tests/utils.cpp | 33 +++ components/multimesh/tests/utils.hpp | 9 + src/wmtk/utils/primitive_range_iter.hpp | 3 +- 10 files changed, 401 insertions(+), 210 deletions(-) create mode 100644 components/multimesh/tests/get_attributes.cpp create mode 100644 components/multimesh/tests/utils.cpp create mode 100644 components/multimesh/tests/utils.hpp diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp index 9e662097da..e254258035 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp @@ -1,10 +1,11 @@ #include "AttributeDescription.hpp" #include +#include namespace wmtk::attribute { // TODO: this definitely will cause a conflict someday if someone else wants to serialize // attributetype -NLOHMANN_JSON_SERIALIZE_ENUM( +WMTK_NLOHMANN_JSON_SERIALIZE_ENUM( AttributeType, { {AttributeType::Char, "char"}, @@ -31,18 +32,39 @@ NLOHMANN_JSON_SERIALIZE_ENUM( } // namespace WMTK_NLOHMANN_JSON_FRIEND_TO_JSON_PROTOTYPE(AttributeDescription) { - WMTK_NLOHMANN_ASSIGN_TYPE_TO_JSON(path, dimension, type); + WMTK_NLOHMANN_ASSIGN_TYPE_TO_JSON(path); + if (nlohmann_json_t.dimension.has_value()) { + nlohmann_json_j["dimension"] = nlohmann_json_t.dimension.value(); + } + if (nlohmann_json_t.type.has_value()) { + nlohmann_json_j["type"] = nlohmann_json_t.type.value(); + } } WMTK_NLOHMANN_JSON_FRIEND_FROM_JSON_PROTOTYPE(AttributeDescription) { - WMTK_NLOHMANN_ASSIGN_TYPE_FROM_JSON(path, dimension, type); + if (nlohmann_json_j.is_string()) { + nlohmann_json_t.path = nlohmann_json_j.get(); + } else { + nlohmann_json_t.path = nlohmann_json_j["path"]; + } + if (nlohmann_json_j.contains("dimension")) { + nlohmann_json_t.dimension = nlohmann_json_j["dimension"]; + } + if (nlohmann_json_j.contains("type")) { + nlohmann_json_t.type = nlohmann_json_j["type"]; + } } -PrimitiveType AttributeDescription::primitive_type() const +std::optional AttributeDescription::primitive_type() const { - assert(dimension < 4); + if (this->dimension.has_value()) { + int8_t d = this->dimension.value(); + assert(d < 4); - return get_primitive_type_from_id(dimension); + return get_primitive_type_from_id(d); + } else { + return {}; + } } } // namespace wmtk::components::multimesh::utils diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp index 05b0811889..ce9675389d 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp @@ -1,9 +1,14 @@ #pragma once #include +#include #include #include #include +namespace wmtk::attribute { +WMTK_NLOHMANN_JSON_DECLARATION(AttributeType) +} + namespace wmtk::components::multimesh::utils { @@ -11,18 +16,18 @@ namespace wmtk::components::multimesh::utils { struct AttributeDescription { // Avoiding defining any constructors to enable aggregate construction - //AttributeDescription() = default; - //AttributeDescription(const AttributeDescription&) = default; - //AttributeDescription(AttributeDescription&&) = default; - //AttributeDescription& operator=(const AttributeDescription&) = default; - //AttributeDescription& operator=(AttributeDescription&&) = default; + // AttributeDescription() = default; + // AttributeDescription(const AttributeDescription&) = default; + // AttributeDescription(AttributeDescription&&) = default; + // AttributeDescription& operator=(const AttributeDescription&) = default; + // AttributeDescription& operator=(AttributeDescription&&) = default; //~AttributeDescription() = default; std::string path; - uint8_t dimension; // internally the primitive type - attribute::AttributeType type; + std::optional dimension; + std::optional type; - PrimitiveType primitive_type() const; + std::optional primitive_type() const; auto operator<=>(const AttributeDescription&) const -> std::strong_ordering; auto operator==(const AttributeDescription&) const -> bool; diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp index 984ea7542d..e948cc9611 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -2,6 +2,7 @@ #include "get_attribute.hpp" #include #include +#include #include "..//MeshCollection.hpp" #include "../NamedMultiMesh.hpp" #include "AttributeDescription.hpp" @@ -9,6 +10,68 @@ #include "wmtk/utils/Logger.hpp" namespace wmtk::components::multimesh::utils { +class attribute_missing_error : public std::range_error +{ +public: + static std::string make_message( + std::string path, + std::optional dimension, + std::optional type) + { + std::string typestr; + if (type.has_value()) { + nlohmann::json j; + j = type.value(); // just using the fact we can generate a strnig for this using json + // to get a printable string + typestr = j; + } + if (type.has_value() && dimension.has_value()) { + return fmt::format( + "Could not find attribute named {} on {}-simplices of type {}", + path, + dimension.value(), + typestr); + } else if (type.has_value()) { + return fmt::format( + "Could not find attribute named {} on {}-simplices", + path, + dimension.value()); + } else if (dimension.has_value()) { + return fmt::format("Could not find attribute named {} of type {}", path, typestr); + } else { + return fmt::format("Could not find attribute named {}", path); + } + } + static std::string make_message(const AttributeDescription& ad) + { + return make_message(ad.path, ad.dimension, ad.type); + } + + attribute_missing_error(const std::string_view& message, const AttributeDescription& ad) + : std::range_error(std::string(message)) + , description(ad) + {} + attribute_missing_error(const AttributeDescription& ad) + : attribute_missing_error(make_message(ad), ad) + {} + attribute_missing_error( + const std::string_view& path, + const std::optional& dimension, + const std::optional& type) + : attribute_missing_error(AttributeDescription(std::string(path), dimension, type)) + {} + attribute_missing_error( + const std::string_view& path, + const std::optional& pt, + const std::optional& type) + : attribute_missing_error( + path, + pt.has_value() ? std::optional{wmtk::get_primitive_type_id(pt.value())} + : std::optional{}, + type) + {} + AttributeDescription description; +}; namespace detail { @@ -66,7 +129,14 @@ template wmtk::attribute::MeshAttributeHandle get_attribute(const Mesh& mesh, const std::string_view& name, PrimitiveType pt) { - return mesh.get_attribute_handle(std::string(name), pt); + if (mesh.has_attribute(std::string(name), pt)) { + return mesh.get_attribute_handle(std::string(name), pt); + } else { + throw attribute_missing_error( + std::string(name), + wmtk::get_primitive_type_id(pt), + wmtk::attribute ::attribute_type_enum_from_type()); + } } wmtk::attribute::MeshAttributeHandle get_attribute( @@ -92,6 +162,50 @@ wmtk::attribute::MeshAttributeHandle get_attribute( } return {}; } +wmtk::attribute::MeshAttributeHandle get_attribute( + const Mesh& mesh, + const std::string_view& name, + std::optional pt, + std::optional type) +{ + using AT = attribute::AttributeType; + // This order matches wmtk::components::utils::get_attributes + constexpr static std::array types{{AT::Char, AT::Int64, AT::Double, AT::Rational}}; + // constexpr static std::array types{{AT::Int64, AT::Double, AT::Char, AT::Rational}}; + if (pt.has_value() && type.has_value()) { + return get_attribute(mesh, name, pt.value(), type.value()); + } else if (pt.has_value()) { + for (AT at : types) { + try { + return get_attribute(mesh, name, pt.value(), at); + } catch (const attribute_missing_error& e) { + continue; + } + } + } else if (type.has_value()) { + for (PrimitiveType p : wmtk::utils::primitive_below(mesh.top_simplex_type())) { + try { + return get_attribute(mesh, name, p, type.value()); + } catch (const attribute_missing_error& e) { + continue; + } + } + } else { + for (AT at : types) { + for (PrimitiveType p : wmtk::utils::primitive_below(mesh.top_simplex_type())) { + try { + return get_attribute(mesh, name, p, at); + } catch (const attribute_missing_error& e) { + continue; + } + } + } + } + + + throw attribute_missing_error(name, pt, type); + return {}; +} } // namespace detail wmtk::attribute::MeshAttributeHandle get_attribute( diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp index 6780e82bb3..2d2b7df48b 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.hpp @@ -7,7 +7,7 @@ class Mesh; namespace components::multimesh { class NamedMultiMesh; class MeshCollection; -} +} // namespace components::multimesh } // namespace wmtk namespace wmtk::components::multimesh { @@ -21,6 +21,9 @@ struct AttributeDescription; // * simplex (int, dimension attribute belongs to // if type and simplex are missing code will search in lexicographical of (primitive_type, type) // where double < int < char < rational for type +// +// this is somewhat redundant to wmtk::components::utils::get_attributes, but is designed around +// supporting multimesh paths. wmtk::attribute::MeshAttributeHandle get_attribute( const NamedMultiMesh& mesh, @@ -32,5 +35,5 @@ wmtk::attribute::MeshAttributeHandle get_attribute( const Mesh& mesh, const AttributeDescription& description); -} -} // namespace wmtk::components::multimesh::utils +} // namespace utils +} // namespace wmtk::components::multimesh diff --git a/components/multimesh/tests/CMakeLists.txt b/components/multimesh/tests/CMakeLists.txt index 919b8f4eda..e25795edf4 100644 --- a/components/multimesh/tests/CMakeLists.txt +++ b/components/multimesh/tests/CMakeLists.txt @@ -1,4 +1,9 @@ -add_component_test(wmtk::${COMPONENT_NAME} named_multimesh.cpp) +add_component_test(wmtk::${COMPONENT_NAME} + named_multimesh.cpp + get_attributes.cpp + utils.hpp + utils.cpp +) target_link_libraries(${WMTK_COMPONENT_TEST_TARGET} PRIVATE wmtk::input) diff --git a/components/multimesh/tests/get_attributes.cpp b/components/multimesh/tests/get_attributes.cpp new file mode 100644 index 0000000000..ab3a4e70b9 --- /dev/null +++ b/components/multimesh/tests/get_attributes.cpp @@ -0,0 +1,188 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wmtk/components/multimesh/MeshCollection.hpp" + +#include "utils.hpp" + +using json = nlohmann::json; + +TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") +{ + { + auto m = make_mesh(); + wmtk::components::multimesh::MeshCollection mc; + wmtk::components::multimesh::NamedMultiMesh& named_mm = + mc.emplace_mesh(*m, std::string("roo")); + + auto double_test_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); + auto char_test_handle = + m->register_attribute("char_test", wmtk::PrimitiveType::Vertex, 1); + auto int_test_handle = + m->register_attribute("int_test", wmtk::PrimitiveType::Vertex, 1); + auto rational_test_handle = + m->register_attribute("rational_test", wmtk::PrimitiveType::Vertex, 1); + + using AT = wmtk::attribute::AttributeType; + +using AD = wmtk::components::multimesh::utils::AttributeDescription; + { // double check that path extraction is working + std::vector double_ads; + double_ads.emplace_back(AD{"double_test", 0, AT::Double}); + double_ads.emplace_back(AD{"/double_test", 0, AT::Double}); + double_ads.emplace_back(AD{"roo/double_test", 0, AT::Double}); + + + for (const auto& ad : double_ads) { + auto h = wmtk::components::multimesh::utils::get_attribute(named_mm, ad); + CHECK(double_test_handle == h); + // just on mesh also works + auto h2 = wmtk::components::multimesh::utils::get_attribute(*m, ad); + CHECK(double_test_handle == h2); + //// meshcollection too + auto h3 = wmtk::components::multimesh::utils::get_attribute(mc, ad); + CHECK(double_test_handle == h3); + } + } + { // check that other types work + CHECK( + rational_test_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"rational_test", 0, AT::Rational})); + CHECK( + int_test_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"int_test", 0, AT::Int64})); + CHECK( + char_test_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"char_test", 0, AT::Char})); + } + { // check that other simplex types work + auto edge_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Edge, 1); + auto tri_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Triangle, 1); + CHECK( + edge_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"double_test", 1, AT::Double})); + CHECK( + tri_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"double_test", 2, AT::Double})); + // TODO: lazy about testing tet + } + } + + + { + using AD = wmtk::components::multimesh::utils::AttributeDescription; + auto m = make_mesh(); + auto children = make_child(*m, {0}); + REQUIRE(children.size() == 1); + auto child = children[0]; + + + wmtk::components::multimesh::NamedMultiMesh named_mm; + named_mm.set_mesh(*m); + { + nlohmann::json js; + js["roo"] = nlohmann::json::array({"child"}); + named_mm.set_names(js); + } + CHECK(std::vector{} == named_mm.get_id("roo")); + CHECK(std::vector{0} == named_mm.get_id("roo.child")); + auto attr_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); + auto child_attr_handle = + child->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); + using AT = wmtk::attribute::AttributeType; + CHECK( + attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"double_test", 0, AT::Double})); + CHECK( + attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"/double_test", 0, AT::Double})); + CHECK( + attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"roo/double_test", 0, AT::Double})); + CHECK( + child_attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"roo.child/double_test", 0, AT::Double})); + CHECK( + child_attr_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{".child/double_test", 0, AT::Double})); + } +} + +TEST_CASE("multimesh_attribute_description_json", "[components][multimesh]") +{ + using AT = wmtk::attribute::AttributeType; + using AD = wmtk::components::multimesh::utils::AttributeDescription; + using JS = nlohmann::json; + auto check = [](const AD& ad, const JS& js) { + JS js2 = ad; + auto ad2 = js.get(); + CHECK(js2 == js); + CHECK(ad == js.get()); + }; + { + AD ad{"double_test", 0, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 0}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"rational_test", 0, AT::Rational}; + JS js{{"path", "rational_test"}, {"dimension", 0}, {"type", "rational"}}; + check(ad, js); + } + { + AD ad{"int_test", 0, AT::Int64}; + JS js{{"path", "int_test"}, {"dimension", 0}, {"type", "int"}}; + check(ad, js); + } + { + AD ad{"char_test", 0, AT::Char}; + JS js{{"path", "char_test"}, {"dimension", 0}, {"type", "char"}}; + check(ad, js); + } + { + AD ad{"double_test", 1, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 1}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"double_test", 2, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 2}, {"type", "double"}}; + check(ad, js); + } + + { + AD ad{"double_test", {}, AT::Double}; + JS js{{"path", "double_test"}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"double_test", 2, {}}; + JS js{{"path", "double_test"}, {"dimension", 2}}; + check(ad, js); + } + { + AD ad{"double_test", {}, {}}; + JS js{{"path", "double_test"}}; + check(ad, js); + } +} diff --git a/components/multimesh/tests/named_multimesh.cpp b/components/multimesh/tests/named_multimesh.cpp index 0f6c05b723..cc8e34a761 100644 --- a/components/multimesh/tests/named_multimesh.cpp +++ b/components/multimesh/tests/named_multimesh.cpp @@ -7,45 +7,12 @@ #include #include #include -#include "tools/TriMesh_examples.hpp" -#include "wmtk/components/multimesh/MeshCollection.hpp" -#include +#include "utils.hpp" using json = nlohmann::json; -namespace { -const std::filesystem::path data_dir = WMTK_DATA_DIR; -auto make_mesh() -{ - return wmtk::tests::disk(5); -} - -auto make_child(wmtk::Mesh& m, const std::vector& path) - -> std::vector> -{ - if (path.size() == 0) { - // multimesh root mesh already exists so nothing to be done - return {}; - } - std::vector> meshes; - for (size_t j = 0; j < path.size(); ++j) { - std::vector p(path.begin(), path.begin() + j); - auto& cur_mesh = m.get_multi_mesh_mesh(p); - int64_t child_index = path[j]; - const auto child_meshes = cur_mesh.get_child_meshes(); - for (int64_t index = child_meshes.size(); index <= child_index; ++index) { - auto new_mesh = make_mesh(); - auto map = wmtk::multimesh::same_simplex_dimension_bijection(cur_mesh, *new_mesh); - - cur_mesh.register_child_mesh(new_mesh, map); - meshes.emplace_back(new_mesh); - } - } - return meshes; -} -} // namespace TEST_CASE("named_multimesh_parse", "[components][multimesh]") @@ -137,159 +104,3 @@ TEST_CASE("named_multimesh_parse", "[components][multimesh]") } } -TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") -{ - { - auto m = make_mesh(); - wmtk::components::multimesh::MeshCollection mc; - wmtk::components::multimesh::NamedMultiMesh& named_mm = - mc.emplace_mesh(*m, std::string("roo")); - - auto double_test_handle = - m->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); - auto char_test_handle = - m->register_attribute("char_test", wmtk::PrimitiveType::Vertex, 1); - auto int_test_handle = - m->register_attribute("int_test", wmtk::PrimitiveType::Vertex, 1); - auto rational_test_handle = - m->register_attribute("rational_test", wmtk::PrimitiveType::Vertex, 1); - - using AT = wmtk::attribute::AttributeType; - -using AD = wmtk::components::multimesh::utils::AttributeDescription; - { // double check that path extraction is working - std::vector double_ads; - double_ads.emplace_back(AD{"double_test", 0, AT::Double}); - double_ads.emplace_back(AD{"/double_test", 0, AT::Double}); - double_ads.emplace_back(AD{"roo/double_test", 0, AT::Double}); - - - for (const auto& ad : double_ads) { - auto h = wmtk::components::multimesh::utils::get_attribute(named_mm, ad); - CHECK(double_test_handle == h); - // just on mesh also works - auto h2 = wmtk::components::multimesh::utils::get_attribute(*m, ad); - CHECK(double_test_handle == h2); - //// meshcollection too - auto h3 = wmtk::components::multimesh::utils::get_attribute(mc, ad); - CHECK(double_test_handle == h3); - } - } - { // check that other types work - CHECK( - rational_test_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"rational_test", 0, AT::Rational})); - CHECK( - int_test_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"int_test", 0, AT::Int64})); - CHECK( - char_test_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"char_test", 0, AT::Char})); - } - { // check that other simplex types work - auto edge_handle = - m->register_attribute("double_test", wmtk::PrimitiveType::Edge, 1); - auto tri_handle = - m->register_attribute("double_test", wmtk::PrimitiveType::Triangle, 1); - CHECK( - edge_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"double_test", 1, AT::Double})); - CHECK( - tri_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"double_test", 2, AT::Double})); - // TODO: lazy about testing tet - } - } - - - { - using AD = wmtk::components::multimesh::utils::AttributeDescription; - auto m = make_mesh(); - auto children = make_child(*m, {0}); - REQUIRE(children.size() == 1); - auto child = children[0]; - - - wmtk::components::multimesh::NamedMultiMesh named_mm; - named_mm.set_mesh(*m); - { - nlohmann::json js; - js["roo"] = nlohmann::json::array({"child"}); - named_mm.set_names(js); - } - CHECK(std::vector{} == named_mm.get_id("roo")); - CHECK(std::vector{0} == named_mm.get_id("roo.child")); - auto attr_handle = - m->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); - auto child_attr_handle = - child->register_attribute("double_test", wmtk::PrimitiveType::Vertex, 1); - using AT = wmtk::attribute::AttributeType; - CHECK( - attr_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"double_test", 0, AT::Double})); - CHECK( - attr_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"/double_test", 0, AT::Double})); - CHECK( - attr_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"roo/double_test", 0, AT::Double})); - CHECK( - child_attr_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{"roo.child/double_test", 0, AT::Double})); - CHECK( - child_attr_handle == wmtk::components::multimesh::utils::get_attribute( - named_mm, - AD{".child/double_test", 0, AT::Double})); - } -} - -TEST_CASE("multimesh_attribute_description_json", "[components][multimesh]") -{ - using AT = wmtk::attribute::AttributeType; - using AD = wmtk::components::multimesh::utils::AttributeDescription; - using JS = nlohmann::json; - auto check = [](const AD& ad, const JS& js) { - JS js2 = ad; - CHECK(js2 == js); - CHECK(ad == js.get()); - }; - { - AD ad{"double_test", 0, AT::Double}; - JS js{{"path", "double_test"}, {"dimension", 0}, {"type", "double"}}; - check(ad, js); - } - { - AD ad{"rational_test", 0, AT::Rational}; - JS js{{"path", "rational_test"}, {"dimension", 0}, {"type", "rational"}}; - check(ad, js); - } - { - AD ad{"int_test", 0, AT::Int64}; - JS js{{"path", "int_test"}, {"dimension", 0}, {"type", "int"}}; - check(ad, js); - } - { - AD ad{"char_test", 0, AT::Char}; - JS js{{"path", "char_test"}, {"dimension", 0}, {"type", "char"}}; - check(ad, js); - } - { - AD ad{"double_test", 1, AT::Double}; - JS js{{"path", "double_test"}, {"dimension", 1}, {"type", "double"}}; - check(ad, js); - } - { - AD ad{"double_test", 2, AT::Double}; - JS js{{"path", "double_test"}, {"dimension", 2}, {"type", "double"}}; - check(ad, js); - } -} diff --git a/components/multimesh/tests/utils.cpp b/components/multimesh/tests/utils.cpp new file mode 100644 index 0000000000..119ce0bc1c --- /dev/null +++ b/components/multimesh/tests/utils.cpp @@ -0,0 +1,33 @@ +#include +#include "tools/TriMesh_examples.hpp" +#include "utils.hpp" +#include + +std::shared_ptr make_mesh() +{ + return wmtk::tests::disk(5); +} + +auto make_child(wmtk::Mesh& m, const std::vector& path) + -> std::vector> +{ + if (path.size() == 0) { + // multimesh root mesh already exists so nothing to be done + return {}; + } + std::vector> meshes; + for (size_t j = 0; j < path.size(); ++j) { + std::vector p(path.begin(), path.begin() + j); + auto& cur_mesh = m.get_multi_mesh_mesh(p); + int64_t child_index = path[j]; + const auto child_meshes = cur_mesh.get_child_meshes(); + for (int64_t index = child_meshes.size(); index <= child_index; ++index) { + auto new_mesh = make_mesh(); + auto map = wmtk::multimesh::same_simplex_dimension_bijection(cur_mesh, *new_mesh); + + cur_mesh.register_child_mesh(new_mesh, map); + meshes.emplace_back(new_mesh); + } + } + return meshes; +} diff --git a/components/multimesh/tests/utils.hpp b/components/multimesh/tests/utils.hpp new file mode 100644 index 0000000000..cf8f747adf --- /dev/null +++ b/components/multimesh/tests/utils.hpp @@ -0,0 +1,9 @@ +#pragma once +#include +#include +namespace wmtk { + class Mesh; +} +std::shared_ptr make_mesh(); +auto make_child(wmtk::Mesh& m, const std::vector& path) + -> std::vector>; diff --git a/src/wmtk/utils/primitive_range_iter.hpp b/src/wmtk/utils/primitive_range_iter.hpp index f3c4351ef8..e7424076af 100644 --- a/src/wmtk/utils/primitive_range_iter.hpp +++ b/src/wmtk/utils/primitive_range_iter.hpp @@ -13,6 +13,7 @@ template < bool Inverted = (Start > End)> class PrimitiveTypeRange { + public: using integral_type = std::underlying_type_t; class iterator { @@ -103,7 +104,7 @@ auto primitive_above() } // returns a vector of primitives including the endpoint template -primitive_below() +auto primitive_below() { constexpr static PrimitiveType Start = PrimitiveType::Vertex; using integral_type = std::underlying_type_t; From 89002a709dba0eac5b68236fd7ff43c3cab4a9a5 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Sun, 24 Nov 2024 21:35:07 -0500 Subject: [PATCH 9/9] adding errors and tests to errors when finding attributes --- .../wmtk/components/multimesh/CMakeLists.txt | 8 + .../multimesh/utils/AttributeDescription.hpp | 37 +++- .../detail/attribute_ambiguous_error.cpp | 23 ++ .../detail/attribute_ambiguous_error.hpp | 32 +++ .../utils/detail/attribute_error.hpp | 20 ++ .../utils/detail/attribute_missing_error.cpp | 9 + .../utils/detail/attribute_missing_error.hpp | 24 +++ .../utils/detail/named_error_text.cpp | 36 ++++ .../utils/detail/named_error_text.hpp | 14 ++ .../multimesh/utils/get_attribute.cpp | 104 +++------ components/multimesh/tests/get_attributes.cpp | 197 +++++++++++------- 11 files changed, 350 insertions(+), 154 deletions(-) create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.cpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.hpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_error.hpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.cpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.hpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.cpp create mode 100644 components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.hpp diff --git a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt index 1bb7d19dc7..95fe98209e 100644 --- a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt +++ b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt @@ -33,6 +33,14 @@ set(SRC_FILES utils/get_attribute.hpp utils/get_attribute.cpp + utils/detail/attribute_error.hpp + + utils/detail/named_error_text.hpp + utils/detail/named_error_text.cpp + utils/detail/attribute_ambiguous_error.hpp + utils/detail/attribute_ambiguous_error.cpp + utils/detail/attribute_missing_error.hpp + utils/detail/attribute_missing_error.cpp ) include(stb) diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp index ce9675389d..a3ff54fb10 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp @@ -15,18 +15,39 @@ namespace wmtk::components::multimesh::utils { // the minimal information to uniquely extract an attribute handle struct AttributeDescription { - // Avoiding defining any constructors to enable aggregate construction - // AttributeDescription() = default; - // AttributeDescription(const AttributeDescription&) = default; - // AttributeDescription(AttributeDescription&&) = default; - // AttributeDescription& operator=(const AttributeDescription&) = default; - // AttributeDescription& operator=(AttributeDescription&&) = default; - //~AttributeDescription() = default; - std::string path; std::optional dimension; std::optional type; + + AttributeDescription() = default; + AttributeDescription(const AttributeDescription&) = default; + AttributeDescription(AttributeDescription&&) = default; + AttributeDescription& operator=(const AttributeDescription&) = default; + AttributeDescription& operator=(AttributeDescription&&) = default; + ~AttributeDescription() = default; + + AttributeDescription( + const std::string_view& p, + const std::optional& dim, + const std::optional& t) + : path(p) + , dimension(dim) + , type(t) + {} + + explicit AttributeDescription( + const std::string_view& p, + const std::optional& pt, + const std::optional& t) + : AttributeDescription( + p, + pt.has_value() ? std::optional{wmtk::get_primitive_type_id(pt.value())} + : std::optional{}, + t) + {} + + std::optional primitive_type() const; auto operator<=>(const AttributeDescription&) const -> std::strong_ordering; diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.cpp new file mode 100644 index 0000000000..3e6949cda1 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.cpp @@ -0,0 +1,23 @@ + +#include "attribute_ambiguous_error.hpp" +#include +#include +#include +#include +#include "named_error_text.hpp" +namespace wmtk::components::multimesh::utils::detail { + +std::string attribute_ambiguous_error::make_message( + const AttributeDescription& ad, + const std::vector& possibilities) +{ + std::vector names; + + std::string (*maker)(const AttributeDescription&) = make_named_error_string; + std::transform(possibilities.begin(), possibilities.end(), std::back_inserter(names), maker); + return fmt::format( + "Multiple options for an attribute {} found: [{}]", + make_named_error_string(ad), + fmt::join(names, ",")); +} +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.hpp new file mode 100644 index 0000000000..a159ae503b --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_ambiguous_error.hpp @@ -0,0 +1,32 @@ +#pragma once +#include "attribute_error.hpp" +namespace wmtk::components::multimesh::utils { +struct AttributeDescription; +} + +namespace wmtk::components::multimesh::utils::detail { + +class attribute_ambiguous_error : public attribute_error +{ +public: + static std::string make_message( + const AttributeDescription& description, + const std::vector& possibilities); + attribute_ambiguous_error( + const AttributeDescription& d, + const std::vector& possibilities) + : attribute_error(make_message(d, possibilities), d) + {} + template + static attribute_ambiguous_error make( + const std::vector& possiblities, + Args&&... args) + { + return attribute_ambiguous_error( + AttributeDescription{std::forward(args)...}, + possiblities); + } +}; + + +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_error.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_error.hpp new file mode 100644 index 0000000000..197679b4d5 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_error.hpp @@ -0,0 +1,20 @@ +#pragma once +#include +#include +#include +#include +#include "../AttributeDescription.hpp" + +namespace wmtk::components::multimesh::utils::detail { + +class attribute_error : public std::range_error +{ +public: + template + attribute_error(const std::string_view& message, Args&&... args) + : std::range_error(std::string(message)) + , description(std::forward(args)...) + {} + AttributeDescription description; +}; +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.cpp new file mode 100644 index 0000000000..67679c186c --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.cpp @@ -0,0 +1,9 @@ +#include "attribute_missing_error.hpp" +#include "named_error_text.hpp" +namespace wmtk::components::multimesh::utils::detail { + +std::string attribute_missing_error::make_message(const AttributeDescription& ad) +{ + return "Could not find attribute " + make_named_error_string(ad); +} +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.hpp new file mode 100644 index 0000000000..3acae69764 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/attribute_missing_error.hpp @@ -0,0 +1,24 @@ +#pragma once +#include "attribute_error.hpp" +namespace wmtk::components::multimesh::utils { +struct AttributeDescription; +} + +namespace wmtk::components::multimesh::utils::detail { + +class attribute_missing_error : public attribute_error +{ +public: + static std::string make_message(const AttributeDescription& description); + attribute_missing_error(const AttributeDescription& d) + : attribute_error(make_message(d), d) + {} + template + static attribute_missing_error make(Args&&... args) + { + return attribute_missing_error(AttributeDescription{std::forward(args)...}); + } +}; + + +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.cpp new file mode 100644 index 0000000000..83bad2c8e1 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.cpp @@ -0,0 +1,36 @@ + +#include "named_error_text.hpp" +#include +#include +#include +#include +#include +#include "../AttributeDescription.hpp" +namespace wmtk::components::multimesh::utils::detail { +std::string make_named_error_string( + const std::string_view& path, + const std::optional& dimension, + const std::optional& type) +{ + std::string typestr; + if (type.has_value()) { + nlohmann::json j; + j = type.value(); // just using the fact we can generate a strnig for this using json + // to get a printable string + typestr = j; + } + if (type.has_value() && dimension.has_value()) { + return fmt::format("named {} on {}-simplices of type {}", path, dimension.value(), typestr); + } else if (dimension.has_value()) { + return fmt::format("named {} on {}-simplices", path, dimension.value()); + } else if (type.has_value()) { + return fmt::format("named {} of type {}", path, typestr); + } else { + return fmt::format("named {}", path); + } +} +std::string make_named_error_string(const AttributeDescription& ad) +{ + return make_named_error_string(ad.path, ad.dimension, ad.type); +} +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.hpp b/components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.hpp new file mode 100644 index 0000000000..3159eccfcd --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/detail/named_error_text.hpp @@ -0,0 +1,14 @@ +#pragma once +#include +#include +#include +namespace wmtk::components::multimesh::utils { +struct AttributeDescription; +} +namespace wmtk::components::multimesh::utils::detail { +std::string make_named_error_string( + const std::string_view& path, + const std::optional& dimension, + const std::optional& type); +std::string make_named_error_string(const AttributeDescription& ad); +} // namespace wmtk::components::multimesh::utils::detail diff --git a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp index e948cc9611..90d14a3d92 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -6,74 +6,13 @@ #include "..//MeshCollection.hpp" #include "../NamedMultiMesh.hpp" #include "AttributeDescription.hpp" +#include "detail/attribute_ambiguous_error.hpp" +#include "detail/attribute_missing_error.hpp" +#include "detail/named_error_text.hpp" #include "get_attribute.hpp" #include "wmtk/utils/Logger.hpp" namespace wmtk::components::multimesh::utils { -class attribute_missing_error : public std::range_error -{ -public: - static std::string make_message( - std::string path, - std::optional dimension, - std::optional type) - { - std::string typestr; - if (type.has_value()) { - nlohmann::json j; - j = type.value(); // just using the fact we can generate a strnig for this using json - // to get a printable string - typestr = j; - } - if (type.has_value() && dimension.has_value()) { - return fmt::format( - "Could not find attribute named {} on {}-simplices of type {}", - path, - dimension.value(), - typestr); - } else if (type.has_value()) { - return fmt::format( - "Could not find attribute named {} on {}-simplices", - path, - dimension.value()); - } else if (dimension.has_value()) { - return fmt::format("Could not find attribute named {} of type {}", path, typestr); - } else { - return fmt::format("Could not find attribute named {}", path); - } - } - static std::string make_message(const AttributeDescription& ad) - { - return make_message(ad.path, ad.dimension, ad.type); - } - - attribute_missing_error(const std::string_view& message, const AttributeDescription& ad) - : std::range_error(std::string(message)) - , description(ad) - {} - attribute_missing_error(const AttributeDescription& ad) - : attribute_missing_error(make_message(ad), ad) - {} - attribute_missing_error( - const std::string_view& path, - const std::optional& dimension, - const std::optional& type) - : attribute_missing_error(AttributeDescription(std::string(path), dimension, type)) - {} - attribute_missing_error( - const std::string_view& path, - const std::optional& pt, - const std::optional& type) - : attribute_missing_error( - path, - pt.has_value() ? std::optional{wmtk::get_primitive_type_id(pt.value())} - : std::optional{}, - type) - {} - AttributeDescription description; -}; - - namespace detail { // turns an attribute path mesh.path/attrname to mesh.path attrname // std::array @@ -132,7 +71,7 @@ get_attribute(const Mesh& mesh, const std::string_view& name, PrimitiveType pt) if (mesh.has_attribute(std::string(name), pt)) { return mesh.get_attribute_handle(std::string(name), pt); } else { - throw attribute_missing_error( + throw attribute_missing_error::make( std::string(name), wmtk::get_primitive_type_id(pt), wmtk::attribute ::attribute_type_enum_from_type()); @@ -143,9 +82,9 @@ wmtk::attribute::MeshAttributeHandle get_attribute( const Mesh& mesh, const std::string_view& name, PrimitiveType pt, - attribute::AttributeType type) + wmtk::attribute::AttributeType type) { - using AT = attribute::AttributeType; + using AT = wmtk::attribute::AttributeType; switch (type) { #define ENTRY(TYPE) \ case TYPE: \ @@ -166,18 +105,27 @@ wmtk::attribute::MeshAttributeHandle get_attribute( const Mesh& mesh, const std::string_view& name, std::optional pt, - std::optional type) + std::optional type) { - using AT = attribute::AttributeType; + using AT = wmtk::attribute::AttributeType; // This order matches wmtk::components::utils::get_attributes constexpr static std::array types{{AT::Char, AT::Int64, AT::Double, AT::Rational}}; // constexpr static std::array types{{AT::Int64, AT::Double, AT::Char, AT::Rational}}; + std::vector possibilities; + wmtk::attribute::MeshAttributeHandle ret; + auto add_option = [&](PrimitiveType prim, AT t) { + ret = get_attribute(mesh, name, prim, t); + + uint8_t dimension = wmtk::get_primitive_type_id(prim); + possibilities.emplace_back(AttributeDescription{name, dimension, t}); + }; if (pt.has_value() && type.has_value()) { - return get_attribute(mesh, name, pt.value(), type.value()); + add_option(pt.value(), type.value()); + } else if (pt.has_value()) { for (AT at : types) { try { - return get_attribute(mesh, name, pt.value(), at); + add_option(pt.value(), at); } catch (const attribute_missing_error& e) { continue; } @@ -185,7 +133,7 @@ wmtk::attribute::MeshAttributeHandle get_attribute( } else if (type.has_value()) { for (PrimitiveType p : wmtk::utils::primitive_below(mesh.top_simplex_type())) { try { - return get_attribute(mesh, name, p, type.value()); + add_option(p, type.value()); } catch (const attribute_missing_error& e) { continue; } @@ -194,7 +142,7 @@ wmtk::attribute::MeshAttributeHandle get_attribute( for (AT at : types) { for (PrimitiveType p : wmtk::utils::primitive_below(mesh.top_simplex_type())) { try { - return get_attribute(mesh, name, p, at); + add_option(p, at); } catch (const attribute_missing_error& e) { continue; } @@ -203,8 +151,14 @@ wmtk::attribute::MeshAttributeHandle get_attribute( } - throw attribute_missing_error(name, pt, type); - return {}; + if (possibilities.empty()) { + throw attribute_missing_error::make(name, pt, type); + } else if (possibilities.size() > 1) { + throw attribute_ambiguous_error::make(possibilities, name,pt,type); + } + + assert(ret.is_valid()); + return ret; } } // namespace detail diff --git a/components/multimesh/tests/get_attributes.cpp b/components/multimesh/tests/get_attributes.cpp index ab3a4e70b9..865b7290de 100644 --- a/components/multimesh/tests/get_attributes.cpp +++ b/components/multimesh/tests/get_attributes.cpp @@ -5,6 +5,8 @@ #include #include #include +#include +#include #include #include #include "wmtk/components/multimesh/MeshCollection.hpp" @@ -12,6 +14,75 @@ #include "utils.hpp" using json = nlohmann::json; +using AT = wmtk::attribute::AttributeType; + +using AD = wmtk::components::multimesh::utils::AttributeDescription; +namespace { +// hack to deal with constructor ambiguity in the AD class +auto make_AD = [](const std::string_view& name, + const std::optional& dim, + const std::optional& at) { return AD{name, dim, at}; }; + +} // namespace + +TEST_CASE("multimesh_attribute_description_json", "[components][multimesh]") +{ + using AT = wmtk::attribute::AttributeType; + using AD = wmtk::components::multimesh::utils::AttributeDescription; + using JS = nlohmann::json; + auto check = [](const AD& ad, const JS& js) { + JS js2 = ad; + auto ad2 = js.get(); + CHECK(js2 == js); + CHECK(ad == js.get()); + }; + { + AD ad{"double_test", 0, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 0}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"rational_test", 0, AT::Rational}; + JS js{{"path", "rational_test"}, {"dimension", 0}, {"type", "rational"}}; + check(ad, js); + } + { + AD ad{"int_test", 0, AT::Int64}; + JS js{{"path", "int_test"}, {"dimension", 0}, {"type", "int"}}; + check(ad, js); + } + { + AD ad{"char_test", 0, AT::Char}; + JS js{{"path", "char_test"}, {"dimension", 0}, {"type", "char"}}; + check(ad, js); + } + { + AD ad{"double_test", 1, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 1}, {"type", "double"}}; + check(ad, js); + } + { + AD ad{"double_test", 2, AT::Double}; + JS js{{"path", "double_test"}, {"dimension", 2}, {"type", "double"}}; + check(ad, js); + } + + { + auto ad = make_AD("double_test", {}, AT::Double); + JS js{{"path", "double_test"}, {"type", "double"}}; + check(ad, js); + } + { + auto ad = make_AD("double_test", 2, {}); + JS js{{"path", "double_test"}, {"dimension", 2}}; + check(ad, js); + } + { + auto ad = make_AD("double_test", {}, {}); + JS js{{"path", "double_test"}}; + check(ad, js); + } +} TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") { @@ -30,14 +101,14 @@ TEST_CASE("named_multimesh_parse_attributes", "[components][multimesh]") auto rational_test_handle = m->register_attribute("rational_test", wmtk::PrimitiveType::Vertex, 1); - using AT = wmtk::attribute::AttributeType; - -using AD = wmtk::components::multimesh::utils::AttributeDescription; { // double check that path extraction is working std::vector double_ads; - double_ads.emplace_back(AD{"double_test", 0, AT::Double}); - double_ads.emplace_back(AD{"/double_test", 0, AT::Double}); - double_ads.emplace_back(AD{"roo/double_test", 0, AT::Double}); + double_ads.emplace_back(make_AD("double_test", 0, AT::Double)); + double_ads.emplace_back(make_AD("/double_test", 0, AT::Double)); + double_ads.emplace_back(make_AD("roo/double_test", 0, AT::Double)); + double_ads.emplace_back(make_AD("double_test", {}, AT::Double)); + double_ads.emplace_back(make_AD("/double_test", 0, {})); + double_ads.emplace_back(make_AD("roo/double_test", {}, {})); for (const auto& ad : double_ads) { @@ -65,26 +136,68 @@ using AD = wmtk::components::multimesh::utils::AttributeDescription; named_mm, AD{"char_test", 0, AT::Char})); } + auto edge_handle = + m->register_attribute("double_test_e", wmtk::PrimitiveType::Edge, 1); + auto tri_handle = + m->register_attribute("double_test_f", wmtk::PrimitiveType::Triangle, 1); { // check that other simplex types work - auto edge_handle = - m->register_attribute("double_test", wmtk::PrimitiveType::Edge, 1); - auto tri_handle = - m->register_attribute("double_test", wmtk::PrimitiveType::Triangle, 1); + CHECK( edge_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - AD{"double_test", 1, AT::Double})); + make_AD("double_test_e", 1, AT::Double))); + CHECK( + tri_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + make_AD("double_test_f", 2, AT::Double))); + CHECK( + tri_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + make_AD("double_test_f", {}, AT::Double))); + CHECK( + tri_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + make_AD("double_test_f", {}, AT::Double))); CHECK( tri_handle == wmtk::components::multimesh::utils::get_attribute( named_mm, - AD{"double_test", 2, AT::Double})); + make_AD("double_test_f", {}, {}))); + CHECK( + edge_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + make_AD("double_test_e", {}, {}))); // TODO: lazy about testing tet } + { + CHECK_THROWS_AS( + wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"doub2r2le_test_e", 2, AT::Double}), + wmtk::components::multimesh::utils::detail::attribute_missing_error); + CHECK_THROWS_AS( + wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"double_test_e", 2, AT::Double}), + wmtk::components::multimesh::utils::detail::attribute_missing_error); + CHECK_THROWS_AS( + wmtk::components::multimesh::utils::get_attribute( + named_mm, + AD{"double_test_f", 1, AT::Double}), + wmtk::components::multimesh::utils::detail::attribute_missing_error); + + auto double_test_handle = + m->register_attribute("double_test", wmtk::PrimitiveType::Edge, 1); + CHECK_THROWS_AS( + wmtk::components::multimesh::utils::get_attribute( + named_mm, + make_AD("double_test", {}, AT::Double)), + wmtk::components::multimesh::utils::detail::attribute_ambiguous_error); + } } { - using AD = wmtk::components::multimesh::utils::AttributeDescription; + using AD = wmtk::components::multimesh::utils::AttributeDescription; auto m = make_mesh(); auto children = make_child(*m, {0}); REQUIRE(children.size() == 1); @@ -128,61 +241,3 @@ using AD = wmtk::components::multimesh::utils::AttributeDescription; } } -TEST_CASE("multimesh_attribute_description_json", "[components][multimesh]") -{ - using AT = wmtk::attribute::AttributeType; - using AD = wmtk::components::multimesh::utils::AttributeDescription; - using JS = nlohmann::json; - auto check = [](const AD& ad, const JS& js) { - JS js2 = ad; - auto ad2 = js.get(); - CHECK(js2 == js); - CHECK(ad == js.get()); - }; - { - AD ad{"double_test", 0, AT::Double}; - JS js{{"path", "double_test"}, {"dimension", 0}, {"type", "double"}}; - check(ad, js); - } - { - AD ad{"rational_test", 0, AT::Rational}; - JS js{{"path", "rational_test"}, {"dimension", 0}, {"type", "rational"}}; - check(ad, js); - } - { - AD ad{"int_test", 0, AT::Int64}; - JS js{{"path", "int_test"}, {"dimension", 0}, {"type", "int"}}; - check(ad, js); - } - { - AD ad{"char_test", 0, AT::Char}; - JS js{{"path", "char_test"}, {"dimension", 0}, {"type", "char"}}; - check(ad, js); - } - { - AD ad{"double_test", 1, AT::Double}; - JS js{{"path", "double_test"}, {"dimension", 1}, {"type", "double"}}; - check(ad, js); - } - { - AD ad{"double_test", 2, AT::Double}; - JS js{{"path", "double_test"}, {"dimension", 2}, {"type", "double"}}; - check(ad, js); - } - - { - AD ad{"double_test", {}, AT::Double}; - JS js{{"path", "double_test"}, {"type", "double"}}; - check(ad, js); - } - { - AD ad{"double_test", 2, {}}; - JS js{{"path", "double_test"}, {"dimension", 2}}; - check(ad, js); - } - { - AD ad{"double_test", {}, {}}; - JS js{{"path", "double_test"}}; - check(ad, js); - } -}