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

mesh_decimation #734

Open
wants to merge 14 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 11 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 e962c0b4ebbd09b94720c3129ac98d1a7a4f9079
GIT_TAG 3dae1070163ebccc61abdd36e59dc4ca2c6f0beb

CONFIGURE_COMMAND ""
BUILD_COMMAND ""
Expand Down
1 change: 1 addition & 0 deletions components/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@ add_component(${WMTK_COMPONENT_PREFIX} "procedural")
add_component(${WMTK_COMPONENT_PREFIX} "fusion")
add_component(${WMTK_COMPONENT_PREFIX} "triangle_insertion")
add_component(${WMTK_COMPONENT_PREFIX} "to_points")
add_component(${WMTK_COMPONENT_PREFIX} "mesh_decimation")

string(LENGTH ${json_components} json_components_length)
math(EXPR json_components_length "${json_components_length}-2")
Expand Down
1 change: 0 additions & 1 deletion components/tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ wmtk::components)
target_include_directories(${WMTK_COMPONENT_TEST_TARGET} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/..)
target_compile_definitions(${WMTK_COMPONENT_TEST_TARGET} PRIVATE WMTK_TEST_DIR=\"${CMAKE_CURRENT_SOURCE_DIR}\")


FetchContent_GetProperties(catch2)
list(APPEND CMAKE_MODULE_PATH ${catch2_SOURCE_DIR}/extras)
include(Catch)
Expand Down
8 changes: 5 additions & 3 deletions components/tests/integration_test.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,9 +88,10 @@ int authenticate_json(const std::string& json_file, const bool compute_validatio
auto tetrahedra = in_args["tests"]["tetrahedra"];
if (meshes.size() != vertices.size() || meshes.size() != edges.size() ||
meshes.size() != faces.size() || meshes.size() != tetrahedra.size()) {
spdlog::error("JSON size missmatch between meshes and vertices edges faces or "
"tetrahedra or meshes key. Add a * "
"to the beginning of filename to allow appends.");
spdlog::error(
"JSON size missmatch between meshes and vertices edges faces or "
"tetrahedra or meshes key. Set true for the parameter \"compute_validation\""
"to allow appends.");
return 2;
}

Expand Down Expand Up @@ -199,4 +200,5 @@ WMTK_INTEGRATION("disk_fan_mm", false);
// WMTK_INTEGRATION("grid",false);
WMTK_INTEGRATION("wildmeshing_2d", false);
WMTK_INTEGRATION("wildmeshing_3d", false);
WMTK_INTEGRATION("mesh_decimation", false);
WMTK_INTEGRATION("marching", false);
87 changes: 87 additions & 0 deletions components/tests/test_component_mesh_decimation.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#include <catch2/catch_test_macros.hpp>
#include <nlohmann/json.hpp>
#include <tools/DEBUG_TetMesh.hpp>
#include <tools/DEBUG_TriMesh.hpp>
#include <tools/TetMesh_examples.hpp>
#include <tools/TriMesh_examples.hpp>
#include <wmtk/Mesh.hpp>
#include <wmtk/TriMesh.hpp>
#include <wmtk/components/mesh_decimation/internal/MeshDecimation.hpp>
#include <wmtk/components/mesh_decimation/internal/MeshDecimationOptions.hpp>
#include <wmtk/components/mesh_decimation/mesh_decimation.hpp>
#include <wmtk/function/simplex/AMIPS.hpp>
#include <wmtk/invariants/TodoInvariant.hpp>
#include <wmtk/io/MeshReader.hpp>
#include <wmtk/io/ParaviewWriter.hpp>
#include <wmtk/operations/EdgeCollapse.hpp>
#include <wmtk/operations/attribute_new/CollapseNewAttributeStrategy.hpp>
#include <wmtk/simplex/link.hpp>
#include <wmtk/utils/mesh_utils.hpp>

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

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

TEST_CASE("component_mesh_decimation_options", "[components][mesh_decimation]")
{
using namespace components::internal;

json o = {
{"input", "input_mesh"},
{"output", "output_mesh"},
{"target_len", 1.0},
{"cell_constraint_tag_name", "tag"},
{"attributes", {"vertices", "tag"}},
{"pass_through", {"dummy"}}};

CHECK_NOTHROW(o.get<MeshDecimationOptions>());
}

TEST_CASE("decimation_test", "[components][2D][3D]")
Zhouyuan-Chen marked this conversation as resolved.
Show resolved Hide resolved
{
using namespace wmtk::components;
wmtk::io::Cache cache("wmtk_cache", ".");

SECTION("3D")
{
auto mesh_in = wmtk::read_mesh(data_dir / "unit_test/meshes/sphere_regularized.hdf5");
Mesh& mesh = *mesh_in;

std::vector<wmtk::attribute::MeshAttributeHandle> keep;
wmtk::attribute::MeshAttributeHandle constrait_cell_tag_handle =
mesh.get_attribute_handle<int64_t>("tag", mesh.top_simplex_type());
wmtk::attribute::MeshAttributeHandle pos_handle =
mesh.get_attribute_handle<double>("vertices", PrimitiveType::Vertex);
keep.emplace_back(constrait_cell_tag_handle);
keep.emplace_back(pos_handle);
mesh.clear_attributes(keep);

std::vector<wmtk::attribute::MeshAttributeHandle> pass_though;
internal::MeshDecimation md(mesh, constrait_cell_tag_handle, 5, pass_though);
md.process();

cache.write_mesh(mesh, "out3d");
}

SECTION("2D")
{
auto mesh_in = wmtk::read_mesh(data_dir / "2d/ellipse_layer/ellipse_01_substructure.hdf5");
Mesh& mesh = *mesh_in;

std::vector<wmtk::attribute::MeshAttributeHandle> keep;
wmtk::attribute::MeshAttributeHandle constrait_cell_tag_handle =
mesh.get_attribute_handle<int64_t>("tag", mesh.top_simplex_type());
wmtk::attribute::MeshAttributeHandle pos_handle =
mesh.get_attribute_handle<double>("vertices", PrimitiveType::Vertex);
keep.emplace_back(constrait_cell_tag_handle);
keep.emplace_back(pos_handle);
mesh.clear_attributes(keep);

std::vector<wmtk::attribute::MeshAttributeHandle> pass_though;
internal::MeshDecimation md(mesh, constrait_cell_tag_handle, 5, pass_though);
md.process();

cache.write_mesh(mesh, "out2d");
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@

set(SRC_FILES
internal/MeshDecimationOptions.hpp
internal/MeshDecimationOptions.cpp
internal/MeshDecimation.hpp
internal/MeshDecimation.cpp
mesh_decimation.hpp
mesh_decimation.cpp
)


#CURRENT_COMPONENT_LIB_NAME is set from the main cmake
target_sources(${CURRENT_COMPONENT_LIB_NAME} PRIVATE ${SRC_FILES})
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
#include "MeshDecimation.hpp"
#include "wmtk/Scheduler.hpp"
#include "wmtk/function/simplex/AMIPS.hpp"
#include "wmtk/invariants/MultiMeshLinkConditionInvariant.hpp"
#include "wmtk/invariants/SimplexInversionInvariant.hpp"
#include "wmtk/invariants/SmallerFunctionInvariant.hpp"
#include "wmtk/invariants/TodoInvariant.hpp"
#include "wmtk/operations/EdgeCollapse.hpp"
#include "wmtk/operations/attribute_new/CollapseNewAttributeStrategy.hpp"
#include "wmtk/operations/attribute_update/AttributeTransferStrategy.hpp"
#include "wmtk/utils/Logger.hpp"

namespace wmtk::components::internal {

MeshDecimation::MeshDecimation(
Mesh& mesh,
attribute::MeshAttributeHandle constrainted_cell_tag_handle,
double target_len,
const std::vector<attribute::MeshAttributeHandle>& pass_through_attributes)
: m_mesh(mesh)
, m_constrainted_cell_tag_handle(constrainted_cell_tag_handle)
, m_target_len(target_len)
, m_pass_through_attributes(pass_through_attributes)
{}

void MeshDecimation::process()
{
using namespace wmtk::attribute;
using namespace wmtk::invariants;
constexpr PrimitiveType PV = PrimitiveType::Vertex;
constexpr PrimitiveType PE = PrimitiveType::Edge;
constexpr PrimitiveType PF = PrimitiveType::Triangle;
constexpr PrimitiveType PT = PrimitiveType::Tetrahedron;

MeshAttributeHandle& cell_tag_handle = m_constrainted_cell_tag_handle;
MeshAttributeHandle position = m_mesh.get_attribute_handle<double>("vertices", PV);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Position should be either in the pass_through attributes or the name has to be given by the user. Please do not hard code it.

Zhouyuan-Chen marked this conversation as resolved.
Show resolved Hide resolved
MeshAttributeHandle todo_edge_handle =
m_mesh.register_attribute<int64_t>("mesh_decimation_todo_edge", PE, 1);
MeshAttributeHandle todo_vertex_handle =
m_mesh.register_attribute<int64_t>("mesh_decimation_todo_vertex", PV, 1);
MeshAttributeHandle edge_len_handle =
m_mesh.register_attribute<double>("mesh_decimation_edge_len", PE, 1);

Accessor<int64_t> acc_cell = m_mesh.create_accessor<int64_t>(cell_tag_handle);
Accessor<double> acc_pos = m_mesh.create_accessor<double>(position);
Accessor<int64_t> acc_edge = m_mesh.create_accessor<int64_t>(todo_edge_handle);
Accessor<int64_t> acc_vertex = m_mesh.create_accessor<int64_t>(todo_vertex_handle);
Accessor<double> acc_len = m_mesh.create_accessor<double>(edge_len_handle);

// build edge tag to protect to topology of the tagged input
switch (m_mesh.top_simplex_type()) {
case PF:
for (const Tuple& edge : m_mesh.get_all(PE)) {
if (m_mesh.is_boundary(PE, edge)) {
acc_vertex.scalar_attribute(edge) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) = 1;

acc_edge.scalar_attribute(edge) = 1;
} else if (
acc_cell.scalar_attribute(edge) !=
acc_cell.scalar_attribute(m_mesh.switch_tuple(edge, PF))) {
acc_vertex.scalar_attribute(edge) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) = 1;

acc_edge.scalar_attribute(edge) = 1;
}
}

Check warning on line 67 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L67

Added line #L67 was not covered by tests
break;
case PT: {
for (const Tuple& face : m_mesh.get_all(PF)) {
if (m_mesh.is_boundary(PF, face)) {
acc_vertex.scalar_attribute(face) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuple(face, PV)) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuples(face, {PE, PV})) = 1;

acc_edge.scalar_attribute(face) = 1;
acc_edge.scalar_attribute(m_mesh.switch_tuple(face, PE)) = 1;
acc_edge.scalar_attribute(m_mesh.switch_tuples(face, {PV, PE})) = 1;
} else if (
acc_cell.scalar_attribute(face) !=
acc_cell.scalar_attribute(m_mesh.switch_tuple(face, PT))) {
acc_vertex.scalar_attribute(face) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuple(face, PV)) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuples(face, {PE, PV})) = 1;

acc_edge.scalar_attribute(face) = 1;
acc_edge.scalar_attribute(m_mesh.switch_tuple(face, PE)) = 1;
acc_edge.scalar_attribute(m_mesh.switch_tuples(face, {PV, PE})) = 1;
}
}

Check warning on line 90 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L90

Added line #L90 was not covered by tests
break;
}
case PE:
case PV:
default:
log_and_throw_error("MeshDecimation.cpp: mesh_decimation component only supports tetmesh "

Check warning on line 96 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L93-L96

Added lines #L93 - L96 were not covered by tests
"and trimesh for now!");
}

