diff --git a/src/wmtk/CMakeLists.txt b/src/wmtk/CMakeLists.txt index 86b477eb92..bd155cf7a1 100644 --- a/src/wmtk/CMakeLists.txt +++ b/src/wmtk/CMakeLists.txt @@ -35,4 +35,5 @@ add_subdirectory(simplex) add_subdirectory(operations) add_subdirectory(autogen) add_subdirectory(invariants) +add_subdirectory(multimesh) diff --git a/src/wmtk/Mesh.cpp b/src/wmtk/Mesh.cpp index ce2dc23cf9..292c5fcd72 100644 --- a/src/wmtk/Mesh.cpp +++ b/src/wmtk/Mesh.cpp @@ -249,6 +249,7 @@ attribute::AttributeScopeHandle Mesh::create_scope() return m_attribute_manager.create_scope(*this); } + template MeshAttributeHandle Mesh::register_attribute(const std::string&, PrimitiveType, long, bool, char); template MeshAttributeHandle @@ -269,4 +270,44 @@ Tuple Mesh::switch_tuples_unsafe( return switch_tuples_unsafe>(tuple, op_sequence); } + +std::vector Mesh::absolute_multi_mesh_id() const +{ + return m_multi_mesh_manager.absolute_id(); +} +void Mesh::register_child_mesh( + const std::shared_ptr& child_mesh_ptr, + const std::vector>& map_tuples) +{ + m_multi_mesh_manager.register_child_mesh(*this,child_mesh_ptr, map_tuples); +} + + +std::vector Mesh::map(const Mesh& other_mesh, const Simplex& my_simplex) const +{ + return m_multi_mesh_manager.map(*this, other_mesh, my_simplex); +} +Simplex Mesh::map_to_parent(const Simplex& my_simplex) const +{ + return m_multi_mesh_manager.map_to_parent(*this, my_simplex); +} +std::vector Mesh::map_to_child(const Mesh& child_mesh, const Simplex& my_simplex) const +{ + return m_multi_mesh_manager.map_to_child(*this, child_mesh, my_simplex); +} + +std::vector Mesh::map_tuples(const Mesh& other_mesh, const Simplex& my_simplex) const +{ + return m_multi_mesh_manager.map_tuples(*this, other_mesh, my_simplex); +} +Tuple Mesh::map_to_parent_tuple(const Simplex& my_simplex) const +{ + return m_multi_mesh_manager.map_to_parent_tuple(*this, my_simplex); +} +std::vector Mesh::map_to_child_tuples(const Mesh& child_mesh, const Simplex& my_simplex) + const +{ + return m_multi_mesh_manager.map_to_child_tuples(*this, child_mesh, my_simplex); +} + } // namespace wmtk diff --git a/src/wmtk/Mesh.hpp b/src/wmtk/Mesh.hpp index a7272cc02b..0b330b29cc 100644 --- a/src/wmtk/Mesh.hpp +++ b/src/wmtk/Mesh.hpp @@ -49,7 +49,6 @@ class Mesh : public std::enable_shared_from_this friend class MultiMeshManager; virtual PrimitiveType top_simplex_type() const = 0; - MultiMeshManager multi_mesh_manager; friend class operations::Operation; @@ -100,8 +99,12 @@ class Mesh : public std::enable_shared_from_this PrimitiveType type, long size, bool replace = false, - T default_value = T(0) - ); + T default_value = T(0)); + + template + bool has_attribute( + const std::string& name, + const PrimitiveType ptype) const; // block standard topology tools template MeshAttributeHandle get_attribute_handle( @@ -315,6 +318,31 @@ class Mesh : public std::enable_shared_from_this bool simplex_is_less(const Simplex& s0, const Simplex& s1) const; + + //============================ + // MultiMesh interface + //============================ + std::vector absolute_multi_mesh_id() const; + void register_child_mesh( + const std::shared_ptr& child_mesh, + const std::vector>& map_tuples); + + // a generic map interface between pairs of mesh in a single multi-mesh structure + std::vector map(const Mesh& other_mesh, const Simplex& my_simplex) const; + // map to just the parent + Simplex map_to_parent(const Simplex& my_simplex) const; + // map to just a child + std::vector map_to_child(const Mesh& child_mesh, const Simplex& my_simplex) const; + + // a generic map interface between pairs of mesh in a single multi-mesh structure but returns + // tuples Each tuple partial encodes a Simplex, whose dimension is the same as my_simplex + std::vector map_tuples(const Mesh& other_mesh, const Simplex& my_simplex) const; + // map to just the parent + Tuple map_to_parent_tuple(const Simplex& my_simplex) const; + // map to just a child + std::vector map_to_child_tuples(const Mesh& child_mesh, const Simplex& my_simplex) const; + + protected: /** * @brief return the global id of the Tuple of the given dimension @@ -336,9 +364,13 @@ class Mesh : public std::enable_shared_from_this // std::shared_ptr request_accesor_cache(); //[[nodiscard]] AccessorScopeHandle push_accesor_scope(); -private: // members +protected: // THese are protected so unit tests can access - do not use manually in other derived + // classes? attribute::AttributeManager m_attribute_manager; + MultiMeshManager m_multi_mesh_manager; + +private: // PImpl'd manager of per-thread update stacks // Every time a new access scope is requested the manager creates another level of indirection // for updates @@ -388,6 +420,13 @@ MeshAttributeHandle Mesh::get_attribute_handle( r.m_primitive_type = ptype; return r; } + +template +bool Mesh::has_attribute(const std::string& name, const PrimitiveType ptype) const +{ + return m_attribute_manager.get(ptype).has_attribute(name); +} + template long Mesh::get_attribute_dimension(const MeshAttributeHandle& handle) const { diff --git a/src/wmtk/MultiMeshManager.cpp b/src/wmtk/MultiMeshManager.cpp index 34f194ab44..c714d43dd1 100644 --- a/src/wmtk/MultiMeshManager.cpp +++ b/src/wmtk/MultiMeshManager.cpp @@ -1,274 +1,351 @@ #include "MultiMeshManager.hpp" +#include +#include #include "Mesh.hpp" -#include "Types.hpp" #include "SimplicialComplex.hpp" -namespace wmtk +#include "multimesh/utils/transport_tuple.hpp" +#include "multimesh/utils/tuple_map_attribute_io.hpp" +namespace wmtk { + +namespace {} // namespace + +Tuple MultiMeshManager::map_tuple_between_meshes( + const Mesh& source_mesh, + const Mesh& target_mesh, + const ConstAccessor& map_accessor, + const Tuple& source_tuple) { - MultiMeshManager::MultiMeshManager() - : m_is_parent_mesh(false) - , m_child_id(-1) - { - } + PrimitiveType source_mesh_primitive_type = source_mesh.top_simplex_type(); + PrimitiveType target_mesh_primitive_type = target_mesh.top_simplex_type(); + PrimitiveType min_primitive_type = + std::min(source_mesh_primitive_type, target_mesh_primitive_type); - MultiMeshManager::~MultiMeshManager() = default; - MultiMeshManager::MultiMeshManager(const MultiMeshManager& o) = default; - MultiMeshManager::MultiMeshManager(MultiMeshManager&& o) = default; - MultiMeshManager& MultiMeshManager::operator=(const MultiMeshManager& o) = default; - MultiMeshManager& MultiMeshManager::operator=(MultiMeshManager&& o) = default; + const auto [source_mesh_base_tuple, target_mesh_base_tuple] = + multimesh::utils::read_tuple_map_attribute(map_accessor, source_tuple); - bool MultiMeshManager::is_parent_mesh() const - { - return m_is_parent_mesh; + if (source_mesh_base_tuple.is_null() || target_mesh_base_tuple.is_null()) { + return Tuple(); // return null tuple } - long MultiMeshManager::child_id() const - { - return m_child_id; - } + // we want to repeat switches from source_base_tuple -> source_tuple to + // target_base _tuple -> return value + // + return multimesh::utils::transport_tuple( + source_mesh_base_tuple, + source_tuple, + source_mesh_primitive_type, + target_mesh_base_tuple, + target_mesh_primitive_type); +} + + +MultiMeshManager::MultiMeshManager() = default; + +MultiMeshManager::~MultiMeshManager() = default; +MultiMeshManager::MultiMeshManager(const MultiMeshManager& o) = default; +MultiMeshManager::MultiMeshManager(MultiMeshManager&& o) = default; +MultiMeshManager& MultiMeshManager::operator=(const MultiMeshManager& o) = default; +MultiMeshManager& MultiMeshManager::operator=(MultiMeshManager&& o) = default; + +bool MultiMeshManager::is_root() const +{ + return m_parent == nullptr; +} - void MultiMeshManager::write_tuple_map_attribute(MeshAttributeHandle map_handle, Mesh& source_mesh, const Tuple& source_tuple, const Tuple& target_tuple) - { - auto map_accessor = source_mesh.create_accessor(map_handle); - auto map = map_accessor.vector_attribute(source_tuple); - - map(0) = source_tuple.m_local_vid; - map(1) = source_tuple.m_local_eid; - map(2) = source_tuple.m_local_fid; - map(3) = source_tuple.m_global_cid; - map(4) = source_tuple.m_hash; - - map(5) = target_tuple.m_local_vid; - map(6) = target_tuple.m_local_eid; - map(7) = target_tuple.m_local_fid; - map(8) = target_tuple.m_global_cid; - map(9) = target_tuple.m_hash; +long MultiMeshManager::child_id() const +{ + return m_child_id; +} + +std::vector MultiMeshManager::absolute_id() const +{ + if (is_root()) { + return {}; + } else { + auto id = m_parent->m_multi_mesh_manager.absolute_id(); + id.emplace_back(m_child_id); + return id; } - - std::tuple MultiMeshManager::read_tuple_map_attribute(MeshAttributeHandle map_handle, const Mesh& source_mesh, const Tuple& source_tuple) - { - auto map_accessor = source_mesh.create_accessor(map_handle); - auto map = map_accessor.const_vector_attribute(source_tuple); +} + - return std::make_tuple(Tuple(map(0), map(1), map(2), map(3), map(4)), Tuple(map(5), map(6), map(7), map(8), map(9))); +void MultiMeshManager::register_child_mesh( + Mesh& my_mesh, + const std::shared_ptr& child_mesh_ptr, + const std::vector>& child_tuple_my_tuple_map) +{ + assert((&my_mesh.m_multi_mesh_manager) == this); + assert(bool(child_mesh_ptr)); + + Mesh& child_mesh = *child_mesh_ptr; + + const PrimitiveType child_primitive_type = child_mesh.top_simplex_type(); + const long new_child_id = long(m_children.size()); + + + constexpr static long TWO_TUPLE_SIZE = 10; + constexpr static long DEFAULT_TUPLES_VALUES = -1; + auto child_to_parent_handle = child_mesh.register_attribute( + child_to_parent_map_attribute_name(), + child_primitive_type, + TWO_TUPLE_SIZE, + false, + DEFAULT_TUPLES_VALUES); + + // TODO: make sure that this attribute doesnt already exist + auto parent_to_child_handle = my_mesh.register_attribute( + parent_to_child_map_attribute_name(new_child_id), + child_primitive_type, + TWO_TUPLE_SIZE, + false, + DEFAULT_TUPLES_VALUES); + + + auto child_to_parent_accessor = child_mesh.create_accessor(child_to_parent_handle); + auto parent_to_child_accessor = my_mesh.create_accessor(parent_to_child_handle); + + // register maps + for (const auto& [child_tuple, my_tuple] : child_tuple_my_tuple_map) { + multimesh::utils::write_tuple_map_attribute( + parent_to_child_accessor, + my_tuple, + child_tuple); + multimesh::utils::write_tuple_map_attribute( + child_to_parent_accessor, + child_tuple, + my_tuple); } - void MultiMeshManager::register_child_mesh(Mesh& parent_mesh, std::shared_ptr child_mesh, const std::vector>& child_mesh_simplex_map) - { - PrimitiveType map_ptype = child_mesh->top_simplex_type(); - long cur_child_id = long(parent_mesh.multi_mesh_manager.child_meshes.size()); - - auto map_to_parent_handle = child_mesh->register_attribute("map_to_parent", map_ptype, 10); - - auto map_to_child_handle = parent_mesh.register_attribute(fmt::format("map_to_child_{}", cur_child_id), map_ptype, 10); - for (long id = 0; id < parent_mesh.capacity(map_ptype); ++id) - { - write_tuple_map_attribute(map_to_child_handle, parent_mesh, parent_mesh.tuple_from_id(map_ptype, id), Tuple()); - } - // register maps - for (long id = 0; id < child_mesh->capacity(map_ptype); ++id) - { - write_tuple_map_attribute(map_to_parent_handle, *child_mesh, child_mesh_simplex_map[id][0], child_mesh_simplex_map[id][1]); + MultiMeshManager& child_manager = child_mesh.m_multi_mesh_manager; - write_tuple_map_attribute(map_to_child_handle, parent_mesh, child_mesh_simplex_map[id][1], child_mesh_simplex_map[id][0]); - } - - // update on child_mesh - child_mesh->multi_mesh_manager.map_to_parent_handle = map_to_parent_handle; - child_mesh->multi_mesh_manager.m_is_parent_mesh = false; - child_mesh->multi_mesh_manager.m_child_id = long(parent_mesh.multi_mesh_manager.child_meshes.size()); - - // update on parent_mesh - parent_mesh.multi_mesh_manager.child_meshes.push_back(child_mesh); - parent_mesh.multi_mesh_manager.map_to_child_handles.push_back(map_to_child_handle); - parent_mesh.multi_mesh_manager.m_is_parent_mesh = true; - + // update on child_mesh + child_manager.map_to_parent_handle = child_to_parent_handle; + child_manager.m_child_id = new_child_id; + child_manager.m_parent = &my_mesh; + + // update myself + m_children.emplace_back(ChildData{child_mesh_ptr, parent_to_child_handle}); +} + +/* + * TODO: It is the consumer's responsibility to generate teh identity map via a utility function +void MultiMeshManager::register_child_mesh( + Mesh& my_mesh, + std::shared_ptr child_mesh, + const std::vector& child_mesh_simplex_id_map) +{ + PrimitiveType map_type = child_mesh->top_simplex_type(); + std::vector> child_tuple_my_tuple_map; + + for (long child_cell_id = 0; child_cell_id < long(child_mesh_simplex_id_map.size()); + ++child_cell_id) { + long parent_cell_id = child_mesh_simplex_id_map[child_cell_id]; + child_tuple_my_tuple_map.push_back( + {child_mesh->tuple_from_id(map_type, child_cell_id), + my_mesh.tuple_from_id(map_type, parent_cell_id)}); } + register_child_mesh(my_mesh, child_mesh, child_tuple_my_tuple_map); +} +*/ - void MultiMeshManager::register_child_mesh(Mesh& parent_mesh, std::shared_ptr child_mesh, const std::vector& child_mesh_simplex_id_map) - { - PrimitiveType map_type = child_mesh->top_simplex_type(); - std::vector> child_mesh_simplex_map; - - for (long child_cell_id = 0; child_cell_id < long(child_mesh_simplex_id_map.size()); ++child_cell_id) - { - long parent_cell_id = child_mesh_simplex_id_map[child_cell_id]; - child_mesh_simplex_map.push_back({child_mesh->tuple_from_id(map_type, child_cell_id), parent_mesh.tuple_from_id(map_type, parent_cell_id)}); - } - register_child_mesh(parent_mesh, child_mesh, child_mesh_simplex_map); +const Mesh& MultiMeshManager::get_root_mesh(const Mesh& my_mesh) const +{ + if (is_root()) { + return my_mesh; + } else { + return m_parent->m_multi_mesh_manager.get_root_mesh(*m_parent); + } +} +Mesh& MultiMeshManager::get_root_mesh(Mesh& my_mesh) +{ + if (is_root()) { + return my_mesh; + } else { + return m_parent->m_multi_mesh_manager.get_root_mesh(*m_parent); + } +} +std::vector +MultiMeshManager::map(const Mesh& my_mesh, const Mesh& other_mesh, const Simplex& my_simplex) const +{ + const auto ret_tups = map_tuples(my_mesh, other_mesh, my_simplex); + return simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + ret_tups, + my_simplex.primitive_type()); +} +std::vector MultiMeshManager::map_tuples( + const Mesh& my_mesh, + const Mesh& other_mesh, + const Simplex& my_simplex) const +{ + const PrimitiveType pt = my_simplex.primitive_type(); + assert((&my_mesh.m_multi_mesh_manager) == this); + std::vector equivalent_tuples = simplex::top_level_cofaces_tuples(my_mesh, my_simplex); + // MultiMeshMapVisitor visitor(my_mesh, other_mesh); + // const auto my_id = absolute_id(); someday could be used to map down + const auto other_id = other_mesh.absolute_multi_mesh_id(); + + // get a root tuple by converting the tuple up parent meshes until root is found + Tuple cur_tuple = my_simplex.tuple(); + const Mesh* cur_mesh = &my_mesh; + while (cur_mesh != + nullptr) { // cur_mesh == nullptr if we just walked past the root node so we stop + cur_tuple = cur_mesh->m_multi_mesh_manager.map_tuple_to_parent_tuple(*cur_mesh, cur_tuple); + cur_mesh = cur_mesh->m_multi_mesh_manager.m_parent; } - Tuple MultiMeshManager::map_tuple_between_meshes(const Mesh& source_mesh, const Mesh& target_mesh, MeshAttributeHandle source_map_handle, const Tuple& source_tuple) - { - PrimitiveType source_mesh_ptype = source_mesh.top_simplex_type(); - PrimitiveType target_mesh_ptype = target_mesh.top_simplex_type(); + // bieng lazy about how i set cur_mesh to nullptr above - could simplify the loop to optimize + cur_mesh = &get_root_mesh(other_mesh); - auto [source_mesh_base_tuple, target_mesh_base_tuple] = read_tuple_map_attribute(source_map_handle, source_mesh, source_tuple); - if (source_mesh_base_tuple.is_null() || target_mesh_base_tuple.is_null()) - { - return Tuple(); // return null tuple - } + // note that (cur_mesh, tuples) always match (i.e tuples are tuples from cur_mesh) + std::vector tuples; + tuples.emplace_back(cur_tuple); - if (source_mesh_ptype == PrimitiveType::Face && target_mesh_ptype == PrimitiveType::Face) - { - Tuple cur_tuple = source_tuple; - std::vector record_switch_operations; - while (cur_tuple != source_mesh_base_tuple) - { - cur_tuple = source_mesh.switch_tuple(cur_tuple, PrimitiveType::Vertex); - record_switch_operations.push_back(PrimitiveType::Vertex); - if (cur_tuple == source_mesh_base_tuple) - { - break; - } - cur_tuple = source_mesh.switch_tuple(cur_tuple, PrimitiveType::Edge); - record_switch_operations.push_back(PrimitiveType::Edge); - } - - Tuple ret_tuple = target_mesh_base_tuple; - for (int i = record_switch_operations.size() - 1; i >= 0; --i) - { - ret_tuple = target_mesh.switch_tuple(ret_tuple, record_switch_operations[i]); - } - - return ret_tuple; - } - else - { - // TODO: implement this for other cases later - // TODO: maybe use a seperate function for each case? - return Tuple(); - } - } + for (auto it = other_id.rbegin(); it != other_id.rend(); ++it) { + // get the select ID from the child map + long child_index = *it; + const ChildData& cd = cur_mesh->m_multi_mesh_manager.m_children.at(child_index); - std::vector MultiMeshManager::find_all_simplices_in_child_mesh(const Mesh& parent_mesh, long child_id, const Simplex& simplex_parent) - { - auto child_mesh_ptr = parent_mesh.multi_mesh_manager.child_meshes[child_id]; - auto map_to_child_handle = parent_mesh.multi_mesh_manager.map_to_child_handles[child_id]; - PrimitiveType simplex_ptype = simplex_parent.primitive_type(); - PrimitiveType childmesh_ptype = child_mesh_ptr->top_simplex_type(); - - if (simplex_ptype > childmesh_ptype) - { - // Can't find higher-dimensional simplex in child_mesh - return std::vector(); + // for every tuple we have try to collect all versions + std::vector new_tuples; + for (const Tuple& t : tuples) { + // get new tuples for every version that exists + std::vector n = + cur_mesh->m_multi_mesh_manager.map_to_child_tuples(*cur_mesh, cd, Simplex(pt, t)); + // append to teh current set of new tuples + new_tuples.insert(new_tuples.end(), n.begin(), n.end()); } + // update teh (mesh,tuples) pair + tuples = std::move(new_tuples); + cur_mesh = cd.mesh.get(); - // Find all dim(child_mesh) simplex in open_star(simplex_parent)) in parent_mesh - auto top_simplex_in_open_star = SimplicialComplex::open_star(parent_mesh, simplex_parent).get_simplices(childmesh_ptype); - - // map tuples to child_mesh and collect all distinct simplices - SimplicialComplex ret_sc(*child_mesh_ptr); - for (auto s : top_simplex_in_open_star) - { - auto child_tuple = map_tuple_between_meshes(parent_mesh, *child_mesh_ptr, map_to_child_handle, s.tuple()); - if (!child_tuple.is_null()) - { - ret_sc.add_simplex(Simplex(simplex_ptype, child_tuple)); - } - } - - return ret_sc.get_simplex_vector(); + // the front id of the current mesh should be the child index from this iteration + assert(cur_mesh->m_multi_mesh_manager.m_child_id == child_index); } - bool MultiMeshManager::is_child_mesh_valid(const Mesh& parent_mesh, const Mesh& child_mesh) - { - // TODO: implement this - - return true; - } + // visitor.map(equivalent_tuples, my_simplex.primitive_type()); - std::vector MultiMeshManager::map_edge_tuple_to_all_children(const Mesh& parent_mesh, const Tuple& edge_tuple) - { - std::vector ret; - for (auto child_mesh_ptr : parent_mesh.multi_mesh_manager.child_meshes) - { - long child_id = child_mesh_ptr->multi_mesh_manager.child_id(); - auto map_to_child_handle = parent_mesh.multi_mesh_manager.map_to_child_handles[child_id]; - Tuple child_tuple = map_tuple_between_meshes(parent_mesh, *child_mesh_ptr, map_to_child_handle, edge_tuple); - ret.push_back(child_tuple); - } - return ret; + return tuples; +} + + +Simplex MultiMeshManager::map_to_parent(const Mesh& my_mesh, const Simplex& my_simplex) const +{ + return Simplex( + my_simplex.primitive_type(), + map_tuple_to_parent_tuple(my_mesh, my_simplex.tuple())); +} +Tuple MultiMeshManager::map_to_parent_tuple(const Mesh& my_mesh, const Simplex& my_simplex) const +{ + return map_tuple_to_parent_tuple(my_mesh, my_simplex.tuple()); +} + +Tuple MultiMeshManager::map_tuple_to_parent_tuple(const Mesh& my_mesh, const Tuple& my_tuple) const +{ + assert((&my_mesh.m_multi_mesh_manager) == this); + assert(!is_root()); + + const Mesh& parent_mesh = *m_parent; + + const auto& map_handle = map_to_parent_handle; + // assert(!map_handle.is_null()); + + auto map_accessor = my_mesh.create_accessor(map_handle); + return map_tuple_between_meshes(my_mesh, parent_mesh, map_accessor, my_tuple); +} +std::vector MultiMeshManager::map_to_child_tuples( + const Mesh& my_mesh, + const ChildData& child_data, + const Simplex& my_simplex) const +{ + assert((&my_mesh.m_multi_mesh_manager) == this); + + const Mesh& child_mesh = *child_data.mesh; + const auto map_handle = child_data.map_handle; + // we will overwrite these tuples inline with the mapped ones while running down the map + // functionalities + std::vector tuples = simplex::top_level_cofaces_tuples(my_mesh, my_simplex); + + auto map_accessor = my_mesh.create_accessor(map_handle); + for (Tuple& tuple : tuples) { + tuple = map_tuple_between_meshes(my_mesh, child_mesh, map_accessor, tuple); } + tuples.erase( + std::remove_if( + tuples.begin(), + tuples.end(), + [](const Tuple& t) -> bool { return t.is_null(); }), + tuples.end()); + return tuples; +} + +std::vector MultiMeshManager::map_to_child_tuples( + const Mesh& my_mesh, + const Mesh& child_mesh, + const Simplex& my_simplex) const +{ + return map_to_child_tuples(my_mesh, child_mesh.m_multi_mesh_manager.child_id(), my_simplex); +} + +std::vector MultiMeshManager::map_to_child_tuples( + const Mesh& my_mesh, + long child_id, + const Simplex& my_simplex) const +{ + // this is just to do a little redirection for simpplifying map_to_child (and potentially for a + // visitor pattern) + return map_to_child_tuples(my_mesh, m_children.at(child_id), my_simplex); +} + +std::vector MultiMeshManager::map_to_child( + const Mesh& my_mesh, + const Mesh& child_mesh, + const Simplex& my_simplex) const +{ + auto tuples = map_to_child_tuples(my_mesh, child_mesh, my_simplex); + return simplex::utils::tuple_vector_to_homogeneous_simplex_vector( + tuples, + my_simplex.primitive_type()); +} - bool MultiMeshManager::is_map_valid(const Mesh& parent_mesh) const - { - for (auto child_mesh_ptr : child_meshes) - { - long child_id = child_mesh_ptr->multi_mesh_manager.child_id(); - PrimitiveType map_type = child_mesh_ptr->top_simplex_type(); - - auto parent_to_child_handle = map_to_child_handles[child_id]; - auto child_to_parent_handle = child_mesh_ptr->multi_mesh_manager.map_to_parent_handle; - auto child_cell_flag_accessor = child_mesh_ptr->get_flag_accessor(map_type); - - for (long id = 0; id < child_mesh_ptr->capacity(map_type); ++id) - { - if (child_cell_flag_accessor.scalar_attribute(child_mesh_ptr->tuple_from_id(map_type, id)) == 0) - { - continue; - } - - // 1. test if all maps in child_mesh exisits - // 2. test if tuples in maps are valid (and up_to_date) - // 3. test if map is symmetric - // 4. test switch_top_simplex operation - - auto [child_tuple, parent_tuple] = read_tuple_map_attribute(child_to_parent_handle, *child_mesh_ptr, child_mesh_ptr->tuple_from_id(map_type, id)); - - if (!child_mesh_ptr->is_valid_slow(child_tuple)) - { - return false; - } - if (!parent_mesh.is_valid_slow(parent_tuple)) - { - return false; - } - - auto [parent_tuple_test, child_tuple_test] = read_tuple_map_attribute(parent_to_child_handle, parent_mesh, parent_tuple); - - if (child_tuple_test != child_tuple || parent_tuple_test != parent_tuple) - { - return false; - } - - // for 4, current code support only mapping between triangle meshes - if (map_type == PrimitiveType::Face && parent_mesh.top_simplex_type() == PrimitiveType::Face) - { - Tuple cur_child_tuple = child_tuple; - Tuple cur_parent_tuple = parent_tuple; - - for (int i = 0; i < 3; i++) - { - if (!child_mesh_ptr->is_boundary(cur_child_tuple)) - { - if (parent_mesh.is_boundary(cur_parent_tuple)) - { - return false; - } - - Tuple child_tuple_opp = child_mesh_ptr->switch_face(cur_child_tuple); - Tuple parent_tuple_opp = parent_mesh.switch_face(cur_parent_tuple); - - if (parent_tuple_opp != map_tuple_between_meshes(*child_mesh_ptr, parent_mesh, child_to_parent_handle, child_tuple_opp)) - { - return false; - } - - } - cur_child_tuple = child_mesh_ptr->switch_edge(child_mesh_ptr->switch_vertex(cur_child_tuple)); - cur_parent_tuple = parent_mesh.switch_edge(parent_mesh.switch_vertex(cur_parent_tuple)); - } - } - else - { - // TODO: implement other cases - continue; - } - - } - + +std::vector> MultiMeshManager::same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child, + const std::vector& parent_simplices) +{ + PrimitiveType primitive_type = parent.top_simplex_type(); + assert(primitive_type == child.top_simplex_type()); + + long size = child.capacity(primitive_type); + assert(size == long(parent_simplices.size())); + std::vector> ret; + ret.reserve(size); + + auto parent_flag_accessor = parent.get_const_flag_accessor(primitive_type); + auto child_flag_accessor = child.get_const_flag_accessor(primitive_type); + + for (long index = 0; index < size; ++index) { + const Tuple ct = child.tuple_from_id(primitive_type, index); + const Tuple pt = parent.tuple_from_id(primitive_type, parent_simplices.at(index)); + if ((parent_flag_accessor.const_scalar_attribute(pt) & 1) == 0) { + continue; + } + if ((child_flag_accessor.const_scalar_attribute(ct) & 1) == 0) { + continue; } - return true; + + ret.emplace_back(std::array{{ct, pt}}); } + return ret; +} + +std::string MultiMeshManager::parent_to_child_map_attribute_name(long index) +{ + return fmt::format("map_to_child_{}", index); +} +std::string MultiMeshManager::child_to_parent_map_attribute_name() +{ + return "map_to_parent"; } +} // namespace wmtk diff --git a/src/wmtk/MultiMeshManager.hpp b/src/wmtk/MultiMeshManager.hpp index 9187819f4d..7e380bc88f 100644 --- a/src/wmtk/MultiMeshManager.hpp +++ b/src/wmtk/MultiMeshManager.hpp @@ -1,18 +1,24 @@ #pragma once +#include #include -#include "attribute/AttributeManager.hpp" -#include "attribute/AttributeScopeHandle.hpp" -#include "attribute/MeshAttributes.hpp" +#include "Accessor.hpp" #include "Simplex.hpp" #include "Tuple.hpp" -#include +#include "attribute/AttributeScopeHandle.hpp" +#include "attribute/MeshAttributes.hpp" +// included to make a friend as this requires IDs +#include + +namespace wmtk { -namespace wmtk -{ class Mesh; class MultiMeshManager { - public: +public: + friend std::vector> multimesh::same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child, + const std::vector& parent_simplices); MultiMeshManager(); ~MultiMeshManager(); @@ -25,49 +31,132 @@ class MultiMeshManager // Storage of MultiMesh //========================================================= - bool is_parent_mesh() const; + bool is_root() const; long child_id() const; - // helper function to read/write the map from source_tuple to target_tuple to the attribute - static void write_tuple_map_attribute(MeshAttributeHandle map_handle, Mesh& source_mesh, const Tuple& source_tuple, const Tuple& target_tuple); - static std::tuple read_tuple_map_attribute(MeshAttributeHandle map_handle, const Mesh& source_mesh, const Tuple& source_tuple); + // + // an id computed using the path to the root. NOTE: traversing from the root requies iterating + // these indices backwards + std::vector absolute_id() const; - // Child Meshes - std::vector> child_meshes; - - // Only valid if this is_parent_mesh == true - // size of child_meshes.size(), store the map to the base_tuple of the child_meshes (on the top level simplex of each child_mesh) - // a map is a pair of two tuples, from a tuple in parent_mesh to a tuple in child_mesh - // i.e. Dim(map_to_child[i]) == Dim(child_meshes[i]) - std::vector> map_to_child_handles; - - // Only valid if this is is_parent_mesh == false - // store the map to the base_tuple of the parent_mesh - MeshAttributeHandle map_to_parent_handle; - - // register child_meshes and the map from child_meshes to this mesh, child_mesh_simplex - static void register_child_mesh(Mesh&parent_mesh, std::shared_ptr child_mesh, const std::vector>&child_mesh_simplex_map); - static void register_child_mesh(Mesh&parent_mesh, std::shared_ptr child_mesh, const std::vector& child_mesh_simplex_id_map); - - // helper function to check if this mesh is a valid child_mesh of parent_mesh - // i.e. the connectivity of this mesh is a subset of this in parent_mesh - static bool is_child_mesh_valid(const Mesh& parent_mesh, const Mesh& child_mesh); - bool is_map_valid(const Mesh& parent_mesh) const; + // register child_meshes and the map from child_meshes to this mesh, child_mesh_simplex + // + void register_child_mesh( + Mesh& my_mesh, + const std::shared_ptr& child_mesh, + const std::vector>& child_tuple_my_tuple_map); + + + // bool are_maps_valid(const Mesh& my_mesh) const; + + //=========== + //=========== + // Map functions + //=========== + //=========== + // Note that when we map a M-tuplefrom a K-complex to a J-complex there are different + // relationships necessary if K == J + // if M == K then this is unique + // if M < K then this is many to many + // if K < J + // if M == K then it is one to many + // if M < K then it is many to many + // + // Note also that functions that end with _tuple or _tuples willl return tuples rather than simplices - // TODO: make it a free function? static function? - // Map source_tuple from source_mesh to target_mesh - static Tuple map_tuple_between_meshes(const Mesh& source_mesh, const Mesh& target_mesh, MeshAttributeHandle source_map_handle, const Tuple& source_tuple); + //=========== + // Simplex maps + //=========== + // generic mapping function that maps a tuple from "this" mesh to the other mesh + std::vector map(const Mesh& my_mesh, const Mesh& other_mesh, const Simplex& my_simplex) + const; + // generic mapping function that maps a tuple from "this" mesh to the other mesh + std::vector + map_tuples(const Mesh& my_mesh, const Mesh& other_mesh, const Simplex& my_simplex) const; - static std::vector find_all_simplices_in_child_mesh(const Mesh& parent_mesh, long child_id, const Simplex& simplex_parent); + + Simplex map_to_parent(const Mesh& my_mesh, const Simplex& my_simplex) const; + Tuple map_to_parent_tuple(const Mesh& my_mesh, const Simplex& my_simplex) const; + + + // generic mapping function that maps a tuple from "this" mesh to one of its children + std::vector + map_to_child(const Mesh& my_mesh, const Mesh& child_mesh, const Simplex& my_simplex) const; + std::vector map_to_child_tuples( + const Mesh& my_mesh, + const Mesh& child_mesh, + const Simplex& my_simplex) const; // Utility function to map a edge tuple to all its children, used in operations - static std::vector map_edge_tuple_to_all_children(const Mesh& parent_mesh,const Tuple& edge_tuple); + std::vector map_edge_tuple_to_all_children(const Mesh& my_mesh, const Simplex& tuple) + const; + + const Mesh& get_root_mesh(const Mesh& my_mesh) const; + Mesh& get_root_mesh(Mesh& my_mesh); + +protected: + struct ChildData + { + std::shared_ptr mesh; + // store the map from the manager's mesh to the child mesh (on the top + // level simplex of the mesh) + // encoded by a pair of two tuples, from a tuple in current mesh to a tuple in + // child_mesh + MeshAttributeHandle map_handle; + }; + +private: + Mesh* m_parent = nullptr; + // only valid if this is teh child of some other mesh + // store the map to the base_tuple of the my_mesh + MeshAttributeHandle map_to_parent_handle; + long m_child_id = -1; + + + // Child Meshes + std::vector m_children; + +protected: // protected to enable unit testing + //=========== + // Tuple maps + //=========== + + // generic mapping function that maps a tuple from "this" mesh to its parent. We don't actually + // need the simplex parent of the tuple being mapped up so we can throw away the simplex-nes + Tuple map_tuple_to_parent_tuple(const Mesh& my_mesh, const Tuple& my_tuple) const; + + + // wrapper for implementing converting tuple to a child using the internal map data + std::vector map_to_child_tuples( + const Mesh& my_mesh, + const ChildData& child_data, + const Simplex& simplex) const; + + // wrapper for implementing converting tuple to a child using the internal map data + std::vector + map_to_child_tuples(const Mesh& my_mesh, long child_id, const Simplex& simplex) const; + + + + + static Tuple map_tuple_between_meshes( + const Mesh& source_mesh, + const Mesh& target_mesh, + const ConstAccessor& source_to_target_map_accessor, + const Tuple& source_tuple); + + const std::vector& children() const { return m_children; } + + static std::string parent_to_child_map_attribute_name(long index); + static std::string child_to_parent_map_attribute_name(); - private: - // flag indicating if this mesh is a parent mesh - bool m_is_parent_mesh; - long m_child_id; +private: + // this is defined internally but is preferablly invoked through the multimesh free function + static std::vector> same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child, + const std::vector& parent_simplices); }; -} \ No newline at end of file +} // namespace wmtk diff --git a/src/wmtk/TriMeshOperationExecutor.cpp b/src/wmtk/TriMeshOperationExecutor.cpp index 38b63c8624..d9875c85fb 100644 --- a/src/wmtk/TriMeshOperationExecutor.cpp +++ b/src/wmtk/TriMeshOperationExecutor.cpp @@ -370,6 +370,7 @@ std::vector> TriMesh::TriMeshOperationExecutor::prepare_operating_tuples_for_child_meshes() const { std::vector> vec_t_child(m_incident_face_datas.size()); + /* for (long i = 0; i < long(m_incident_face_datas.size()); ++i) { vec_t_child[i] = MultiMeshManager::map_edge_tuple_to_all_children( m_mesh, @@ -389,11 +390,14 @@ TriMesh::TriMeshOperationExecutor::prepare_operating_tuples_for_child_meshes() c } } } + */ return vec_t_child; } Tuple TriMesh::TriMeshOperationExecutor::split_edge() { + return split_edge_single_mesh(); + /* if (!m_mesh.multi_mesh_manager.is_parent_mesh()) { return split_edge_single_mesh(); } else { @@ -482,6 +486,7 @@ Tuple TriMesh::TriMeshOperationExecutor::split_edge() } return ret_tuple; } +*/ } Tuple TriMesh::TriMeshOperationExecutor::split_edge_single_mesh() @@ -524,6 +529,7 @@ Tuple TriMesh::TriMeshOperationExecutor::split_edge_single_mesh() void TriMesh::TriMeshOperationExecutor::update_hash_in_map(TriMesh& child_mesh) { + /* long child_id = child_mesh.multi_mesh_manager.child_id(); auto child_hash_accessor = child_mesh.get_cell_hash_accessor(); @@ -559,10 +565,13 @@ void TriMesh::TriMeshOperationExecutor::update_hash_in_map(TriMesh& child_mesh) t_parent_new); } } + */ } Tuple TriMesh::TriMeshOperationExecutor::collapse_edge() { + return collapse_edge_single_mesh(); + /* if (!m_mesh.multi_mesh_manager.is_parent_mesh()) { return collapse_edge_single_mesh(); } else { @@ -597,6 +606,7 @@ Tuple TriMesh::TriMeshOperationExecutor::collapse_edge() return ret_tuple; } + */ } diff --git a/src/wmtk/Tuple.hpp b/src/wmtk/Tuple.hpp index ca5e2aded0..0837f1cf05 100644 --- a/src/wmtk/Tuple.hpp +++ b/src/wmtk/Tuple.hpp @@ -10,7 +10,7 @@ class PointMesh; class EdgeMesh; class TriMesh; class TetMesh; -namespace autogen::utils { +namespace utils { class TupleInspector; } namespace operations { @@ -42,7 +42,7 @@ class Tuple friend class operations::Operation; friend class MultiMeshManager; friend class utils::TupleCellLessThan; - friend class autogen::utils::TupleInspector; + friend class utils::TupleInspector; // friend long Mesh::id(const Tuple& tuple, const PrimitiveType& type) const; // friend Mesh::is_ccw(const Tuple& tuple) const; // friend Mesh::switch_tuple(const Tuple& tuple, const PrimitiveType& type) const; diff --git a/src/wmtk/attribute/MeshAttributes.cpp b/src/wmtk/attribute/MeshAttributes.cpp index a052c18cfe..906dfe5083 100644 --- a/src/wmtk/attribute/MeshAttributes.cpp +++ b/src/wmtk/attribute/MeshAttributes.cpp @@ -53,8 +53,11 @@ void MeshAttributes::clear_current_scope() } } template -AttributeHandle -MeshAttributes::register_attribute(const std::string& name, long dimension, bool replace, T default_value) +AttributeHandle MeshAttributes::register_attribute( + const std::string& name, + long dimension, + bool replace, + T default_value) { assert(replace || m_handles.find(name) == m_handles.end()); @@ -79,6 +82,11 @@ AttributeHandle MeshAttributes::attribute_handle(const std::string& name) con { return m_handles.at(name); } +template +bool MeshAttributes::has_attribute(const std::string& name) const +{ + return m_handles.find(name) != m_handles.end(); +} template bool MeshAttributes::operator==(const MeshAttributes& other) const @@ -129,9 +137,10 @@ void MeshAttributes::reserve(const long size) } } template - long MeshAttributes::dimension(const AttributeHandle& handle) const { - return attribute(handle).dimension(); - } +long MeshAttributes::dimension(const AttributeHandle& handle) const +{ + return attribute(handle).dimension(); +} template class MeshAttributes; diff --git a/src/wmtk/attribute/MeshAttributes.hpp b/src/wmtk/attribute/MeshAttributes.hpp index 0aaad07f51..15f9f8795c 100644 --- a/src/wmtk/attribute/MeshAttributes.hpp +++ b/src/wmtk/attribute/MeshAttributes.hpp @@ -37,8 +37,11 @@ class MeshAttributes void serialize(const int dim, MeshWriter& writer) const; - [[nodiscard]] AttributeHandle - register_attribute(const std::string& name, long dimension, bool replace = false, T default_value = T(0)); + [[nodiscard]] AttributeHandle register_attribute( + const std::string& name, + long dimension, + bool replace = false, + T default_value = T(0)); long reserved_size() const; void reserve(const long size); @@ -53,6 +56,7 @@ class MeshAttributes protected: AttributeHandle attribute_handle(const std::string& name) const; + bool has_attribute(const std::string& name) const; Attribute& attribute(const AttributeHandle& handle); diff --git a/src/wmtk/autogen/CMakeLists.txt b/src/wmtk/autogen/CMakeLists.txt index 442dc47edf..5f184c75e4 100644 --- a/src/wmtk/autogen/CMakeLists.txt +++ b/src/wmtk/autogen/CMakeLists.txt @@ -23,6 +23,5 @@ set(SRC_FILES local_switch_tuple.hpp local_switch_tuple.cpp - utils/TupleInspector.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/autogen/tet_mesh/is_ccw.cpp b/src/wmtk/autogen/tet_mesh/is_ccw.cpp index 2ca4b4c14c..5dbe7bae30 100644 --- a/src/wmtk/autogen/tet_mesh/is_ccw.cpp +++ b/src/wmtk/autogen/tet_mesh/is_ccw.cpp @@ -1,8 +1,8 @@ #include "is_ccw.hpp" +#include #include #include -#include #include "autogenerated_tables.hpp" #include "local_id_table_offset.hpp" diff --git a/src/wmtk/autogen/tet_mesh/local_id_table_offset.cpp b/src/wmtk/autogen/tet_mesh/local_id_table_offset.cpp index 63e1eecaa7..ddadfb1c35 100644 --- a/src/wmtk/autogen/tet_mesh/local_id_table_offset.cpp +++ b/src/wmtk/autogen/tet_mesh/local_id_table_offset.cpp @@ -1,5 +1,5 @@ #include "local_id_table_offset.hpp" -#include +#include #include "autogenerated_tables.hpp" namespace wmtk::autogen::tet_mesh { // computes the offset of a tuple's local ids in the tables diff --git a/src/wmtk/autogen/tet_mesh/local_switch_tuple.cpp b/src/wmtk/autogen/tet_mesh/local_switch_tuple.cpp index 0337d2d782..99bd2ea0cb 100644 --- a/src/wmtk/autogen/tet_mesh/local_switch_tuple.cpp +++ b/src/wmtk/autogen/tet_mesh/local_switch_tuple.cpp @@ -1,6 +1,6 @@ #include "local_switch_tuple.hpp" #include -#include +#include #include "autogenerated_tables.hpp" #include "local_id_table_offset.hpp" diff --git a/src/wmtk/autogen/tri_mesh/is_ccw.cpp b/src/wmtk/autogen/tri_mesh/is_ccw.cpp index 8007d7aed0..d6e9ceac24 100644 --- a/src/wmtk/autogen/tri_mesh/is_ccw.cpp +++ b/src/wmtk/autogen/tri_mesh/is_ccw.cpp @@ -1,7 +1,7 @@ #include "is_ccw.hpp" +#include #include -#include #include "autogenerated_tables.hpp" #include "local_id_table_offset.hpp" diff --git a/src/wmtk/autogen/tri_mesh/local_id_table_offset.cpp b/src/wmtk/autogen/tri_mesh/local_id_table_offset.cpp index f2acd9ec8c..2c20d149c2 100644 --- a/src/wmtk/autogen/tri_mesh/local_id_table_offset.cpp +++ b/src/wmtk/autogen/tri_mesh/local_id_table_offset.cpp @@ -1,5 +1,5 @@ #include "local_id_table_offset.hpp" -#include +#include #include "autogenerated_tables.hpp" namespace wmtk::autogen::tri_mesh { // computes the offset of a tuple's local ids in the tables diff --git a/src/wmtk/autogen/tri_mesh/local_switch_tuple.cpp b/src/wmtk/autogen/tri_mesh/local_switch_tuple.cpp index 5203878047..e88f9fa533 100644 --- a/src/wmtk/autogen/tri_mesh/local_switch_tuple.cpp +++ b/src/wmtk/autogen/tri_mesh/local_switch_tuple.cpp @@ -1,6 +1,6 @@ #include "local_switch_tuple.hpp" #include -#include +#include #include "autogenerated_tables.hpp" #include "local_id_table_offset.hpp" diff --git a/src/wmtk/multimesh/CMakeLists.txt b/src/wmtk/multimesh/CMakeLists.txt new file mode 100644 index 0000000000..d41a4a8680 --- /dev/null +++ b/src/wmtk/multimesh/CMakeLists.txt @@ -0,0 +1,8 @@ + +set(SRC_FILES + same_simplex_dimension_surjection.hpp + same_simplex_dimension_surjection.cpp + ) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) + +add_subdirectory(utils) diff --git a/src/wmtk/multimesh/same_simplex_dimension_surjection.cpp b/src/wmtk/multimesh/same_simplex_dimension_surjection.cpp new file mode 100644 index 0000000000..0c45a5b046 --- /dev/null +++ b/src/wmtk/multimesh/same_simplex_dimension_surjection.cpp @@ -0,0 +1,28 @@ +#include "same_simplex_dimension_surjection.hpp" +#include +#include + + +namespace wmtk::multimesh { +std::vector> same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child) +{ + PrimitiveType primitive_type = parent.top_simplex_type(); + assert(primitive_type == child.top_simplex_type()); + long size = parent.capacity(primitive_type); + assert(size == child.capacity(primitive_type)); + std::vector ps; + ps.reserve(size); + std::iota(ps.begin(), ps.end(), 0); + return same_simplex_dimension_surjection(parent, child, ps); +} + +std::vector> same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child, + const std::vector& parent_simplices) +{ + return MultiMeshManager::same_simplex_dimension_surjection(parent, child, parent_simplices); +} +} // namespace wmtk::multimesh diff --git a/src/wmtk/multimesh/same_simplex_dimension_surjection.hpp b/src/wmtk/multimesh/same_simplex_dimension_surjection.hpp new file mode 100644 index 0000000000..81fb2c75c4 --- /dev/null +++ b/src/wmtk/multimesh/same_simplex_dimension_surjection.hpp @@ -0,0 +1,27 @@ +#pragma once +#include +#include +#include + +namespace wmtk { +class Mesh; +} + +namespace wmtk::multimesh { + +// Handles the "trivial " mapping case as we see in OBJ files +// parent and child are assumed to be homogeneous meshes of the same dimension and the same number +// of top level simplices It is assumed that for each index between [0,num_top_level_simplices) it +// is assumed that the tuple parent.tuple_from_id(pt,index) should be mapped to +// child.tuple_from_id(pt,index) +std::vector> same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child); +// same as above except the mapping selects a subset of parent_simplices +// the tuple parent.tuple_from_id(pt,parent_simplices[index]) should be mapped to +// child.tuple_from_id(pt,index) +std::vector> same_simplex_dimension_surjection( + const Mesh& parent, + const Mesh& child, + const std::vector& parent_simplices); +} // namespace wmtk::multimesh diff --git a/src/wmtk/multimesh/utils/CMakeLists.txt b/src/wmtk/multimesh/utils/CMakeLists.txt new file mode 100644 index 0000000000..4e611a9b80 --- /dev/null +++ b/src/wmtk/multimesh/utils/CMakeLists.txt @@ -0,0 +1,13 @@ + +set(SRC_FILES + find_local_switch_sequence.cpp + local_switch_tuple.cpp + transport_tuple.cpp + find_local_switch_sequence.hpp + local_switch_tuple.hpp + transport_tuple.hpp + tuple_map_attribute_io.hpp + tuple_map_attribute_io.cpp + ) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) + diff --git a/src/wmtk/multimesh/utils/find_local_switch_sequence.cpp b/src/wmtk/multimesh/utils/find_local_switch_sequence.cpp new file mode 100644 index 0000000000..393c50dc9a --- /dev/null +++ b/src/wmtk/multimesh/utils/find_local_switch_sequence.cpp @@ -0,0 +1,59 @@ +#include "find_local_switch_sequence.hpp" +#include "local_switch_tuple.hpp" +namespace wmtk::multimesh::utils { + + +namespace { + +std::vector find_local_switch_sequence_trimesh( + const Tuple& source, + const Tuple& target) +{ + // TODO: assert that base_source and base_target use the same global CID + + // circulate + Tuple cur_tuple = source; + std::vector switches; + auto try_and_record = [&](PrimitiveType pt) -> bool { + cur_tuple = local_switch_tuple(PrimitiveType::Face, cur_tuple, pt); + switches.emplace_back(pt); + return cur_tuple == target; + }; + for (long j = 0; j < 3; ++j) { + for (PrimitiveType pt : {PrimitiveType::Vertex, PrimitiveType::Edge}) { + if (try_and_record(pt)) { + return switches; + } + } + } + throw "switch sequence was unable to find a sequence of switches to match tuples"; + return switches; +} +std::vector find_local_switch_sequence_edgemesh( + const Tuple& source, + const Tuple& target) +{ + if (source != target) { + return std::vector{PrimitiveType::Vertex}; + } else { + return std::vector{}; + } + throw "switch sequence was unable to find a sequence of switches to match tuples"; +} +} // namespace + +// Maps the tuple source according to the operation sequence +// std::vector operations where operations satisfies +// base_target = switch_tuples(base_source, operations) +std::vector +find_local_switch_sequence(const Tuple& source, const Tuple& target, PrimitiveType primitive_type) +{ + switch (primitive_type) { + case PrimitiveType::Face: return find_local_switch_sequence_trimesh(source, target); + case PrimitiveType::Edge: return find_local_switch_sequence_edgemesh(source, target); + case PrimitiveType::Vertex: return {}; + case PrimitiveType::Tetrahedron: + default: return {}; + } +} +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/find_local_switch_sequence.hpp b/src/wmtk/multimesh/utils/find_local_switch_sequence.hpp new file mode 100644 index 0000000000..451a31854f --- /dev/null +++ b/src/wmtk/multimesh/utils/find_local_switch_sequence.hpp @@ -0,0 +1,13 @@ +#pragma once +#include +#include +#include + +namespace wmtk::multimesh::utils { + +// Maps the tuple source according to the operation sequence +// std::vector operations where operations satisfies +// base_target = switch_tuples(base_source, operations) +std::vector +find_local_switch_sequence(const Tuple& source, const Tuple& target, PrimitiveType primitive_type); +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/local_switch_tuple.cpp b/src/wmtk/multimesh/utils/local_switch_tuple.cpp new file mode 100644 index 0000000000..757cf0ac19 --- /dev/null +++ b/src/wmtk/multimesh/utils/local_switch_tuple.cpp @@ -0,0 +1,33 @@ +#include "local_switch_tuple.hpp" +#include +#include +#include + +namespace wmtk::multimesh::utils { + +Tuple local_switch_tuple( + PrimitiveType mesh_primitive_type, + const Tuple& source, + PrimitiveType primitive_type) +{ + switch (mesh_primitive_type) { + case PrimitiveType::Face: return autogen::tri_mesh::local_switch_tuple(source, primitive_type); + case PrimitiveType::Tetrahedron: + return autogen::tet_mesh::local_switch_tuple(source, primitive_type); + case PrimitiveType::Vertex: + case PrimitiveType::Edge: + default: return Tuple(); + } +} + +Tuple local_switch_tuples( + PrimitiveType mesh_primitive_type, + const Tuple& tuple, + const std::initializer_list& op_sequence) +{ + return local_switch_tuples>( + mesh_primitive_type, + tuple, + op_sequence); +} +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/local_switch_tuple.hpp b/src/wmtk/multimesh/utils/local_switch_tuple.hpp new file mode 100644 index 0000000000..02fc3e0ea7 --- /dev/null +++ b/src/wmtk/multimesh/utils/local_switch_tuple.hpp @@ -0,0 +1,53 @@ +#pragma once +#include +#include + +namespace wmtk::multimesh::utils { + +// Maps the tuple source according to the operation sequence +// std::vector operations where operations satisfies +// base_target = switch_tuples(base_source, operations) +Tuple local_switch_tuple( + PrimitiveType mesh_primitive_type, + const Tuple& source, + PrimitiveType primitive_type); + + +// Performs a sequence of switch_tuple operations in the order specified in sequence. +// in debug mode this will assert a failure, in release this will return a null tuple +#if defined(__cpp_concepts) +template +#else +template +#endif +Tuple local_switch_tuples( + PrimitiveType mesh_primitive_type, + const Tuple& tuple, + const ContainerType& sequence); +// annoying initializer list prototype to catch switch_tuples(t, {PV,PE}) +Tuple local_switch_tuples( + PrimitiveType mesh_primitive_type, + const Tuple& tuple, + const std::initializer_list& sequence); + + +// IMPLEMENTATION of above declaration +#if defined(__cpp_concepts) +template +#else +template +#endif +Tuple local_switch_tuples( + PrimitiveType mesh_primitive_type, + const Tuple& tuple, + const ContainerType& sequence) +{ + static_assert(std::is_same_v); + Tuple r = tuple; + for (const PrimitiveType primitive : sequence) { + r = local_switch_tuple(mesh_primitive_type, r, primitive); + } + return r; +} + +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/transport_tuple.cpp b/src/wmtk/multimesh/utils/transport_tuple.cpp new file mode 100644 index 0000000000..358d99564f --- /dev/null +++ b/src/wmtk/multimesh/utils/transport_tuple.cpp @@ -0,0 +1,18 @@ +#include "transport_tuple.hpp" +#include "find_local_switch_sequence.hpp" +#include "local_switch_tuple.hpp" + +namespace wmtk::multimesh::utils { + +Tuple transport_tuple( + const Tuple& base_source, + const Tuple& base_target, + PrimitiveType base_primitive_type, + const Tuple& source, + PrimitiveType primitive_type) +{ + std::vector operations = + find_local_switch_sequence(base_source, base_target, base_primitive_type); + return local_switch_tuples(primitive_type, source, operations); +} +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/transport_tuple.hpp b/src/wmtk/multimesh/utils/transport_tuple.hpp new file mode 100644 index 0000000000..6363a4bcea --- /dev/null +++ b/src/wmtk/multimesh/utils/transport_tuple.hpp @@ -0,0 +1,16 @@ +#pragma once +#include +#include + +namespace wmtk::multimesh::utils { + +// Maps the tuple source according to the operation sequence +// std::vector operations where operations satisfies +// base_target = switch_tuples(base_source, operations) +Tuple transport_tuple( + const Tuple& base_source, + const Tuple& base_target, + PrimitiveType base_primitive_type, + const Tuple& source, + PrimitiveType primitive_type); +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/tuple_map_attribute_io.cpp b/src/wmtk/multimesh/utils/tuple_map_attribute_io.cpp new file mode 100644 index 0000000000..5cf22880d7 --- /dev/null +++ b/src/wmtk/multimesh/utils/tuple_map_attribute_io.cpp @@ -0,0 +1,64 @@ +#include "tuple_map_attribute_io.hpp" +#include +#include +#include +namespace wmtk::multimesh::utils { +namespace { +Vector tuple_to_vector5(const Tuple& t) +{ + Vector v; + v(0) = wmtk::utils::TupleInspector::local_vid(t); + v(1) = wmtk::utils::TupleInspector::local_eid(t); + v(2) = wmtk::utils::TupleInspector::local_fid(t); + v(3) = wmtk::utils::TupleInspector::global_cid(t); + v(4) = wmtk::utils::TupleInspector::hash(t); + return v; +} + +template +Tuple vector5_to_tuple(const Eigen::MatrixBase& v) +{ + return Tuple(v(0), v(1), v(2), v(3), v(4)); +} +} // namespace + +void write_tuple_map_attribute( + Accessor& map_accessor, + const Tuple& source_tuple, + const Tuple& target_tuple) +{ + auto map = map_accessor.vector_attribute(source_tuple); + + map.head<5>() = tuple_to_vector5(source_tuple); + map.tail<5>() = tuple_to_vector5(target_tuple); +} + +void write_tuple_map_attribute_slow( + Mesh& source_mesh, + MeshAttributeHandle map_handle, + const Tuple& source_tuple, + const Tuple& target_tuple) +{ + auto map_accessor = source_mesh.create_accessor(map_handle); + write_tuple_map_attribute(map_accessor, source_tuple, target_tuple); +} + +std::tuple read_tuple_map_attribute( + const ConstAccessor& map_accessor, + const Tuple& source_tuple) +{ + auto map = map_accessor.const_vector_attribute(source_tuple); + + return std::make_tuple(vector5_to_tuple(map.head<5>()), vector5_to_tuple(map.tail<5>())); +} + + +std::tuple read_tuple_map_attribute_slow( + const Mesh& source_mesh, + MeshAttributeHandle map_handle, + const Tuple& source_tuple) +{ + auto acc = source_mesh.create_const_accessor(map_handle); + return read_tuple_map_attribute(acc, source_tuple); +} +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/multimesh/utils/tuple_map_attribute_io.hpp b/src/wmtk/multimesh/utils/tuple_map_attribute_io.hpp new file mode 100644 index 0000000000..5bd6235089 --- /dev/null +++ b/src/wmtk/multimesh/utils/tuple_map_attribute_io.hpp @@ -0,0 +1,27 @@ +#include + +namespace wmtk::multimesh::utils { + + +// utility functions to simplify how we encode 2-Tuple attributes as 10-long attribute +void write_tuple_map_attribute( + Accessor& map_accessor, + const Tuple& source_tuple, + const Tuple& target_tuple); + +std::tuple read_tuple_map_attribute( + const ConstAccessor& accessor, + const Tuple& source_tuple); + +void write_tuple_map_attribute_slow( + Mesh& source_mesh, + MeshAttributeHandle map_handle, + const Tuple& source_tuple, + const Tuple& target_tuple); + + +std::tuple read_tuple_map_attribute_slow( + const Mesh& source_mesh, + MeshAttributeHandle map_handle, + const Tuple& source_tuple); +} // namespace wmtk::multimesh::utils diff --git a/src/wmtk/simplex/CMakeLists.txt b/src/wmtk/simplex/CMakeLists.txt index 2f2e1b9edb..7fc95f6a54 100644 --- a/src/wmtk/simplex/CMakeLists.txt +++ b/src/wmtk/simplex/CMakeLists.txt @@ -22,17 +22,9 @@ set(SRC_FILES simplex_boundary.cpp simplex_boundary_iterable.hpp simplex_boundary_iterable.cpp - iterable/ClosedStarIterable.hpp - iterable/ClosedStarIterable.cpp - iterable/LinkIterable.hpp - iterable/LinkIterable.cpp - iterable/OpenStarIterable.hpp - iterable/OpenStarIterable.cpp - iterable/TopLevelCofacesIterable.hpp - iterable/TopLevelCofacesIterable.cpp - iterable/SimplexBoundaryIterable.hpp - iterable/SimplexBoundaryIterable.cpp internal/SimplexEqualFunctor.hpp internal/SimplexLessFunctor.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) +add_subdirectory(utils) +add_subdirectory(iterable) diff --git a/src/wmtk/simplex/SimplexCollection.hpp b/src/wmtk/simplex/SimplexCollection.hpp index f98073a2eb..469ce1e739 100644 --- a/src/wmtk/simplex/SimplexCollection.hpp +++ b/src/wmtk/simplex/SimplexCollection.hpp @@ -12,9 +12,9 @@ class SimplexCollection public: SimplexCollection(const Mesh& mesh, std::vector&& simplices = {}) : m_mesh{mesh} + , m_simplices(std::move(simplices)) , m_simplex_is_less(mesh) , m_simplex_is_equal(mesh) - , m_simplices(std::move(simplices)) {} /** @@ -65,12 +65,12 @@ class SimplexCollection const SimplexCollection& collection_a, const SimplexCollection& collection_b); -protected: - internal::SimplexLessFunctor m_simplex_is_less; - internal::SimplexEqualFunctor m_simplex_is_equal; protected: const Mesh& m_mesh; std::vector m_simplices; +protected: + internal::SimplexLessFunctor m_simplex_is_less; + internal::SimplexEqualFunctor m_simplex_is_equal; }; } // namespace wmtk::simplex diff --git a/src/wmtk/simplex/iterable/CMakeLists.txt b/src/wmtk/simplex/iterable/CMakeLists.txt new file mode 100644 index 0000000000..9792dc557b --- /dev/null +++ b/src/wmtk/simplex/iterable/CMakeLists.txt @@ -0,0 +1,13 @@ +set(SRC_FILES + ClosedStarIterable.hpp + ClosedStarIterable.cpp + LinkIterable.hpp + LinkIterable.cpp + OpenStarIterable.hpp + OpenStarIterable.cpp + TopLevelCofacesIterable.hpp + TopLevelCofacesIterable.cpp + SimplexBoundaryIterable.hpp + SimplexBoundaryIterable.cpp +) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/simplex/top_level_cofaces.cpp b/src/wmtk/simplex/top_level_cofaces.cpp index 72d0cde670..ba12b02e35 100644 --- a/src/wmtk/simplex/top_level_cofaces.cpp +++ b/src/wmtk/simplex/top_level_cofaces.cpp @@ -1,5 +1,6 @@ #include "top_level_cofaces.hpp" #include +#include "utils/tuple_vector_to_homogeneous_simplex_vector.hpp" #include #include @@ -14,21 +15,11 @@ namespace wmtk::simplex { namespace { -std::vector tuple_to_simplices(const std::vector& tups, PrimitiveType primitive) -{ - std::vector r; - r.reserve(tups.size()); - std::transform(tups.begin(), tups.end(), std::back_inserter(r), [primitive](const Tuple& t) { - return Simplex(primitive, t); - }); - return r; -} - std::vector top_level_cofaces_tuples_vertex(const TriMesh& mesh, const Tuple& t) { std::vector collection; - std::set touched_cells; + std::set touched_cells; std::queue q; q.push(t); while (!q.empty()) { @@ -72,7 +63,7 @@ std::vector top_level_cofaces_tuples_face(const TriMesh& mesh, const Tupl std::vector top_level_cofaces_tuples_vertex(const TetMesh& mesh, const Tuple& t) { std::vector collection; - std::set touched_cells; + std::set touched_cells; std::queue q; q.push(t); while (!q.empty()) { @@ -108,7 +99,7 @@ std::vector top_level_cofaces_tuples_vertex(const TetMesh& mesh, const Tu std::vector top_level_cofaces_tuples_edge(const TetMesh& mesh, const Tuple& t) { std::vector collection; - std::set touched_cells; + std::set touched_cells; std::queue q; q.push(t); while (!q.empty()) { @@ -223,7 +214,9 @@ top_level_cofaces(const TriMesh& mesh, const Simplex& simplex, const bool sort_a { SimplexCollection collection( mesh, - tuple_to_simplices(top_level_cofaces_tuples(mesh, simplex), PrimitiveType::Face)); + utils::tuple_vector_to_homogeneous_simplex_vector( + top_level_cofaces_tuples(mesh, simplex), + PrimitiveType::Face)); if (sort_and_clean) { collection.sort_and_clean(); } @@ -236,7 +229,9 @@ top_level_cofaces(const TetMesh& mesh, const Simplex& simplex, const bool sort_a { SimplexCollection collection( mesh, - tuple_to_simplices(top_level_cofaces_tuples(mesh, simplex), PrimitiveType::Tetrahedron)); + utils::tuple_vector_to_homogeneous_simplex_vector( + top_level_cofaces_tuples(mesh, simplex), + PrimitiveType::Tetrahedron)); if (sort_and_clean) { collection.sort_and_clean(); } @@ -249,7 +244,9 @@ top_level_cofaces(const Mesh& mesh, const Simplex& simplex, const bool sort_and_ { SimplexCollection collection( mesh, - tuple_to_simplices(top_level_cofaces_tuples(mesh, simplex), mesh.top_simplex_type())); + utils::tuple_vector_to_homogeneous_simplex_vector( + top_level_cofaces_tuples(mesh, simplex), + mesh.top_simplex_type())); if (sort_and_clean) { collection.sort_and_clean(); } diff --git a/src/wmtk/simplex/utils/CMakeLists.txt b/src/wmtk/simplex/utils/CMakeLists.txt new file mode 100644 index 0000000000..8a5f6ee2b6 --- /dev/null +++ b/src/wmtk/simplex/utils/CMakeLists.txt @@ -0,0 +1,5 @@ +set(SRC_FILES + tuple_vector_to_homogeneous_simplex_vector.hpp + tuple_vector_to_homogeneous_simplex_vector.cpp + ) +target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/simplex/utils/tuple_vector_to_homogeneous_simplex_vector.cpp b/src/wmtk/simplex/utils/tuple_vector_to_homogeneous_simplex_vector.cpp new file mode 100644 index 0000000000..5e713cf2d4 --- /dev/null +++ b/src/wmtk/simplex/utils/tuple_vector_to_homogeneous_simplex_vector.cpp @@ -0,0 +1,18 @@ + +#include "tuple_vector_to_homogeneous_simplex_vector.hpp" +#include +#include + +namespace wmtk::simplex::utils { +std::vector tuple_vector_to_homogeneous_simplex_vector( + const std::vector& tups, + PrimitiveType primitive) +{ + std::vector r; + r.reserve(tups.size()); + std::transform(tups.begin(), tups.end(), std::back_inserter(r), [primitive](const Tuple& t) { + return Simplex(primitive, t); + }); + return r; +} +} // namespace wmtk::simplex::utils diff --git a/src/wmtk/simplex/utils/tuple_vector_to_homogeneous_simplex_vector.hpp b/src/wmtk/simplex/utils/tuple_vector_to_homogeneous_simplex_vector.hpp new file mode 100644 index 0000000000..ad8b91d7f1 --- /dev/null +++ b/src/wmtk/simplex/utils/tuple_vector_to_homogeneous_simplex_vector.hpp @@ -0,0 +1,11 @@ +#pragma once +#include +#include +#include + + +namespace wmtk::simplex::utils { +std::vector tuple_vector_to_homogeneous_simplex_vector( + const std::vector&, + PrimitiveType primitive); +} diff --git a/src/wmtk/utils/CMakeLists.txt b/src/wmtk/utils/CMakeLists.txt index 3b06117a0c..5242c768ce 100644 --- a/src/wmtk/utils/CMakeLists.txt +++ b/src/wmtk/utils/CMakeLists.txt @@ -11,5 +11,6 @@ set(SRC_FILES Rational.hpp mesh_utils.hpp mesh_utils.cpp + TupleInspector.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/autogen/utils/TupleInspector.hpp b/src/wmtk/utils/TupleInspector.hpp similarity index 94% rename from src/wmtk/autogen/utils/TupleInspector.hpp rename to src/wmtk/utils/TupleInspector.hpp index 24fdd5cb7a..de9e0f1412 100644 --- a/src/wmtk/autogen/utils/TupleInspector.hpp +++ b/src/wmtk/utils/TupleInspector.hpp @@ -2,7 +2,7 @@ #include -namespace wmtk::autogen::utils { +namespace wmtk::utils { // NOTE: this is just for helping with autogen accessing tuple intenrals. DO NOT USE ELSEWHERE struct TupleInspector { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index c67c9e6505..5f011cddab 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -34,6 +34,11 @@ set(TEST_SOURCES tools/TetMesh_examples.cpp tools/DEBUG_TetMesh.hpp tools/DEBUG_TetMesh.cpp + + tools/DEBUG_Mesh.hpp + tools/DEBUG_Mesh.cpp + tools/DEBUG_MultiMeshManager.hpp + tools/DEBUG_MultiMeshManager.cpp ) add_executable(wmtk_tests ${TEST_SOURCES}) diff --git a/tests/test_3d_operations.cpp b/tests/test_3d_operations.cpp index a23a711575..858f987cb6 100644 --- a/tests/test_3d_operations.cpp +++ b/tests/test_3d_operations.cpp @@ -35,19 +35,19 @@ TEST_CASE("get_split_simplices_to_delete", "[operations][split][3d]") // debug code std::cout << "vertex: " << std::endl; - for (int i = 0; i < ids_to_delete[0].size(); i++) { + for (size_t i = 0; i < ids_to_delete[0].size(); i++) { std::cout << ids_to_delete[0][i] << std::endl; } std::cout << "edge: " << std::endl; - for (int i = 0; i < ids_to_delete[1].size(); i++) { + for (size_t i = 0; i < ids_to_delete[1].size(); i++) { std::cout << ids_to_delete[1][i] << std::endl; } std::cout << "face: " << std::endl; - for (int i = 0; i < ids_to_delete[2].size(); i++) { + for (size_t i = 0; i < ids_to_delete[2].size(); i++) { std::cout << ids_to_delete[2][i] << std::endl; } std::cout << "tet: " << std::endl; - for (int i = 0; i < ids_to_delete[3].size(); i++) { + for (size_t i = 0; i < ids_to_delete[3].size(); i++) { std::cout << ids_to_delete[3][i] << std::endl; } @@ -351,4 +351,4 @@ TEST_CASE("collapse_edge", "[operation][collapse][3d]") REQUIRE(m.is_connectivity_valid()); REQUIRE(m.valid_primitive_count(PT) == 5); } -} \ No newline at end of file +} diff --git a/tests/test_multi_mesh.cpp b/tests/test_multi_mesh.cpp index 922152f4d6..ec8868cc26 100644 --- a/tests/test_multi_mesh.cpp +++ b/tests/test_multi_mesh.cpp @@ -1,6 +1,8 @@ #include #include +#include +#include #include "tools/DEBUG_TriMesh.hpp" #include "tools/TriMesh_examples.hpp" @@ -16,85 +18,263 @@ constexpr PrimitiveType PV = PrimitiveType::Vertex; constexpr PrimitiveType PE = PrimitiveType::Edge; constexpr PrimitiveType PF = PrimitiveType::Face; -TEST_CASE("test_register_child_mesh","[multimesh][2D]") + +namespace {} // namespace + + +TEST_CASE("test_register_child_mesh", "[multimesh][2D]") { DEBUG_TriMesh parent = two_neighbors(); std::shared_ptr child0_ptr = std::make_shared(single_triangle()); - std::vector child0_map = {0}; std::shared_ptr child1_ptr = std::make_shared(one_ear()); - std::vector child1_map = {0,1}; - - MultiMeshManager::register_child_mesh(parent, child0_ptr, child0_map); - MultiMeshManager::register_child_mesh(parent, child1_ptr, child1_map); - - auto p_mul_manager = parent.multi_mesh_manager; - REQUIRE(p_mul_manager.child_meshes.size() == 2); - REQUIRE(p_mul_manager.child_meshes[0] == child0_ptr); - REQUIRE(p_mul_manager.child_meshes[1] == child1_ptr); - - auto [tuple1, tuple2] = MultiMeshManager::read_tuple_map_attribute(p_mul_manager.map_to_child_handles[0], parent, parent.tuple_from_id(PF,0)); - REQUIRE(tuple1 == parent.tuple_from_id(PF,0)); - REQUIRE(tuple2 == child0_ptr->tuple_from_id(PF,0)); - - auto [tuple3, tuple4] = MultiMeshManager::read_tuple_map_attribute(p_mul_manager.map_to_child_handles[0], parent, parent.tuple_from_id(PF,1)); - - REQUIRE(!tuple3.is_null()); - REQUIRE(tuple4.is_null()); - - auto c2_mul_manager = child1_ptr->multi_mesh_manager; - - REQUIRE(c2_mul_manager.is_parent_mesh() == false); - REQUIRE(c2_mul_manager.child_id() == 1); - - auto [tuple5, tuple6] = MultiMeshManager::read_tuple_map_attribute(c2_mul_manager.map_to_parent_handle, *child1_ptr, child1_ptr->tuple_from_id(PF,1)); - REQUIRE(tuple5 == child1_ptr->tuple_from_id(PF,1)); - REQUIRE(tuple6 == parent.tuple_from_id(PF,1)); - REQUIRE(p_mul_manager.is_map_valid(parent) == true); + auto& child0 = *child0_ptr; + auto& child1 = *child1_ptr; + + auto child0_map = multimesh::same_simplex_dimension_surjection(parent, child0, {2}); + auto child1_map = multimesh::same_simplex_dimension_surjection(parent, child1, {0, 1}); + + parent.register_child_mesh(child0_ptr, child0_map); + parent.register_child_mesh(child1_ptr, child1_map); + + const auto& p_mul_manager = parent.multi_mesh_manager(); + const auto& c0_mul_manager = child0.multi_mesh_manager(); + const auto& c1_mul_manager = child1.multi_mesh_manager(); + REQUIRE(p_mul_manager.children().size() == 2); + REQUIRE(p_mul_manager.children()[0].mesh == child0_ptr); + REQUIRE(p_mul_manager.children()[1].mesh == child1_ptr); + REQUIRE(c0_mul_manager.children().size() == 0); + REQUIRE(c1_mul_manager.children().size() == 0); + REQUIRE(&c0_mul_manager.get_root_mesh(child0) == &parent); + REQUIRE(&c1_mul_manager.get_root_mesh(child1) == &parent); + + REQUIRE(p_mul_manager.is_root()); + REQUIRE(!c0_mul_manager.is_root()); + REQUIRE(!c1_mul_manager.is_root()); + + + // test id computation + REQUIRE(parent.absolute_multi_mesh_id().empty()); + REQUIRE(child0.absolute_multi_mesh_id() == std::vector{{0}}); + REQUIRE(child1.absolute_multi_mesh_id() == std::vector{{1}}); + + // test attribute contents + { + const std::string c_to_p_name = + DEBUG_MultiMeshManager::child_to_parent_map_attribute_name(); + const std::string p_to_c0_name = + DEBUG_MultiMeshManager::parent_to_child_map_attribute_name(0); + const std::string p_to_c1_name = + DEBUG_MultiMeshManager::parent_to_child_map_attribute_name(1); + REQUIRE(parent.has_attribute(p_to_c0_name, PF)); + REQUIRE(parent.has_attribute(p_to_c1_name, PF)); + REQUIRE(child0.has_attribute(c_to_p_name, PF)); + REQUIRE(child1.has_attribute(c_to_p_name, PF)); + + auto parent_to_child0_handle = parent.get_attribute_handle(p_to_c0_name, PF); + auto parent_to_child1_handle = parent.get_attribute_handle(p_to_c1_name, PF); + auto child0_to_parent_handle = child0.get_attribute_handle(c_to_p_name, PF); + auto child1_to_parent_handle = child1.get_attribute_handle(c_to_p_name, PF); + + auto parent_to_child0_acc = parent.create_const_accessor(parent_to_child0_handle); + auto parent_to_child1_acc = parent.create_const_accessor(parent_to_child1_handle); + auto child0_to_parent_acc = child0.create_const_accessor(child0_to_parent_handle); + auto child1_to_parent_acc = child1.create_const_accessor(child1_to_parent_handle); + + { + std::vector> p_to_c0_map{ + std::tuple{Tuple(), Tuple()}, + std::tuple{Tuple(), Tuple()}, + std::tuple{parent.tuple_from_id(PF, 2), child0.tuple_from_id(PF, 0)}}; + + std::vector> p_to_c1_map{ + std::tuple{parent.tuple_from_id(PF, 0), child1.tuple_from_id(PF, 0)}, + std::tuple{parent.tuple_from_id(PF, 1), child1.tuple_from_id(PF, 1)}, + std::tuple{Tuple(), Tuple()}, + }; + + std::vector> c0_to_p_map{ + std::tuple{child0.tuple_from_id(PF, 0), parent.tuple_from_id(PF, 2)}}; + + std::vector> c1_to_p_map{ + std::tuple{child1.tuple_from_id(PF, 0), parent.tuple_from_id(PF, 0)}, + std::tuple{child1.tuple_from_id(PF, 1), parent.tuple_from_id(PF, 1)}}; + + + for (long parent_index = 0; parent_index < 3; ++parent_index) { + auto ptuple = parent.tuple_from_id(PF, parent_index); + auto p_to_c0_tuple_tuple = + multimesh::utils::read_tuple_map_attribute(parent_to_child0_acc, ptuple); + auto p_to_c1_tuple_tuple = + multimesh::utils::read_tuple_map_attribute(parent_to_child1_acc, ptuple); + + CHECK(p_to_c0_tuple_tuple == p_to_c0_map[parent_index]); + CHECK(p_to_c1_tuple_tuple == p_to_c1_map[parent_index]); + } + for (size_t child0_index = 0; child0_index < c0_to_p_map.size(); ++child0_index) { + auto tuple = child0.tuple_from_id(PF, child0_index); + auto c0_to_p_tuple_tuple = + multimesh::utils::read_tuple_map_attribute(child0_to_parent_acc, tuple); + + CHECK(c0_to_p_tuple_tuple == c0_to_p_map[child0_index]); + } + for (size_t child1_index = 0; child1_index < c1_to_p_map.size(); ++child1_index) { + auto tuple = child1.tuple_from_id(PF, child1_index); + auto c1_to_p_tuple_tuple = + multimesh::utils::read_tuple_map_attribute(child1_to_parent_acc, tuple); + + CHECK(c1_to_p_tuple_tuple == c1_to_p_map[child1_index]); + } + } + { + std::vector p_to_c0_map{Tuple(), Tuple(), child0.tuple_from_id(PF, 0)}; + + std::vector p_to_c1_map{ + child1.tuple_from_id(PF, 0), + child1.tuple_from_id(PF, 1), + Tuple()}; + + std::vector c0_to_p_map{parent.tuple_from_id(PF, 2)}; + + std::vector c1_to_p_map{ + parent.tuple_from_id(PF, 0), + parent.tuple_from_id(PF, 1)}; + + + for (long parent_index = 0; parent_index < 3; ++parent_index) { + auto ptuple = parent.tuple_from_id(PF, parent_index); + Simplex psimplex = Simplex(PF, ptuple); + + Tuple c0_expected = p_to_c0_map[parent_index]; + if (!c0_expected.is_null()) { + auto c0tuples = parent.map_to_child_tuples(child0, psimplex); + REQUIRE(c0tuples.size() == 1); + CHECK(c0tuples[0] == c0_expected); + } + + Tuple c1_expected = p_to_c1_map[parent_index]; + if (!c1_expected.is_null()) { + auto c1tuples = parent.map_to_child_tuples(child1, psimplex); + REQUIRE(c1tuples.size() == 1); + CHECK(c1tuples[0] == c1_expected); + } + } + for (size_t child0_index = 0; child0_index < c0_to_p_map.size(); ++child0_index) { + auto tuple = child0.tuple_from_id(PF, child0_index); + Simplex csimplex = Simplex(PF, tuple); + auto ptuple = child0.map_to_parent_tuple(csimplex); + CHECK(ptuple == c0_to_p_map[child0_index]); + } + for (size_t child1_index = 0; child1_index < c1_to_p_map.size(); ++child1_index) { + auto tuple = child1.tuple_from_id(PF, child1_index); + Simplex csimplex = Simplex(PF, tuple); + auto ptuple = child1.map_to_parent_tuple(csimplex); + CHECK(ptuple == c1_to_p_map[child1_index]); + } + } + } + + // test actual api calls + { + // try the tuples that should succeed + for (const auto& [ct, pt] : child0_map) { + auto ncts = parent.map_to_child_tuples(child0, Simplex(PrimitiveType::Face, pt)); + REQUIRE(ncts.size() == 1); + auto nct = ncts[0]; + auto npt = child0.map_to_parent_tuple(Simplex(PrimitiveType::Face, ct)); + + CHECK(nct == ct); + CHECK(npt == pt); + } + for (const auto& [ct, pt] : child1_map) { + auto ncts = parent.map_to_child_tuples(child1, Simplex(PrimitiveType::Face, pt)); + REQUIRE(ncts.size() == 1); + auto nct = ncts[0]; + auto npt = child1.map_to_parent_tuple(Simplex(PrimitiveType::Face, ct)); + + CHECK(nct == ct); + CHECK(npt == pt); + } + + + // go through simplex indices that aren't available in the map + for (long index = 0; index < 2; ++index) { + auto pt = parent.tuple_from_id(PF, index); + auto ncts = parent.map_to_child(child0, Simplex(PrimitiveType::Face, pt)); + CHECK(ncts.size() == 0); + } + for (long index = 2; index < 3; ++index) { + auto pt = parent.tuple_from_id(PF, index); + auto ncts = parent.map_to_child(child1, Simplex(PrimitiveType::Face, pt)); + CHECK(ncts.size() == 0); + } + } + + + p_mul_manager.check_map_valid(parent); } -TEST_CASE("test_multi_mesh_navigation","[multimesh][2D]") +TEST_CASE("test_multi_mesh_navigation", "[multimesh][2D]") { DEBUG_TriMesh parent = two_neighbors(); std::shared_ptr child0_ptr = std::make_shared(single_triangle()); - std::vector child0_map = {0}; std::shared_ptr child1_ptr = std::make_shared(one_ear()); - std::vector child1_map = {0,1}; - std::shared_ptr child2_ptr = std::make_shared(two_neighbors_cut_on_edge01()); - std::vector child2_map = {0,1,2}; + std::shared_ptr child2_ptr = + std::make_shared(two_neighbors_cut_on_edge01()); + + auto& child0 = *child0_ptr; + auto& child1 = *child1_ptr; + auto& child2 = *child2_ptr; + + auto child0_map = multimesh::same_simplex_dimension_surjection(parent, child0, {0}); + auto child1_map = multimesh::same_simplex_dimension_surjection(parent, child1, {0, 1}); + auto child2_map = multimesh::same_simplex_dimension_surjection(parent, child2, {0, 1, 2}); + + parent.register_child_mesh(child0_ptr, child0_map); + parent.register_child_mesh(child1_ptr, child1_map); + parent.register_child_mesh(child2_ptr, child2_map); - MultiMeshManager::register_child_mesh(parent, child0_ptr, child0_map); - MultiMeshManager::register_child_mesh(parent, child1_ptr, child1_map); - MultiMeshManager::register_child_mesh(parent, child2_ptr, child2_map); + + auto get_single_child_tuple = [&](const auto& mesh, const auto& tuple) -> Tuple { + auto tups = parent.map_to_child_tuples(mesh, Simplex(PF, tuple)); + REQUIRE(tups.size() == 1); + return tups[0]; + }; Tuple edge = parent.edge_tuple_between_v1_v2(1, 0, 0); - Tuple edge_child0 = MultiMeshManager::map_tuple_between_meshes(parent, *child0_ptr, parent.multi_mesh_manager.map_to_child_handles[0], edge); - Tuple edge_child1 = MultiMeshManager::map_tuple_between_meshes(parent, *child1_ptr, parent.multi_mesh_manager.map_to_child_handles[1], edge); - Tuple edge_child2 = MultiMeshManager::map_tuple_between_meshes(parent, *child2_ptr, parent.multi_mesh_manager.map_to_child_handles[2], edge); + Tuple edge_child0 = get_single_child_tuple(child0, edge); + Tuple edge_child1 = get_single_child_tuple(child1, edge); + Tuple edge_child2 = get_single_child_tuple(child2, edge); - CHECK(edge_child0 == child0_ptr->edge_tuple_between_v1_v2(1, 0, 0)); - CHECK(edge_child1 == child1_ptr->edge_tuple_between_v1_v2(1, 0, 0)); + CHECK(edge_child0 == child0.edge_tuple_between_v1_v2(1, 0, 0)); + CHECK(edge_child1 == child1.edge_tuple_between_v1_v2(1, 0, 0)); CHECK(edge_child2 == child2_ptr->edge_tuple_between_v1_v2(1, 0, 0)); - CHECK(child0_ptr->switch_vertex(edge_child0) == MultiMeshManager::map_tuple_between_meshes(parent, *child0_ptr, parent.multi_mesh_manager.map_to_child_handles[0], parent.switch_vertex(edge))); - CHECK(child1_ptr->switch_vertex(edge_child1) == MultiMeshManager::map_tuple_between_meshes(parent, *child1_ptr, parent.multi_mesh_manager.map_to_child_handles[1], parent.switch_vertex(edge))); - CHECK(child2_ptr->switch_vertex(edge_child2) == MultiMeshManager::map_tuple_between_meshes(parent, *child2_ptr, parent.multi_mesh_manager.map_to_child_handles[2], parent.switch_vertex(edge))); - - CHECK(child0_ptr->switch_edge(edge_child0) == MultiMeshManager::map_tuple_between_meshes(parent, *child0_ptr, parent.multi_mesh_manager.map_to_child_handles[0], parent.switch_edge(edge))); - CHECK(child1_ptr->switch_edge(edge_child1) == MultiMeshManager::map_tuple_between_meshes(parent, *child1_ptr, parent.multi_mesh_manager.map_to_child_handles[1], parent.switch_edge(edge))); - CHECK(child2_ptr->switch_edge(edge_child2) == MultiMeshManager::map_tuple_between_meshes(parent, *child2_ptr, parent.multi_mesh_manager.map_to_child_handles[2], parent.switch_edge(edge))); + for (PrimitiveType pt : {PV, PE}) { + CHECK( + child0.switch_tuple(edge_child0, pt) == + get_single_child_tuple(child0, parent.switch_tuple(edge, pt))); + CHECK( + child1.switch_tuple(edge_child1, pt) == + get_single_child_tuple(child1, parent.switch_tuple(edge, pt))); + CHECK( + child2.switch_tuple(edge_child2, pt) == + get_single_child_tuple(child2, parent.switch_tuple(edge, pt))); + } } -TEST_CASE("test_split_multi_mesh","[multimesh][2D]") +/* +TEST_CASE("test_split_multi_mesh", "[multimesh][2D]") { DEBUG_TriMesh parent = two_neighbors(); std::shared_ptr child0_ptr = std::make_shared(single_triangle()); std::vector child0_map = {0}; std::shared_ptr child1_ptr = std::make_shared(one_ear()); - std::vector child1_map = {0,1}; - std::shared_ptr child2_ptr = std::make_shared(two_neighbors_cut_on_edge01()); - std::vector child2_map = {0,1,2}; + std::vector child1_map = {0, 1}; + std::shared_ptr child2_ptr = + std::make_shared(two_neighbors_cut_on_edge01()); + std::vector child2_map = {0, 1, 2}; MultiMeshManager::register_child_mesh(parent, child0_ptr, child0_map); MultiMeshManager::register_child_mesh(parent, child1_ptr, child1_map); @@ -107,8 +287,8 @@ TEST_CASE("test_split_multi_mesh","[multimesh][2D]") executor.split_edge(); REQUIRE(parent.is_connectivity_valid()); - REQUIRE(child0_ptr->is_connectivity_valid()); - REQUIRE(child1_ptr->is_connectivity_valid()); + REQUIRE(child0.is_connectivity_valid()); + REQUIRE(child1.is_connectivity_valid()); REQUIRE(child2_ptr->is_connectivity_valid()); REQUIRE(parent.multi_mesh_manager.is_map_valid(parent) == true); @@ -117,12 +297,12 @@ TEST_CASE("test_split_multi_mesh","[multimesh][2D]") CHECK(parent.fv_from_fid(4) == Eigen::Matrix(0, 5, 2)); CHECK(parent.fv_from_fid(5) == Eigen::Matrix(3, 1, 5)); CHECK(parent.fv_from_fid(6) == Eigen::Matrix(3, 5, 0)); - CHECK(child0_ptr->fv_from_fid(1) == Eigen::Matrix(3, 1, 2)); - CHECK(child0_ptr->fv_from_fid(2) == Eigen::Matrix(0, 3, 2)); - CHECK(child1_ptr->fv_from_fid(2) == Eigen::Matrix(4, 1, 2)); - CHECK(child1_ptr->fv_from_fid(3) == Eigen::Matrix(0, 4, 2)); - CHECK(child1_ptr->fv_from_fid(4) == Eigen::Matrix(3, 1, 4)); - CHECK(child1_ptr->fv_from_fid(5) == Eigen::Matrix(3, 4, 0)); + CHECK(child0.fv_from_fid(1) == Eigen::Matrix(3, 1, 2)); + CHECK(child0.fv_from_fid(2) == Eigen::Matrix(0, 3, 2)); + CHECK(child1.fv_from_fid(2) == Eigen::Matrix(4, 1, 2)); + CHECK(child1.fv_from_fid(3) == Eigen::Matrix(0, 4, 2)); + CHECK(child1.fv_from_fid(4) == Eigen::Matrix(3, 1, 4)); + CHECK(child1.fv_from_fid(5) == Eigen::Matrix(3, 4, 0)); CHECK(child2_ptr->fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); CHECK(child2_ptr->fv_from_fid(3) == Eigen::Matrix(7, 1, 2)); CHECK(child2_ptr->fv_from_fid(4) == Eigen::Matrix(0, 7, 2)); @@ -135,10 +315,10 @@ TEST_CASE("test_split_multi_mesh","[multimesh][2D]") executor1.split_edge(); REQUIRE(parent.is_connectivity_valid()); - REQUIRE(child0_ptr->is_connectivity_valid()); - REQUIRE(child1_ptr->is_connectivity_valid()); + REQUIRE(child0.is_connectivity_valid()); + REQUIRE(child1.is_connectivity_valid()); REQUIRE(child2_ptr->is_connectivity_valid()); - REQUIRE(parent.multi_mesh_manager.is_map_valid(parent) == true); + REQUIRE(parent.multi_mesh_manager.is_map_valid(parent) == true); CHECK(parent.fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); CHECK(parent.fv_from_fid(3) == Eigen::Matrix(5, 1, 2)); @@ -147,17 +327,17 @@ TEST_CASE("test_split_multi_mesh","[multimesh][2D]") CHECK(parent.fv_from_fid(8) == Eigen::Matrix(6, 5, 2)); CHECK(parent.fv_from_fid(9) == Eigen::Matrix(3, 6, 0)); CHECK(parent.fv_from_fid(10) == Eigen::Matrix(3, 5, 6)); - - CHECK(child0_ptr->fv_from_fid(1) == Eigen::Matrix(3, 1, 2)); - CHECK(child0_ptr->fv_from_fid(3) == Eigen::Matrix(0, 4, 2)); - CHECK(child0_ptr->fv_from_fid(4) == Eigen::Matrix(4, 3, 2)); - - CHECK(child1_ptr->fv_from_fid(2) == Eigen::Matrix(4, 1, 2)); - CHECK(child1_ptr->fv_from_fid(4) == Eigen::Matrix(3, 1, 4)); - CHECK(child1_ptr->fv_from_fid(6) == Eigen::Matrix(0, 5, 2)); - CHECK(child1_ptr->fv_from_fid(7) == Eigen::Matrix(5, 4, 2)); - CHECK(child1_ptr->fv_from_fid(8) == Eigen::Matrix(3, 5, 0)); - CHECK(child1_ptr->fv_from_fid(9) == Eigen::Matrix(3, 4, 5)); + + CHECK(child0.fv_from_fid(1) == Eigen::Matrix(3, 1, 2)); + CHECK(child0.fv_from_fid(3) == Eigen::Matrix(0, 4, 2)); + CHECK(child0.fv_from_fid(4) == Eigen::Matrix(4, 3, 2)); + + CHECK(child1.fv_from_fid(2) == Eigen::Matrix(4, 1, 2)); + CHECK(child1.fv_from_fid(4) == Eigen::Matrix(3, 1, 4)); + CHECK(child1.fv_from_fid(6) == Eigen::Matrix(0, 5, 2)); + CHECK(child1.fv_from_fid(7) == Eigen::Matrix(5, 4, 2)); + CHECK(child1.fv_from_fid(8) == Eigen::Matrix(3, 5, 0)); + CHECK(child1.fv_from_fid(9) == Eigen::Matrix(3, 4, 5)); CHECK(child2_ptr->fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); CHECK(child2_ptr->fv_from_fid(3) == Eigen::Matrix(7, 1, 2)); @@ -168,15 +348,16 @@ TEST_CASE("test_split_multi_mesh","[multimesh][2D]") CHECK(child2_ptr->fv_from_fid(10) == Eigen::Matrix(3, 8, 10)); } -TEST_CASE("test_collapse_multi_mesh","[multimesh][2D]") +TEST_CASE("test_collapse_multi_mesh", "[multimesh][2D]") { DEBUG_TriMesh parent = two_neighbors(); std::shared_ptr child0_ptr = std::make_shared(two_neighbors()); - std::vector child0_map = {0,1,2}; + std::vector child0_map = {0, 1, 2}; std::shared_ptr child1_ptr = std::make_shared(one_ear()); - std::vector child1_map = {0,1}; - std::shared_ptr child2_ptr = std::make_shared(two_neighbors_cut_on_edge01()); - std::vector child2_map = {0,1,2}; + std::vector child1_map = {0, 1}; + std::shared_ptr child2_ptr = + std::make_shared(two_neighbors_cut_on_edge01()); + std::vector child2_map = {0, 1, 2}; MultiMeshManager::register_child_mesh(parent, child0_ptr, child0_map); MultiMeshManager::register_child_mesh(parent, child1_ptr, child1_map); @@ -189,19 +370,18 @@ TEST_CASE("test_collapse_multi_mesh","[multimesh][2D]") executor.collapse_edge(); REQUIRE(parent.is_connectivity_valid()); - REQUIRE(child0_ptr->is_connectivity_valid()); - REQUIRE(child1_ptr->is_connectivity_valid()); + REQUIRE(child0.is_connectivity_valid()); + REQUIRE(child1.is_connectivity_valid()); REQUIRE(child2_ptr->is_connectivity_valid()); REQUIRE(parent.multi_mesh_manager.is_map_valid(parent) == true); CHECK(parent.fv_from_fid(1) == Eigen::Matrix(3, 2, 0)); CHECK(parent.fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); - CHECK(child0_ptr->fv_from_fid(1) == Eigen::Matrix(3, 2, 0)); - CHECK(child0_ptr->fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); - CHECK(child1_ptr->fv_from_fid(1) == Eigen::Matrix(3, 2, 0)); + CHECK(child0.fv_from_fid(1) == Eigen::Matrix(3, 2, 0)); + CHECK(child0.fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); + CHECK(child1.fv_from_fid(1) == Eigen::Matrix(3, 2, 0)); CHECK(child2_ptr->fv_from_fid(1) == Eigen::Matrix(3, 5, 6)); CHECK(child2_ptr->fv_from_fid(2) == Eigen::Matrix(0, 2, 4)); - } - +*/ diff --git a/tests/tools/DEBUG_Mesh.cpp b/tests/tools/DEBUG_Mesh.cpp new file mode 100644 index 0000000000..b9c8365b04 --- /dev/null +++ b/tests/tools/DEBUG_Mesh.cpp @@ -0,0 +1,30 @@ +#include "DEBUG_Mesh.hpp" +#include + +namespace wmtk::tests { + + +bool DEBUG_Mesh::operator==(const DEBUG_Mesh& o) const +{ + return static_cast(*this) == static_cast(o); +} +bool DEBUG_Mesh::operator!=(const DEBUG_Mesh& o) const +{ + return !(*this == o); +} + + +void DEBUG_Mesh::print_state() const {} + + +void DEBUG_Mesh::reserve_attributes(PrimitiveType type, long size) +{ + Mesh::reserve_attributes(type, size); +} + + +Accessor DEBUG_Mesh::get_cell_hash_accessor() +{ + return Mesh::get_cell_hash_accessor(); +} +} // namespace wmtk::tests diff --git a/tests/tools/DEBUG_Mesh.hpp b/tests/tools/DEBUG_Mesh.hpp new file mode 100644 index 0000000000..dc248a801c --- /dev/null +++ b/tests/tools/DEBUG_Mesh.hpp @@ -0,0 +1,52 @@ + +#pragma once +#include +#include "DEBUG_Mesh.hpp" +#include "DEBUG_MultiMeshManager.hpp" + +namespace wmtk::tests { +class DEBUG_Mesh : public virtual Mesh +{ +public: + using Mesh::Mesh; + bool operator==(const DEBUG_Mesh& o) const; + bool operator!=(const DEBUG_Mesh& o) const; + + // uses spdlog to print out a variety of information about the mesh + void print_state() const; + DEBUG_MultiMeshManager& multi_mesh_manager() + { + return reinterpret_cast(m_multi_mesh_manager); + } + const DEBUG_MultiMeshManager& multi_mesh_manager() const + { + return reinterpret_cast(m_multi_mesh_manager); + } + + template + attribute::AccessorBase create_base_accessor(const MeshAttributeHandle& handle) + { + return attribute::AccessorBase(*this, handle); + } + + template + attribute::AccessorBase create_const_base_accessor( + const MeshAttributeHandle& handle) const + { + return attribute::AccessorBase(const_cast(*this), handle); + } + template + attribute::AccessorBase create_base_accessor(const MeshAttributeHandle& handle) const + { + return create_const_base_accessor(handle); + } + + void reserve_attributes(PrimitiveType type, long size); + + + using Mesh::tuple_from_id; + + Accessor get_cell_hash_accessor(); +}; + +} // namespace wmtk::tests diff --git a/tests/tools/DEBUG_MultiMeshManager.cpp b/tests/tools/DEBUG_MultiMeshManager.cpp new file mode 100644 index 0000000000..38bf4c341d --- /dev/null +++ b/tests/tools/DEBUG_MultiMeshManager.cpp @@ -0,0 +1,96 @@ +#include "DEBUG_MultiMeshManager.hpp" +#include +#include +#include +#include "DEBUG_Mesh.hpp" + + +namespace wmtk::tests { +void DEBUG_MultiMeshManager::check_child_mesh_valid(const Mesh& my_mesh, const Mesh& child_mesh) + const +{ + // TODO: implement this +} + + +void DEBUG_MultiMeshManager::check_map_valid(const Mesh& my_mesh) const +{ + for (long index = 0; index < long(children().size()); ++index) { + const auto& child_data = children()[index]; + REQUIRE(bool(child_data.mesh)); + REQUIRE(child_data.mesh->absolute_multi_mesh_id().front() == index); + check_child_map_valid(my_mesh, child_data); + } +} +void DEBUG_MultiMeshManager::check_child_map_valid(const Mesh& my_mesh, const ChildData& child_data) + const +{ + const Mesh& child_mesh = *child_data.mesh; + const auto parent_to_child_handle = child_data.map_handle; + PrimitiveType map_type = child_mesh.top_simplex_type(); + + const std::string c_to_p_name = child_to_parent_map_attribute_name(); + + REQUIRE(child_mesh.has_attribute(c_to_p_name, map_type)); + auto child_to_parent_handle = child_mesh.get_attribute_handle(c_to_p_name, map_type); + auto child_cell_flag_accessor = child_mesh.get_flag_accessor(map_type); + + for (const Tuple& child_tuple : child_mesh.get_all(map_type)) { + // 1. test if all maps in child_mesh exisits + auto [child_tuple_from_child, parent_tuple_from_child] = + multimesh::utils::read_tuple_map_attribute_slow( + child_mesh, + child_to_parent_handle, + child_tuple); + + // 2. test if tuples in maps are valid (and up_to_date) + { + CHECK(child_mesh.is_valid_slow(child_tuple_from_child)); + CHECK(my_mesh.is_valid_slow(parent_tuple_from_child)); + } + + // 3. test if map is symmetric + { + auto [parent_tuple_from_parent, child_tuple_from_parent] = + multimesh::utils::read_tuple_map_attribute_slow( + my_mesh, + parent_to_child_handle, + parent_tuple_from_child); + + CHECK( + (child_tuple_from_child == child_tuple_from_parent && + parent_tuple_from_child == parent_tuple_from_parent)); + } + + // 4. test switch_top_simplex operation + // for 4, current code support only mapping between triangle meshes + if (map_type == PrimitiveType::Face && my_mesh.top_simplex_type() == PrimitiveType::Face) { + Tuple cur_child_tuple = child_tuple; + Tuple cur_parent_tuple = parent_tuple_from_child; + + auto child_to_parent_accessor = + child_mesh.create_const_accessor(child_to_parent_handle); + for (int i = 0; i < 3; i++) { + if (!child_mesh.is_boundary(cur_child_tuple)) { + REQUIRE(!my_mesh.is_boundary(cur_parent_tuple)); + + Tuple child_tuple_opp = child_mesh.switch_face(cur_child_tuple); + Tuple parent_tuple_opp = my_mesh.switch_face(cur_parent_tuple); + + CHECK( + parent_tuple_opp == map_tuple_between_meshes( + child_mesh, + my_mesh, + child_to_parent_accessor, + child_tuple_opp)); + } + cur_child_tuple = child_mesh.switch_edge(child_mesh.switch_vertex(cur_child_tuple)); + cur_parent_tuple = my_mesh.switch_edge(my_mesh.switch_vertex(cur_parent_tuple)); + } + } else { + // TODO: implement other cases + continue; + } + } +} +} // namespace wmtk::tests diff --git a/tests/tools/DEBUG_MultiMeshManager.hpp b/tests/tools/DEBUG_MultiMeshManager.hpp new file mode 100644 index 0000000000..fecc8425a6 --- /dev/null +++ b/tests/tools/DEBUG_MultiMeshManager.hpp @@ -0,0 +1,20 @@ +#pragma once +#include + +namespace wmtk::tests { + +class DEBUG_MultiMeshManager : public MultiMeshManager +{ +public: + using MultiMeshManager::child_to_parent_map_attribute_name; + using MultiMeshManager::children; + using MultiMeshManager::is_root; + using MultiMeshManager::parent_to_child_map_attribute_name; + + + // these run catch2 tests + void check_child_mesh_valid(const Mesh& my_mesh, const Mesh& child_mesh) const; + void check_map_valid(const Mesh& my_mesh) const; + void check_child_map_valid(const Mesh& my_mesh, const ChildData& child_data) const; +}; +} // namespace wmtk::tests diff --git a/tests/tools/DEBUG_TriMesh.hpp b/tests/tools/DEBUG_TriMesh.hpp index e7b1bd63cb..e1c0cebcb2 100644 --- a/tests/tools/DEBUG_TriMesh.hpp +++ b/tests/tools/DEBUG_TriMesh.hpp @@ -1,9 +1,10 @@ #pragma once #include #include +#include "DEBUG_MultiMeshManager.hpp" namespace wmtk::tests { -class DEBUG_TriMesh : public TriMesh +class DEBUG_TriMesh : public TriMesh //, public virtual DEBUG_Mesh { public: using TriMesh::TriMesh; @@ -17,6 +18,10 @@ class DEBUG_TriMesh : public TriMesh // uses spdlog to print out a variety of information about the mesh void print_state() const; + DEBUG_MultiMeshManager& multi_mesh_manager() + { + return reinterpret_cast(m_multi_mesh_manager); + } void print_vf() const; Eigen::Matrix fv_from_fid(const long fid) const; diff --git a/tests/tools/TetMesh_examples.hpp b/tests/tools/TetMesh_examples.hpp index dccd69b172..e039048903 100644 --- a/tests/tools/TetMesh_examples.hpp +++ b/tests/tools/TetMesh_examples.hpp @@ -5,9 +5,9 @@ namespace wmtk::tests_3d { // 0 -// / \\ -// / \ \ -// / \ \ +// / \\ . +// / \ \ . +// / \ \ . // / \ \ 3 // 1 --------- 2 // @@ -35,29 +35,29 @@ TetMesh one_ear(); TetMesh two_ears(); // 0 ---------- 4 -// / \\ // \ -// / \ \ // \ -// / \ \ // \ -// / \ \3 \ +// / \\ // \ . +// / \ \ // \ . +// / \ \ // \ . +// / \ \3 \ . // 1 --------- 2/ -------- 5 tuple edge 2-3 // TetMesh three_incident_tets(); // 0 ---------- 4 -// / \\ // \ -// / \ \ // \ -// / \ \ // \ -// / \ \3 \ +// / \\ // \ . +// / \ \ // \ . +// / \ \ // \ . +// / \ \3 \ . // 1 --------- 2/ -------- 5 tuple edge 2-3 -// \ / /\ \ / -// \ / / \\ / -// \ // \\ / -// \ // \ / +// \ / /\ \ / . +// \ / / \\ / . +// \ // \\ / . +// \ // \ / . // 6 -----------7 // TetMesh six_cycle_tets(); -} // namespace wmtk::tests_3d \ No newline at end of file +} // namespace wmtk::tests_3d