From a0cb1e6c08f07fb039dc3a65df9cf8c228dc7b53 Mon Sep 17 00:00:00 2001 From: Michael Tao Date: Wed, 4 Oct 2023 11:48:34 -0400 Subject: [PATCH] adding updates to autogen to help iwth unit tests and adding tests --- src/wmtk/autogen/CMakeLists.txt | 5 + src/wmtk/autogen/is_ccw.cpp | 30 ++ src/wmtk/autogen/is_ccw.hpp | 14 + src/wmtk/autogen/local_switch_tuple.cpp | 15 + src/wmtk/autogen/local_switch_tuple.hpp | 9 + src/wmtk/autogen/tet_mesh/is_ccw.cpp | 3 + src/wmtk/autogen/tet_mesh/is_ccw.hpp | 4 +- .../tet_mesh/local_id_table_offset.cpp | 21 +- .../tet_mesh/local_id_table_offset.hpp | 4 + .../autogen/tet_mesh/local_switch_tuple.hpp | 1 + src/wmtk/autogen/tri_mesh/is_ccw.cpp | 2 + .../tri_mesh/local_id_table_offset.cpp | 10 + .../tri_mesh/local_id_table_offset.hpp | 4 + .../autogen/tri_mesh/local_switch_tuple.hpp | 1 + tests/CMakeLists.txt | 1 + tests/test_autogen.cpp | 271 ++++++++++++++++++ 16 files changed, 392 insertions(+), 3 deletions(-) create mode 100644 src/wmtk/autogen/is_ccw.cpp create mode 100644 src/wmtk/autogen/is_ccw.hpp create mode 100644 src/wmtk/autogen/local_switch_tuple.cpp create mode 100644 src/wmtk/autogen/local_switch_tuple.hpp create mode 100644 tests/test_autogen.cpp diff --git a/src/wmtk/autogen/CMakeLists.txt b/src/wmtk/autogen/CMakeLists.txt index c0364a2250..442dc47edf 100644 --- a/src/wmtk/autogen/CMakeLists.txt +++ b/src/wmtk/autogen/CMakeLists.txt @@ -18,6 +18,11 @@ set(SRC_FILES tri_mesh/local_id_table_offset.cpp tri_mesh/local_id_table_offset.hpp + is_ccw.hpp + is_ccw.cpp + local_switch_tuple.hpp + local_switch_tuple.cpp + utils/TupleInspector.hpp ) target_sources(wildmeshing_toolkit PRIVATE ${SRC_FILES}) diff --git a/src/wmtk/autogen/is_ccw.cpp b/src/wmtk/autogen/is_ccw.cpp new file mode 100644 index 0000000000..67f0a164c9 --- /dev/null +++ b/src/wmtk/autogen/is_ccw.cpp @@ -0,0 +1,30 @@ +#include "is_ccw.hpp" +#include +#include +#include +namespace wmtk::autogen { +bool is_ccw(PrimitiveType pt, const Tuple& t) +{ + switch (pt) { + case PrimitiveType::Face: return tri_mesh::is_ccw(t); + case PrimitiveType::Tetrahedron: return tet_mesh::is_ccw(t); + case PrimitiveType::Vertex: + case PrimitiveType::Edge: + default: throw "notimplemented"; + } + return false; +} + +// validates whether the tuple local ids are valid for computing ccw'ness +bool tuple_is_valid_for_ccw(PrimitiveType pt, const Tuple& t) +{ + switch (pt) { + case PrimitiveType::Face: return tri_mesh::tuple_is_valid_for_ccw(t); + case PrimitiveType::Tetrahedron: return tet_mesh::tuple_is_valid_for_ccw(t); + case PrimitiveType::Vertex: + case PrimitiveType::Edge: + default: throw "notimplemented"; + } + return false; +} +} // namespace wmtk::autogen diff --git a/src/wmtk/autogen/is_ccw.hpp b/src/wmtk/autogen/is_ccw.hpp new file mode 100644 index 0000000000..100606f21d --- /dev/null +++ b/src/wmtk/autogen/is_ccw.hpp @@ -0,0 +1,14 @@ +#pragma once +#include + +namespace wmtk { +class Tuple; +} + +// NOTE: this header primarily exists to simplify unit testing, not really for use +namespace wmtk::autogen { +bool is_ccw(PrimitiveType ptype, const Tuple& t); + +// validates whether the tuple local ids are valid for computing ccw'ness +bool tuple_is_valid_for_ccw(PrimitiveType ptype, const Tuple& t); +} // namespace wmtk::autogen diff --git a/src/wmtk/autogen/local_switch_tuple.cpp b/src/wmtk/autogen/local_switch_tuple.cpp new file mode 100644 index 0000000000..ae0745bbe2 --- /dev/null +++ b/src/wmtk/autogen/local_switch_tuple.cpp @@ -0,0 +1,15 @@ +#include "local_switch_tuple.hpp" +#include +#include +namespace wmtk::autogen { +Tuple local_switch_tuple(PrimitiveType mesh_type, const Tuple& t, PrimitiveType pt) +{ + switch (mesh_type) { + case PrimitiveType::Face: return tri_mesh::local_switch_tuple(t, pt); + case PrimitiveType::Tetrahedron: return tet_mesh::local_switch_tuple(t, pt); + case PrimitiveType::Vertex: + case PrimitiveType::Edge: throw "notimplemented"; + } + return Tuple(); +} +} // namespace wmtk::autogen diff --git a/src/wmtk/autogen/local_switch_tuple.hpp b/src/wmtk/autogen/local_switch_tuple.hpp new file mode 100644 index 0000000000..87c2472783 --- /dev/null +++ b/src/wmtk/autogen/local_switch_tuple.hpp @@ -0,0 +1,9 @@ + +#pragma once +#include +#include + +// NOTE: this header primarily exists to simplify unit testing, not really for use +namespace wmtk::autogen { +Tuple local_switch_tuple(PrimitiveType mesh_type, const Tuple& t, PrimitiveType pt); +} diff --git a/src/wmtk/autogen/tet_mesh/is_ccw.cpp b/src/wmtk/autogen/tet_mesh/is_ccw.cpp index 1f039bf210..2ca4b4c14c 100644 --- a/src/wmtk/autogen/tet_mesh/is_ccw.cpp +++ b/src/wmtk/autogen/tet_mesh/is_ccw.cpp @@ -1,5 +1,7 @@ #include "is_ccw.hpp" +#include +#include #include #include "autogenerated_tables.hpp" #include "local_id_table_offset.hpp" @@ -7,6 +9,7 @@ namespace wmtk::autogen::tet_mesh { bool is_ccw(const Tuple& tuple) { + assert(tuple_is_valid_for_ccw(tuple)); using namespace utils; const long offset = local_id_table_offset(tuple); return auto_3d_table_ccw[offset] == 1; diff --git a/src/wmtk/autogen/tet_mesh/is_ccw.hpp b/src/wmtk/autogen/tet_mesh/is_ccw.hpp index 0018b7390b..84b8ca1ac5 100644 --- a/src/wmtk/autogen/tet_mesh/is_ccw.hpp +++ b/src/wmtk/autogen/tet_mesh/is_ccw.hpp @@ -1,5 +1,7 @@ #pragma once -#include +namespace wmtk { +class Tuple; +} namespace wmtk::autogen::tet_mesh { bool is_ccw(const Tuple& t); 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 1993eb7243..63e1eecaa7 100644 --- a/src/wmtk/autogen/tet_mesh/local_id_table_offset.cpp +++ b/src/wmtk/autogen/tet_mesh/local_id_table_offset.cpp @@ -6,8 +6,25 @@ namespace wmtk::autogen::tet_mesh { long local_id_table_offset(const Tuple& tuple) { using namespace utils; - return TupleInspector::local_vid(tuple) * 6 * 4 + TupleInspector::local_eid(tuple) * 4 + - TupleInspector::local_fid(tuple); + long value = TupleInspector::local_vid(tuple) * 6 * 4 + TupleInspector::local_eid(tuple) * 4 + + TupleInspector::local_fid(tuple); + + + // value = (TupleInspector::local_vid(tuple) * 6 + TupleInspector::local_eid(tuple)) * 4 + + // TupleInspector::local_fid(tuple); + return value; +} + +std::array lvid_leid_lfid_from_table_offset(long table_offset) +{ + std::array r; + auto& [lvid, leid, lfid] = r; + lfid = table_offset % 4; + + long ve_offset = table_offset / 4; + leid = ve_offset % 6; + lvid = ve_offset / 6; + return r; } } // namespace wmtk::autogen::tet_mesh diff --git a/src/wmtk/autogen/tet_mesh/local_id_table_offset.hpp b/src/wmtk/autogen/tet_mesh/local_id_table_offset.hpp index bd74c73f4a..caede9d639 100644 --- a/src/wmtk/autogen/tet_mesh/local_id_table_offset.hpp +++ b/src/wmtk/autogen/tet_mesh/local_id_table_offset.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include @@ -6,4 +7,7 @@ namespace wmtk::autogen::tet_mesh { // computes the offset of a tuple's local ids in the tables long local_id_table_offset(const Tuple& t); +// returns a lvid/leid/lfid associated iwth a particular tuple offset +std::array lvid_leid_lfid_from_table_offset(long table_offset); + } // namespace wmtk::autogen::tet_mesh diff --git a/src/wmtk/autogen/tet_mesh/local_switch_tuple.hpp b/src/wmtk/autogen/tet_mesh/local_switch_tuple.hpp index 6d9bb0931e..3ed761407c 100644 --- a/src/wmtk/autogen/tet_mesh/local_switch_tuple.hpp +++ b/src/wmtk/autogen/tet_mesh/local_switch_tuple.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include namespace wmtk::autogen::tet_mesh { diff --git a/src/wmtk/autogen/tri_mesh/is_ccw.cpp b/src/wmtk/autogen/tri_mesh/is_ccw.cpp index 5485c189e6..8007d7aed0 100644 --- a/src/wmtk/autogen/tri_mesh/is_ccw.cpp +++ b/src/wmtk/autogen/tri_mesh/is_ccw.cpp @@ -1,5 +1,6 @@ #include "is_ccw.hpp" +#include #include #include "autogenerated_tables.hpp" #include "local_id_table_offset.hpp" @@ -7,6 +8,7 @@ namespace wmtk::autogen::tri_mesh { bool is_ccw(const Tuple& tuple) { + assert(tuple_is_valid_for_ccw(tuple)); using namespace utils; const long offset = local_id_table_offset(tuple); return auto_2d_table_ccw[offset] == 1; 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 e99927a060..f2acd9ec8c 100644 --- a/src/wmtk/autogen/tri_mesh/local_id_table_offset.cpp +++ b/src/wmtk/autogen/tri_mesh/local_id_table_offset.cpp @@ -9,4 +9,14 @@ long local_id_table_offset(const Tuple& tuple) return TupleInspector::local_vid(tuple) * 3 + TupleInspector::local_eid(tuple); } +std::array lvid_leid_from_table_offset(long table_offset) +{ + std::array r; + auto& [lvid, leid] = r; + + lvid = table_offset / 3; + leid = table_offset % 3; + return r; +} + } // namespace wmtk::autogen::tri_mesh diff --git a/src/wmtk/autogen/tri_mesh/local_id_table_offset.hpp b/src/wmtk/autogen/tri_mesh/local_id_table_offset.hpp index 407f733661..6b29f5b560 100644 --- a/src/wmtk/autogen/tri_mesh/local_id_table_offset.hpp +++ b/src/wmtk/autogen/tri_mesh/local_id_table_offset.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include @@ -6,5 +7,8 @@ namespace wmtk::autogen::tri_mesh { // computes the offset of a tuple's local ids in the tables long local_id_table_offset(const Tuple& t); +// returns a lvid/leid associated iwth a particular tuple offset +std::array lvid_leid_from_table_offset(long table_offset); + } // namespace wmtk::autogen::tri_mesh diff --git a/src/wmtk/autogen/tri_mesh/local_switch_tuple.hpp b/src/wmtk/autogen/tri_mesh/local_switch_tuple.hpp index 3a67107896..f8ff6fbcff 100644 --- a/src/wmtk/autogen/tri_mesh/local_switch_tuple.hpp +++ b/src/wmtk/autogen/tri_mesh/local_switch_tuple.hpp @@ -1,4 +1,5 @@ #pragma once +#include #include namespace wmtk::autogen::tri_mesh { diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt index 08a53809f1..69df6ecdd1 100644 --- a/tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -10,6 +10,7 @@ lagrange_include_modules(io) # Sources set(TEST_SOURCES test_topology.cpp + test_autogen.cpp test_tuple.cpp test_tuple_2d.cpp test_tuple_3d.cpp diff --git a/tests/test_autogen.cpp b/tests/test_autogen.cpp new file mode 100644 index 0000000000..f2d60fe9d4 --- /dev/null +++ b/tests/test_autogen.cpp @@ -0,0 +1,271 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using namespace wmtk; +using namespace wmtk::autogen; +; +namespace { + +std::vector primitives_up_to(PrimitiveType pt) +{ + std::vector r; + + switch (pt) { + case PrimitiveType::Tetrahedron: r.emplace_back(PrimitiveType::Face); [[fallthrough]]; + case PrimitiveType::Face: r.emplace_back(PrimitiveType::Edge); [[fallthrough]]; + case PrimitiveType::Edge: r.emplace_back(PrimitiveType::Vertex); [[fallthrough]]; + case PrimitiveType::Vertex: + default: break; + } + return r; +} + + +long max_tuple_count(PrimitiveType pt) +{ + switch (pt) { + case PrimitiveType::Face: return long(std::size(wmtk::autogen::tri_mesh::auto_2d_table_ccw)); + case PrimitiveType::Tetrahedron: + return long(std::size(wmtk::autogen::tet_mesh::auto_3d_table_ccw)); + case PrimitiveType::Vertex: + case PrimitiveType::Edge: break; + } + return -1; +} + +Tuple tuple_from_offset_id(PrimitiveType pt, int offset) +{ + long lvid = 0, leid = 0, lfid = 0, gcid = 0, hash = 0; + + switch (pt) { + case PrimitiveType::Face: { + // bug in the standard? tie should work :-( + auto r = tri_mesh::lvid_leid_from_table_offset(offset); + lvid = r[0]; + leid = r[1]; + } break; + case PrimitiveType::Tetrahedron: { + auto r = tet_mesh::lvid_leid_lfid_from_table_offset(offset); + lvid = r[0]; + leid = r[1]; + lfid = r[2]; + } break; + case PrimitiveType::Vertex: + case PrimitiveType::Edge: break; + } + + Tuple r(lvid, leid, lfid, gcid, hash); + if (!tuple_is_valid_for_ccw(pt, r)) { + r = Tuple(); + } + return r; +} + +std::vector all_valid_local_tuples(PrimitiveType pt) +{ + std::vector tups; + tups.reserve(max_tuple_count(pt)); + for (long idx = 0; idx < max_tuple_count(pt); ++idx) { + tups.emplace_back(tuple_from_offset_id(pt, idx)); + } + + tups.erase( + std::remove_if( + tups.begin(), + tups.end(), + [](const Tuple& t) -> bool { return t.is_null(); }), + tups.end()); + return tups; +} +} // namespace + +TEST_CASE("tuple_autogen_sizes", "[tuple]") +{ + size_t valid_face = 6; + size_t valid_tet = 24; + + REQUIRE(all_valid_local_tuples(PrimitiveType::Face).size() == valid_face); + REQUIRE(all_valid_local_tuples(PrimitiveType::Tetrahedron).size() == valid_tet); + + auto get_array_range = [](const auto& array) -> std::array { + return std::array{{array, array + std::size(array)}}; + }; + {// ccw check + {// tri + auto ccw_range = get_array_range(tri_mesh::auto_2d_table_ccw); + size_t count = std::count_if(ccw_range[0], ccw_range[1], [](long v) { return v != -1; }); + CHECK(count == valid_face); +} +{ + auto ccw_range = get_array_range(tet_mesh::auto_3d_table_ccw); + size_t count = std::count_if(ccw_range[0], ccw_range[1], [](long v) { return v != -1; }); + CHECK(count == valid_tet); +} +} +{{// tri + auto range = get_array_range(tri_mesh::auto_2d_table_vertex); +size_t count = + std::count_if(range[0], range[1], [](const long v[2]) { return v[0] != -1 && v[1] != -1; }); +CHECK(count == valid_face); +} +{ // tri + auto range = get_array_range(tri_mesh::auto_2d_table_edge); + size_t count = + std::count_if(range[0], range[1], [](const long v[2]) { return v[0] != -1 && v[1] != -1; }); + CHECK(count == valid_face); +} +} +{ + { // tet + auto range = get_array_range(tet_mesh::auto_3d_table_vertex); + size_t count = std::count_if(range[0], range[1], [](const long v[3]) { + return v[0] != -1 && v[1] != -1 && v[2] != -1; + }); + CHECK(count == valid_tet); + } + { // tet + auto range = get_array_range(tet_mesh::auto_3d_table_edge); + size_t count = std::count_if(range[0], range[1], [](const long v[3]) { + return v[0] != -1 && v[1] != -1 && v[2] != -1; + }); + CHECK(count == valid_tet); + } + { // tet + auto range = get_array_range(tet_mesh::auto_3d_table_face); + size_t count = std::count_if(range[0], range[1], [](const long v[3]) { + return v[0] != -1 && v[1] != -1 && v[2] != -1; + }); + CHECK(count == valid_tet); + } +} +} + +TEST_CASE("tuple_autogen_id_inversion", "[tuple]") +{ + // when other meshes are available add them here + for (PrimitiveType pt : {PrimitiveType::Face, PrimitiveType::Tetrahedron}) { + for (long idx = 0; idx < max_tuple_count(pt); ++idx) { + Tuple t = tuple_from_offset_id(pt, idx); + if (t.is_null()) { + continue; + } else { + switch (pt) { + case PrimitiveType::Face: { + CHECK(idx == tri_mesh::local_id_table_offset(t)); + break; + } + case PrimitiveType::Tetrahedron: { + CHECK(idx == tet_mesh::local_id_table_offset(t)); + break; + } + case PrimitiveType::Vertex: + case PrimitiveType::Edge: break; + } + } + } + } +} + +TEST_CASE("tuple_autogen_ptype_is_ccw_equivalent", "[tuple]") +{ + { + auto tuples = all_valid_local_tuples(PrimitiveType::Face); + for (const auto& t : tuples) { + CHECK(tri_mesh::is_ccw(t) == is_ccw(PrimitiveType::Face, t)); + } + } + + { + auto tuples = all_valid_local_tuples(PrimitiveType::Tetrahedron); + for (const auto& t : tuples) { + CHECK(tet_mesh::is_ccw(t) == is_ccw(PrimitiveType::Tetrahedron, t)); + } + } +} + +TEST_CASE("tuple_autogen_local_id_inversion", "[tuple]") +{ + // NOTE: this works because we assume the unused ids are = 0; from tuple_from_offset_id + // above + { + auto tuples = all_valid_local_tuples(PrimitiveType::Face); + for (const auto& t : tuples) { + long id = tri_mesh::local_id_table_offset(t); + auto [lvid, leid] = tri_mesh::lvid_leid_from_table_offset(id); + Tuple nt(lvid, leid, 0, 0, 0); + long nid = tri_mesh::local_id_table_offset(nt); + + CHECK(t == nt); + CHECK(id == nid); + } + } + + { + auto tuples = all_valid_local_tuples(PrimitiveType::Tetrahedron); + for (const auto& t : tuples) { + long id = tet_mesh::local_id_table_offset(t); + auto [lvid, leid, lfid] = tet_mesh::lvid_leid_lfid_from_table_offset(id); + Tuple nt(lvid, leid, lfid, 0, 0); + long nid = tet_mesh::local_id_table_offset(nt); + CHECK(t == nt); + CHECK(id == nid); + } + } +} + +TEST_CASE("tuple_autogen_ptype_local_switch_tuple_equivalent", "[tuple]") +{ + { + auto tuples = all_valid_local_tuples(PrimitiveType::Face); + for (const auto& t : tuples) { + for (PrimitiveType pt : primitives_up_to(PrimitiveType::Face)) { + CHECK( + tri_mesh::local_switch_tuple(t, pt) == + local_switch_tuple(PrimitiveType::Face, t, pt)); + } + } + } + + { + auto tuples = all_valid_local_tuples(PrimitiveType::Tetrahedron); + for (const auto& t : tuples) { + for (PrimitiveType pt : primitives_up_to(PrimitiveType::Tetrahedron)) { + CHECK( + tet_mesh::local_switch_tuple(t, pt) == + local_switch_tuple(PrimitiveType::Tetrahedron, t, pt)); + } + } + } +} + + +TEST_CASE("tuple_autogen_switch_still_valid", "[tuple]") +{ + // when other meshes are available add them here + for (PrimitiveType mesh_type : {PrimitiveType::Face /*, PrimitiveType::Tetrahedron*/}) { + auto tuples = all_valid_local_tuples(mesh_type); + + for (const auto& t : tuples) { + CHECK(tuple_is_valid_for_ccw(mesh_type, t)); + // for (PrimitiveType pt : primitives_up_to(mesh_type)) { + // CHECK(tuple_is_valid_for_ccw(mesh_type, local_switch_tuple(mesh_type, t, + // pt))); + // } + } + } +}