diff --git a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt index 226c9f200a..95fe98209e 100644 --- a/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt +++ b/components/multimesh/src/wmtk/components/multimesh/CMakeLists.txt @@ -23,12 +23,24 @@ set(SRC_FILES NamedMultiMesh.hpp NamedMultiMesh.cpp - utils/get_attribute.hpp - utils/get_attribute.cpp internal/split_path.hpp MeshCollection.hpp MeshCollection.cpp + + utils/AttributeDescription.hpp + utils/AttributeDescription.cpp + + 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/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 2dd276bf35..ed25016151 100644 --- a/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp +++ b/components/multimesh/src/wmtk/components/multimesh/NamedMultiMesh.hpp @@ -17,15 +17,22 @@ 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(); // 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/utils/AttributeDescription.cpp b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp new file mode 100644 index 0000000000..e254258035 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.cpp @@ -0,0 +1,70 @@ +#include "AttributeDescription.hpp" +#include + +#include +namespace wmtk::attribute { +// TODO: this definitely will cause a conflict someday if someone else wants to serialize +// attributetype +WMTK_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); + 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) +{ + 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"]; + } +} + +std::optional AttributeDescription::primitive_type() const +{ + if (this->dimension.has_value()) { + int8_t d = this->dimension.value(); + assert(d < 4); + + 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 new file mode 100644 index 0000000000..a3ff54fb10 --- /dev/null +++ b/components/multimesh/src/wmtk/components/multimesh/utils/AttributeDescription.hpp @@ -0,0 +1,59 @@ +#pragma once +#include +#include +#include +#include +#include + +namespace wmtk::attribute { +WMTK_NLOHMANN_JSON_DECLARATION(AttributeType) +} + +namespace wmtk::components::multimesh::utils { + + +// the minimal information to uniquely extract an attribute handle +struct AttributeDescription +{ + 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; + auto operator==(const AttributeDescription&) const -> bool; + + + WMTK_NLOHMANN_JSON_FRIEND_DECLARATION(AttributeDescription) +}; +} // namespace wmtk::components::multimesh::utils 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 ea404b6dc6..90d14a3d92 100644 --- a/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp +++ b/components/multimesh/src/wmtk/components/multimesh/utils/get_attribute.cpp @@ -1,87 +1,202 @@ #include "get_attribute.hpp" +#include #include +#include #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 { -wmtk::attribute::MeshAttributeHandle get_attribute(const Mesh& mesh, const nlohmann::json& js) +namespace wmtk::components::multimesh::utils { +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(js.is_string()) { - spdlog::trace("Parsing {} as just a name", js.dump()); +#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()); + if (ret[1].empty()) { + std::swap(ret[0], ret[1]); } - 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 ret; +#else + + std::array ret; + std::vector tmp; + if (attribute_path.empty()) { + tmp.emplace_back(""); + tmp.emplace_back(""); + + } else { + std::string v = std::string(attribute_path); + std::istringstream iss(v); + std::string token; + if (v.size() > 0 && v[0] == '/') { + tmp.emplace_back(""); } - return false; - }; + 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 = {"", tmp[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) +{ + if (mesh.has_attribute(std::string(name), pt)) { + return mesh.get_attribute_handle(std::string(name), pt); + } else { + throw attribute_missing_error::make( + std::string(name), + wmtk::get_primitive_type_id(pt), + wmtk::attribute ::attribute_type_enum_from_type()); + } +} - // 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; - } +wmtk::attribute::MeshAttributeHandle get_attribute( + const Mesh& mesh, + const std::string_view& name, + PrimitiveType pt, + wmtk::attribute::AttributeType type) +{ + using AT = wmtk::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 {}; +} +wmtk::attribute::MeshAttributeHandle get_attribute( + const Mesh& mesh, + const std::string_view& name, + std::optional pt, + std::optional type) +{ + 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()) { + add_option(pt.value(), type.value()); + } else if (pt.has_value()) { + for (AT at : types) { + try { + add_option(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 { + add_option(p, type.value()); + } catch (const attribute_missing_error& e) { + continue; + } } - // 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 {}; + for (AT at : types) { + for (PrimitiveType p : wmtk::utils::primitive_below(mesh.top_simplex_type())) { + try { + add_option(p, at); + } catch (const attribute_missing_error& e) { + continue; + } + } + } } + + + 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 wmtk::attribute::MeshAttributeHandle get_attribute( - const wmtk::components::multimesh::MeshCollection& mc, - const nlohmann::json& js) + 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) { - const std::string name = js.contains("mesh") ? js["mesh"] : ""; - const auto& mesh = mc.get_mesh(name); - return get_attribute(mesh, js); + auto [mesh_path, attribute_name] = detail::decompose_attribute_path(description); + + const Mesh& nmm = mesh.get_mesh(mesh_path); + return detail::get_attribute( + nmm, + 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..2d2b7df48b 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; -} +} // namespace components::multimesh } // namespace wmtk -namespace wmtk::components::multimesh::utils { +namespace wmtk::components::multimesh { +namespace utils { +struct AttributeDescription; // // desires json that has // * name (str for attribute name) @@ -18,9 +21,19 @@ namespace wmtk::components::multimesh::utils { // * 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 Mesh& m, const nlohmann::json& js); wmtk::attribute::MeshAttributeHandle get_attribute( - const wmtk::components::multimesh::MeshCollection& m, - const nlohmann::json& js); -} // namespace wmtk::components::multimesh::utils + 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); + +} // 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..865b7290de --- /dev/null +++ b/components/multimesh/tests/get_attributes.cpp @@ -0,0 +1,243 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "wmtk/components/multimesh/MeshCollection.hpp" + +#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]") +{ + { + 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); + + { // double check that path extraction is working + std::vector double_ads; + 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) { + 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})); + } + 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 + + CHECK( + edge_handle == wmtk::components::multimesh::utils::get_attribute( + named_mm, + 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, + 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; + 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})); + } +} + diff --git a/components/multimesh/tests/named_multimesh.cpp b/components/multimesh/tests/named_multimesh.cpp index 558c21e267..cc8e34a761 100644 --- a/components/multimesh/tests/named_multimesh.cpp +++ b/components/multimesh/tests/named_multimesh.cpp @@ -3,41 +3,16 @@ #include #include #include -#include "tools/TriMesh_examples.hpp" -#include "wmtk/components/multimesh/NamedMultiMesh.hpp" +#include +#include +#include +#include -#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) -{ - if (path.size() == 0) { - // multimesh root mesh already exists so nothing to be done - return; - } - 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); - } - } -} -} // namespace TEST_CASE("named_multimesh_parse", "[components][multimesh]") @@ -128,3 +103,4 @@ TEST_CASE("named_multimesh_parse", "[components][multimesh]") named_mm.get_mesh(".child.c2").shared_from_this()); } } + 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/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; \ + } 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 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;