// stage 1: tag edges incident to the surface
for (const Tuple& edge : m_mesh.get_all(PE)) {
if (acc_vertex.scalar_attribute(edge) == 1 ||
acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) == 1) {
acc_edge.scalar_attribute(edge) = 1;
}
}

Check warning on line 106 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L106

Added line #L106 was not covered by tests
// stage 2: tag vertices incident to the tagged edge
for (const Tuple& edge : m_mesh.get_all(PE)) {
if (acc_edge.scalar_attribute(edge) == 1) {
acc_vertex.scalar_attribute(edge) = 1;
acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) = 1;
}
}

Check warning on line 113 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L113

Added line #L113 was not covered by tests
// stage 3: tag edges incident to the tagged vertices
for (const Tuple& edge : m_mesh.get_all(PE)) {
if (acc_vertex.scalar_attribute(edge) == 1 ||
acc_vertex.scalar_attribute(m_mesh.switch_tuple(edge, PV)) == 1) {
acc_edge.scalar_attribute(edge) = 1;
}
}

Check warning on line 120 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L120

Added line #L120 was not covered by tests

// Storing edge lengths and Edge length update
auto compute_edge_length = [](const Eigen::MatrixXd& P) -> Eigen::VectorXd {
assert(P.cols() == 2);
assert(P.rows() == 2 || P.rows() == 3);
return Eigen::VectorXd::Constant(1, (P.col(0) - P.col(1)).norm());
};
auto edge_length_update =
std::make_shared<wmtk::operations::SingleAttributeTransferStrategy<double, double>>(
edge_len_handle,
position,
compute_edge_length);
edge_length_update->run_on_all();

