Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Dzint/non manifold simplification #743

Open
wants to merge 11 commits into
base: main
Choose a base branch
from
2 changes: 1 addition & 1 deletion cmake/recipes/tests/wmtk_data.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ ExternalProject_Add(
SOURCE_DIR ${WMTK_DATA_ROOT}

GIT_REPOSITORY https://github.com/wildmeshing/data.git
GIT_TAG f3797257743dc9e0a269a16e91c6bec619e15146
GIT_TAG ece41ebb363e35a27cb9c728cc68e297f0ac6257

CONFIGURE_COMMAND ""
BUILD_COMMAND ""
Expand Down
2 changes: 2 additions & 0 deletions components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,8 @@ add_component(${WMTK_COMPONENT_PREFIX} "isotropic_remeshing")
add_component(${WMTK_COMPONENT_PREFIX} "marching")
add_component(${WMTK_COMPONENT_PREFIX} "mesh_info")
add_component(${WMTK_COMPONENT_PREFIX} "multimesh")
add_component(${WMTK_COMPONENT_PREFIX} "non_manifold_input")
add_component(${WMTK_COMPONENT_PREFIX} "non_manifold_simplification")
add_component(${WMTK_COMPONENT_PREFIX} "regular_space")
add_component(${WMTK_COMPONENT_PREFIX} "tag_intersection")
add_component(${WMTK_COMPONENT_PREFIX} "wildmeshing")
Expand Down
109 changes: 109 additions & 0 deletions components/tests/test_component_non_manifold_input.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
#include <catch2/catch_test_macros.hpp>
#include <nlohmann/json.hpp>
#include <wmtk/components/base/Paths.hpp>
#include <wmtk/components/non_manifold_input/non_manifold_input.hpp>
#include <wmtk/io/Cache.hpp>
#include <wmtk/io/ParaviewWriter.hpp>

using namespace wmtk::components::base;
using json = nlohmann::json;
using namespace wmtk;

const std::filesystem::path data_dir = WMTK_DATA_DIR;

TEST_CASE("component_non_manifold_input", "[components][non_manifold_input]")
{
wmtk::io::Cache cache("wmtk_cache", ".");

json j;

size_t nmv_expect = 0;
size_t nme_expect = 0;

SECTION("manifold")
{
const std::filesystem::path input_file = data_dir / "200683_sf.msh";
j = {
{"type", "non_manifold_input"},
{"name", "input_mesh"},
{"file", input_file.string()},
{"ignore_z", false},
{"tetrahedron_attributes", json::array()},
{"non_manifold_vertex_label", "nmv"},
{"non_manifold_edge_label", "nme"},
{"non_manifold_tag_value", 1}};
}
SECTION("non_manifold_edge")
{
const std::filesystem::path input_file = data_dir / "edge_with_four_triangles.msh";
j = {
{"type", "non_manifold_input"},
{"name", "input_mesh"},
{"file", input_file.string()},
{"ignore_z", false},
{"tetrahedron_attributes", json::array()},
{"non_manifold_vertex_label", "nmv"},
{"non_manifold_edge_label", "nme"},
{"non_manifold_tag_value", 1}};

nmv_expect = 2;
nme_expect = 1;
}
SECTION("non_manifold_vertex")
{
const std::filesystem::path input_file = data_dir / "hour_glass.msh";
j = {
{"type", "non_manifold_input"},
{"name", "input_mesh"},
{"file", input_file.string()},
{"ignore_z", false},
{"tetrahedron_attributes", json::array()},
{"non_manifold_vertex_label", "nmv"},
{"non_manifold_edge_label", "nme"},
{"non_manifold_tag_value", 1}};

nmv_expect = 1;
nme_expect = 0;
}

Paths p;

CHECK_NOTHROW(wmtk::components::non_manifold_input(p, j, cache));

// validation
{
auto m = cache.read_mesh("input_mesh");

REQUIRE(m->has_attribute<int64_t>("nmv", PrimitiveType::Vertex));
REQUIRE(m->has_attribute<int64_t>("nme", PrimitiveType::Edge));

auto nmv_handle = m->get_attribute_handle<int64_t>("nmv", PrimitiveType::Vertex);
auto nme_handle = m->get_attribute_handle<int64_t>("nme", PrimitiveType::Edge);
auto nmv_acc = m->create_accessor<int64_t>(nmv_handle);
auto nme_acc = m->create_accessor<int64_t>(nme_handle);

size_t nmv_counter = 0;
size_t nme_counter = 0;

for (const Tuple& t : m->get_all(PrimitiveType::Vertex)) {
if (nmv_acc.const_scalar_attribute(t) == 1) {
++nmv_counter;
}
}
for (const Tuple& t : m->get_all(PrimitiveType::Edge)) {
if (nme_acc.const_scalar_attribute(t) == 1) {
++nme_counter;
}
}

CHECK(nmv_counter == nmv_expect);
CHECK(nme_counter == nme_expect);


if (true) {
wmtk::io::ParaviewWriter
writer("mesh_with_non_manifold_tags", "vertices", *m, true, true, true, false);
m->serialize(writer);
}
}
}
57 changes: 57 additions & 0 deletions components/tests/test_component_non_manifold_simplification.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
#include <catch2/catch_test_macros.hpp>
#include <nlohmann/json.hpp>
#include <wmtk/components/base/Paths.hpp>
#include <wmtk/components/non_manifold_input/non_manifold_input.hpp>
#include <wmtk/components/non_manifold_simplification/non_manifold_simplification.hpp>
#include <wmtk/io/Cache.hpp>
#include <wmtk/io/ParaviewWriter.hpp>
#include <wmtk/utils/Logger.hpp>

using namespace wmtk::components::base;
using json = nlohmann::json;
using namespace wmtk;

const std::filesystem::path data_dir = WMTK_DATA_DIR;


TEST_CASE("component_non_manifold_simplification", "[components][non_manifold_simplification]")
{
logger().set_level(spdlog::level::off);

wmtk::io::Cache cache("wmtk_cache", ".");

// input
{
const std::filesystem::path input_file = data_dir / "hour_glass.msh";
json j = {
{"type", "non_manifold_input"},
{"name", "input_mesh"},
{"file", input_file.string()},
{"ignore_z", false},
{"tetrahedron_attributes", json::array()},
{"non_manifold_vertex_label", "nmv"},
{"non_manifold_edge_label", "nme"},
{"non_manifold_tag_value", 1}};

REQUIRE_NOTHROW(wmtk::components::non_manifold_input(Paths(), j, cache));
}

// simplification
{
json j = {
{"type", "non_manifold_simplification"},
{"input", "input_mesh"},
{"output", "output_mesh"},
{"position", "vertices"},
{"iterations", 2},
{"length_abs", -1},
{"length_rel", 10},
{"envelope_size", 0.001},
{"non_manifold_vertex_label", "nmv"},
{"non_manifold_edge_label", "nme"},
{"non_manifold_tag_value", 1},
{"pass_through", json::array()}};

REQUIRE_NOTHROW(wmtk::components::non_manifold_simplification(Paths(), j, cache));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include <wmtk/Mesh.hpp>
#include <wmtk/components/base/resolve_path.hpp>
#include <wmtk/io/MeshReader.hpp>
#include <wmtk/utils/Logger.hpp>
#include <wmtk/utils/mesh_utils.hpp>

#include "internal/InputOptions.hpp"
Expand All @@ -22,7 +23,9 @@ void input(const base::Paths& paths, const nlohmann::json& j, io::Cache& cache)
}

std::shared_ptr<Mesh> mesh = read_mesh(file, options.ignore_z, options.tetrahedron_attributes);
assert(mesh->is_connectivity_valid());
if (!mesh->is_connectivity_valid()) {
wmtk::logger().warn("Invalid topology! For non-manifold meshes this is expected.");
}

cache.write_mesh(*mesh, options.name);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
set(SRC_FILES
NonManifoldInputOptions.hpp
non_manifold_input.cpp
non_manifold_input.hpp)

#CURRENT_COMPONENT_LIB_NAME is set from the main cmake
target_sources(${CURRENT_COMPONENT_LIB_NAME} PRIVATE ${SRC_FILES})
target_link_libraries(${CURRENT_COMPONENT_LIB_NAME} PRIVATE wmtkc_input)
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
#pragma once

#include <wmtk/components/base/json_utils.hpp>

#include <nlohmann/json.hpp>

namespace wmtk {
namespace components {

struct NonManifoldInputOptions
{
std::string name;
std::filesystem::path file;
bool ignore_z;
std::vector<std::string> tetrahedron_attributes;
std::string non_manifold_vertex_label;
std::string non_manifold_edge_label;
int64_t non_manifold_tag_value;
};

NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE(
NonManifoldInputOptions,
name,
file,
ignore_z,
tetrahedron_attributes,
non_manifold_vertex_label,
non_manifold_edge_label,
non_manifold_tag_value);

} // namespace components
} // namespace wmtk
Original file line number Diff line number Diff line change
@@ -0,0 +1,125 @@
#include "non_manifold_input.hpp"

#include <wmtk/components/input/input.hpp>
#include <wmtk/simplex/faces_single_dimension.hpp>
#include <wmtk/simplex/top_dimension_cofaces.hpp>
#include <wmtk/utils/Logger.hpp>

#include "NonManifoldInputOptions.hpp"

void wmtk::components::non_manifold_input(
const base::Paths& paths,
const nlohmann::json& j,
io::Cache& cache)
{
NonManifoldInputOptions options = j.get<NonManifoldInputOptions>();

assert(options.tetrahedron_attributes
.empty()); // tetrahedron attributes might be deleted in this component

// call input component
nlohmann::json input_j = j;
input_j.erase("non_manifold_vertex_label");
input_j.erase("non_manifold_edge_label");
input_j.erase("non_manifold_tag_value");
input(paths, j, cache);

auto mesh_in = cache.read_mesh(options.name);

auto& m = *mesh_in;

if (m.top_simplex_type() == PrimitiveType::Vertex) {
logger().warn("Unnecessary use of non-manifold input component. Point meshes cannot be "

Check warning on line 32 in components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp#L32

Added line #L32 was not covered by tests
"non-manifold.");
return;

Check warning on line 34 in components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp#L34

Added line #L34 was not covered by tests
}

if (m.top_simplex_type() == PrimitiveType::Tetrahedron) {
logger().warn("This component was never tested for tet meshes. It should work but proceed "

Check warning on line 38 in components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp#L38

Added line #L38 was not covered by tests
"with care (and probably add a unit test for tet meshes).");
}

//////////////////////////
// find non-manifoldness

auto vertex_top_cofaces =
m.register_attribute<int64_t>("vertex_top_cofaces", PrimitiveType::Vertex, 1);
auto edge_top_cofaces =
m.register_attribute<int64_t>("edge_top_cofaces", PrimitiveType::Edge, 1);

auto v_tc_acc = m.create_accessor<int64_t>(vertex_top_cofaces);
auto e_tc_acc = m.create_accessor<int64_t>(edge_top_cofaces);

for (const Tuple& t : m.get_all(m.top_simplex_type())) {
const auto vertices = simplex::faces_single_dimension_tuples(
m,
simplex::Simplex(m.top_simplex_type(), t),
PrimitiveType::Vertex);

for (const Tuple& v : vertices) {
v_tc_acc.scalar_attribute(v)++;
}

const auto edges = simplex::faces_single_dimension_tuples(
m,
simplex::Simplex(m.top_simplex_type(), t),
PrimitiveType::Edge);

for (const Tuple& e : edges) {
e_tc_acc.scalar_attribute(e)++;
}
}

auto non_manifold_vertex_tag =
m.register_attribute<int64_t>(options.non_manifold_vertex_label, PrimitiveType::Vertex, 1);
auto non_manifold_edge_tag =
m.register_attribute<int64_t>(options.non_manifold_edge_label, PrimitiveType::Edge, 1);

auto nmv_acc = m.create_accessor<int64_t>(non_manifold_vertex_tag);
auto nme_acc = m.create_accessor<int64_t>(non_manifold_edge_tag);

// check if the stored number of incident top simplices is the same as the one that can be
// reached by navigation on the mesh

size_t nmv_counter = 0; // number of non-manifold vertices
for (const Tuple& v : m.get_all(PrimitiveType::Vertex)) {
const size_t n_top_cofaces =
simplex::top_dimension_cofaces_tuples(m, simplex::Simplex::vertex(v)).size();

if (n_top_cofaces != v_tc_acc.const_scalar_attribute(v)) {
nmv_acc.scalar_attribute(v) = options.non_manifold_tag_value;
nmv_counter++;
}
}

Check warning on line 93 in components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp#L93

Added line #L93 was not covered by tests

size_t nme_counter = 0; // number of non-manifold edges
for (const Tuple& e : m.get_all(PrimitiveType::Edge)) {
const size_t n_top_cofaces =
simplex::top_dimension_cofaces_tuples(m, simplex::Simplex::edge(e)).size();

if (n_top_cofaces != e_tc_acc.const_scalar_attribute(e)) {
nme_acc.scalar_attribute(e) = options.non_manifold_tag_value;
nme_counter++;
}
}

Check warning on line 104 in components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/non_manifold_input/wmtk/components/non_manifold_input/non_manifold_input.cpp#L104

Added line #L104 was not covered by tests

logger().info(
"The mesh {} contains {} non-manifold vertices and {} non-manifold edges",
options.name,
nmv_counter,
nme_counter);

if (nmv_counter > 0 || nme_counter > 0) {
logger().info("Updating mesh {} in cache.", options.name);

auto pos_handle = m.get_attribute_handle<double>("vertices", PrimitiveType::Vertex);

m.clear_attributes({pos_handle, non_manifold_vertex_tag, non_manifold_edge_tag});

cache.write_mesh(m, options.name);
} else {
logger().info(
"Mesh {} does not contain any non-manfoldness. No tags are added.",
options.name);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#pragma once

#include <nlohmann/json.hpp>
#include <wmtk/io/Cache.hpp>

#include <wmtk/components/base/Paths.hpp>

namespace wmtk::components {

void non_manifold_input(const base::Paths& paths, const nlohmann::json& j, io::Cache& cache);

} // namespace wmtk::components
Loading
Loading