auto m_prio_short_edges_first = [&](const simplex::Simplex& s) {
assert(s.primitive_type() == PrimitiveType::Edge);
auto acc = m_mesh.create_accessor<double>(edge_len_handle);
return std::vector<double>({acc.scalar_attribute(s.tuple())});
};

auto op_collapse = std::make_shared<operations::EdgeCollapse>(m_mesh);

op_collapse->add_invariant(
std::make_shared<TodoInvariant>(m_mesh, todo_edge_handle.as<int64_t>(), 0));
op_collapse->add_invariant(
std::make_shared<TodoSmallerInvariant>(m_mesh, edge_len_handle.as<double>(), m_target_len));

auto m_amips = std::make_shared<function::AMIPS>(m_mesh, position);
auto m_link_conditions = std::make_shared<InvariantCollection>(m_mesh);
m_link_conditions->add(std::make_shared<MultiMeshLinkConditionInvariant>(m_mesh));
auto m_function_invariant =
std::make_shared<SmallerFunctionInvariant>(m_mesh.top_simplex_type(), m_amips, 30);
auto m_inversion_invariant =
std::make_shared<SimplexInversionInvariant>(m_mesh, position.as<double>());

op_collapse->add_invariant(m_link_conditions);
op_collapse->add_invariant(m_inversion_invariant);
op_collapse->add_invariant(m_function_invariant);

op_collapse->add_transfer_strategy(edge_length_update);

op_collapse->set_priority(m_prio_short_edges_first);

op_collapse->set_new_attribute_strategy(
position,
wmtk::operations::CollapseBasicStrategy::Mean);
op_collapse->set_new_attribute_strategy(todo_vertex_handle);

op_collapse->set_new_attribute_strategy(todo_edge_handle);
op_collapse->set_new_attribute_strategy(edge_len_handle);
op_collapse->set_new_attribute_strategy(cell_tag_handle);

// pass_through
for (const auto& attr : m_pass_through_attributes) {
op_collapse->set_new_attribute_strategy(attr);

Check warning on line 175 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimation.cpp#L175

Added line #L175 was not covered by tests
}

while (true) {
Scheduler scheduler;
SchedulerStats pass_stats = scheduler.run_operation_on_all(*op_collapse);
m_mesh.consolidate();
if (pass_stats.number_of_successful_operations() == 0) {
break;
}
}
}

} // namespace wmtk::components::internal
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
#pragma once

#include <wmtk/Mesh.hpp>

namespace wmtk::components::internal {

class MeshDecimation
{
public:
MeshDecimation(
Mesh& mesh,
attribute::MeshAttributeHandle constrainted_cell_tag_handle,
double target_len,
const std::vector<attribute::MeshAttributeHandle>& pass_through_attributes);

void process();

private:
Mesh& m_mesh;

attribute::MeshAttributeHandle m_constrainted_cell_tag_handle;
double m_target_len;

std::vector<attribute::MeshAttributeHandle> m_pass_through_attributes;
};

} // namespace wmtk::components::internal
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
#include "MeshDecimationOptions.hpp"

#include <wmtk/utils/Logger.hpp>

namespace wmtk::components::internal {

void to_json(nlohmann::json& j, MeshDecimationOptions& o)

Check warning on line 7 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp#L7

Added line #L7 was not covered by tests
{
j = {
{"input", o.input},
{"output", o.output},
{"target_len", o.target_len},
{"cell_constraint_tag_name", o.cell_constraint_tag_name},
{"attributes", o.attributes},
{"pass_through", o.pass_through}};
}

Check warning on line 16 in components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp

View check run for this annotation

Codecov / codecov/patch

components/wmtk_components/mesh_decimation/wmtk/components/mesh_decimation/internal/MeshDecimationOptions.cpp#L9-L16

Added lines #L9 - L16 were not covered by tests

void from_json(const nlohmann::json& j, MeshDecimationOptions& o)
{
o.input = j.at("input");
o.output = j.at("output");
o.target_len = j.at("target_len");
o.cell_constraint_tag_name = j.at("cell_constraint_tag_name");
j.at("attributes").get_to(o.attributes);
j.at("pass_through").get_to(o.pass_through);
}

} // namespace wmtk::components::internal
Loading
Loading