Skip to content

Commit 1107b6b

Browse files
authored
Merge pull request #850 from wildmeshing/dzint/component_simplicial_embedding
Add simplicial embedding component.
2 parents 5b5fffb + f3c7e6d commit 1107b6b

File tree

8 files changed

+654
-0
lines changed

8 files changed

+654
-0
lines changed

components/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,8 @@ add_subdirectory("CDT/wmtk/components/CDT")
3737

3838
add_subdirectory("edge_insertion/wmtk/components/edge_insertion")
3939

40+
add_subdirectory("simplicial_embedding/wmtk/components/simplicial_embedding")
41+
4042
# add_component("export_cache")
4143
# add_component("import_cache")
4244
# add_component("mesh_info")
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
set(COMPONENT_NAME simplicial_embedding)
2+
add_component(${COMPONENT_NAME})
3+
4+
if(NOT ${WMTK_ENABLE_COMPONENT_${COMPONENT_NAME}})
5+
return()
6+
endif()
7+
8+
set(SRC_FILES
9+
SimplicialEmbeddingOptions.hpp
10+
internal/SimplicialEmbedding.hpp
11+
internal/SimplicialEmbedding.cpp
12+
simplicial_embedding.hpp
13+
simplicial_embedding.cpp
14+
)
15+
16+
17+
target_sources(wmtk_${COMPONENT_NAME} PRIVATE ${SRC_FILES})
18+
19+
add_component_test(wmtk::${COMPONENT_NAME}
20+
${PROJECT_SOURCE_DIR}/components/tests/simplicial_embedding/test_simplicial_embedding.cpp)
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
#pragma once
2+
3+
#include <wmtk/Mesh.hpp>
4+
5+
namespace wmtk::components {
6+
7+
struct SimplicialEmbeddingOptions
8+
{
9+
/**
10+
* All simplex dimensions must have an int64_t scalar attribute where the tags are stored.
11+
*/
12+
std::map<PrimitiveType, attribute::MeshAttributeHandle> tag_attributes;
13+
/**
14+
* The value that should be simplicially embedded.
15+
*/
16+
int64_t value;
17+
/**
18+
* Other attributes that should be processed with the default behavior.
19+
*/
20+
std::vector<attribute::MeshAttributeHandle> pass_through_attributes;
21+
/**
22+
* If false, this component forms a simplicial complex out of the tags, i.e., all faces of
23+
* tagged simplices are also tagged.
24+
*/
25+
bool generate_simplicial_embedding = true;
26+
};
27+
28+
} // namespace wmtk::components
Lines changed: 311 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,311 @@
1+
#include "SimplicialEmbedding.hpp"
2+
3+
#include <wmtk/Scheduler.hpp>
4+
#include <wmtk/invariants/SimplexInversionInvariant.hpp>
5+
#include <wmtk/invariants/TodoInvariant.hpp>
6+
#include <wmtk/operations/EdgeSplit.hpp>
7+
#include <wmtk/operations/attribute_new/SplitNewAttributeStrategy.hpp>
8+
#include <wmtk/operations/composite/TetCellSplit.hpp>
9+
#include <wmtk/operations/composite/TriFaceSplit.hpp>
10+
#include <wmtk/simplex/faces.hpp>
11+
#include <wmtk/simplex/faces_single_dimension.hpp>
12+
#include <wmtk/utils/Logger.hpp>
13+
#include <wmtk/utils/primitive_range.hpp>
14+
15+
#include <deque>
16+
17+
namespace wmtk::components::internal {
18+
19+
namespace {
20+
21+
class TagAttribute
22+
{
23+
public:
24+
wmtk::attribute::Accessor<int64_t> m_accessor;
25+
PrimitiveType m_ptype;
26+
int64_t m_val;
27+
28+
TagAttribute(
29+
Mesh& m,
30+
const attribute::MeshAttributeHandle& attribute,
31+
PrimitiveType ptype,
32+
int64_t val)
33+
: m_accessor(m.create_accessor<int64_t>(attribute))
34+
, m_ptype(ptype)
35+
, m_val(val)
36+
{}
37+
38+
TagAttribute(TagAttribute&) = delete;
39+
};
40+
} // namespace
41+
42+
SimplicialEmbedding::SimplicialEmbedding(
43+
Mesh& mesh,
44+
const std::vector<attribute::MeshAttributeHandle>& label_attributes,
45+
const int64_t& value,
46+
const std::vector<attribute::MeshAttributeHandle>& pass_through_attributes)
47+
: m_mesh(mesh)
48+
, m_label_attributes(label_attributes)
49+
, m_value(value)
50+
, m_pass_through_attributes(pass_through_attributes)
51+
{}
52+
53+
void SimplicialEmbedding::regularize_tags(bool generate_simplicial_embedding)
54+
{
55+
using namespace operations;
56+
57+
std::vector<attribute::MeshAttributeHandle> todo_handles;
58+
for (size_t i = 0; i < m_label_attributes.size(); ++i) {
59+
attribute::MeshAttributeHandle todo_handle =
60+
m_mesh.register_attribute<int64_t>("todo", m_label_attributes[i].primitive_type(), 1);
61+
todo_handles.emplace_back(todo_handle);
62+
}
63+
64+
std::deque<TagAttribute> tag_attributes;
65+
for (size_t i = 0; i < m_label_attributes.size(); ++i) {
66+
tag_attributes.emplace_back(
67+
m_mesh,
68+
m_label_attributes[i],
69+
m_label_attributes[i].primitive_type(),
70+
m_value);
71+
}
72+
73+
// make sure tag vector is complete and sorted in descending order
74+
for (size_t i = 0; i < tag_attributes.size(); ++i) {
75+
TagAttribute& a = tag_attributes[i];
76+
if (get_primitive_type_id(a.m_ptype) != m_mesh.top_cell_dimension() - i) {
77+
log_and_throw_error("Tag array must be sorted in descending order starting with "
78+
"the top simplex type up to vertex.");
79+
}
80+
}
81+
82+
83+
// tag all faces of attributes
84+
for (size_t attr_it = 0; attr_it < tag_attributes.size() - 1; ++attr_it) {
85+
const TagAttribute& ta = tag_attributes[attr_it];
86+
for (const Tuple& t : m_mesh.get_all(ta.m_ptype)) {
87+
if (ta.m_accessor.const_scalar_attribute(t) != ta.m_val) {
88+
continue; // t is not tagged
89+
}
90+
91+
const PrimitiveType face_ptype =
92+
get_primitive_type_from_id(get_primitive_type_id(ta.m_ptype) - 1);
93+
const auto faces = simplex::faces_single_dimension_tuples(
94+
m_mesh,
95+
simplex::Simplex(m_mesh, ta.m_ptype, t),
96+
face_ptype);
97+
98+
TagAttribute& face_ta = tag_attributes[attr_it + 1];
99+
for (const Tuple& f : faces) {
100+
face_ta.m_accessor.scalar_attribute(f) = face_ta.m_val;
101+
}
102+
}
103+
}
104+
105+
if (!generate_simplicial_embedding) {
106+
return;
107+
}
108+
109+
// check for inversions
110+
if (m_mesh.has_attribute<double>("vertices", PrimitiveType::Vertex)) {
111+
const auto pos_handle =
112+
m_mesh.get_attribute_handle<double>("vertices", PrimitiveType::Vertex);
113+
if (pos_handle.dimension() == m_mesh.top_cell_dimension() - 1) {
114+
SimplexInversionInvariant<double> inv(pos_handle.mesh(), pos_handle.as<double>());
115+
116+
if (!inv.after({}, m_mesh.get_all(m_mesh.top_simplex_type()))) {
117+
logger().error("Mesh is not inversion free BEFORE simplicial embedding!");
118+
}
119+
}
120+
}
121+
122+
// split untagged simplices that have only tagged faces
123+
for (size_t attr_it = 0; attr_it < tag_attributes.size() - 1;) {
124+
const TagAttribute& ta = tag_attributes[attr_it];
125+
126+
attribute::MeshAttributeHandle& todo_handle = todo_handles[attr_it];
127+
auto todo_acc = m_mesh.create_accessor<int64_t>(todo_handle);
128+
129+
int64_t count_todos = 0;
130+
for (const Tuple& t : m_mesh.get_all(ta.m_ptype)) {
131+
if (ta.m_accessor.const_scalar_attribute(t) == ta.m_val) {
132+
continue; // t is tagged
133+
}
134+
135+
const PrimitiveType face_ptype =
136+
get_primitive_type_from_id(get_primitive_type_id(ta.m_ptype) - 1);
137+
const auto faces = simplex::faces_single_dimension_tuples(
138+
m_mesh,
139+
simplex::Simplex(m_mesh, ta.m_ptype, t),
140+
face_ptype);
141+
142+
const TagAttribute& face_ta = tag_attributes[attr_it + 1];
143+
144+
bool all_faces_are_tagged = true;
145+
146+
for (const Tuple& f : faces) {
147+
if (face_ta.m_accessor.const_scalar_attribute(f) != face_ta.m_val) {
148+
all_faces_are_tagged = false;
149+
break;
150+
}
151+
}
152+
153+
if (all_faces_are_tagged) {
154+
todo_acc.scalar_attribute(t) = 1;
155+
++count_todos;
156+
}
157+
}
158+
159+
// split simplex because all its faces are tagged
160+
Scheduler scheduler;
161+
int64_t count_done = 0;
162+
switch (ta.m_ptype) {
163+
case PrimitiveType::Edge: { // edge split
164+
EdgeSplit op_split(m_mesh);
165+
op_split.add_invariant(std::make_shared<TodoInvariant>(
166+
m_mesh,
167+
std::get<attribute::TypedAttributeHandle<int64_t>>(todo_handle.handle())));
168+
169+
// todos
170+
for (const attribute::MeshAttributeHandle& h : todo_handles) {
171+
op_split.set_new_attribute_strategy(
172+
h,
173+
SplitBasicStrategy::None,
174+
SplitRibBasicStrategy::None);
175+
}
176+
// labels
177+
for (const attribute::MeshAttributeHandle& h : m_label_attributes) {
178+
op_split.set_new_attribute_strategy(
179+
h,
180+
SplitBasicStrategy::Copy,
181+
SplitRibBasicStrategy::None);
182+
}
183+
// pass_through
184+
for (const auto& attr : m_pass_through_attributes) {
185+
op_split.set_new_attribute_strategy(attr);
186+
}
187+
188+
logger().info("split {} edges", count_todos);
189+
while (true) {
190+
const auto stats = scheduler.run_operation_on_all(op_split);
191+
count_done += stats.number_of_successful_operations();
192+
if (stats.number_of_successful_operations() == 0) {
193+
break;
194+
}
195+
}
196+
197+
break;
198+
}
199+
case PrimitiveType::Triangle: { // face split
200+
composite::TriFaceSplit op_face_split(m_mesh);
201+
op_face_split.add_invariant(std::make_shared<TodoInvariant>(
202+
m_mesh,
203+
std::get<attribute::TypedAttributeHandle<int64_t>>(todo_handle.handle())));
204+
205+
// todos
206+
for (const attribute::MeshAttributeHandle& h : todo_handles) {
207+
op_face_split.split().set_new_attribute_strategy(
208+
h,
209+
SplitBasicStrategy::None,
210+
SplitRibBasicStrategy::None);
211+
op_face_split.collapse().set_new_attribute_strategy(h, CollapseBasicStrategy::None);
212+
}
213+
// labels
214+
for (const attribute::MeshAttributeHandle& h : m_label_attributes) {
215+
op_face_split.split().set_new_attribute_strategy(
216+
h,
217+
SplitBasicStrategy::Copy,
218+
SplitRibBasicStrategy::None);
219+
op_face_split.collapse().set_new_attribute_strategy(h, CollapseBasicStrategy::None);
220+
}
221+
// pass_through
222+
for (const auto& attr : m_pass_through_attributes) {
223+
op_face_split.split().set_new_attribute_strategy(attr);
224+
op_face_split.collapse().set_new_attribute_strategy(
225+
attr,
226+
CollapseBasicStrategy::None);
227+
}
228+
229+
230+
logger().info("split {} faces", count_todos);
231+
while (true) {
232+
const auto stats = scheduler.run_operation_on_all(op_face_split);
233+
count_done += stats.number_of_successful_operations();
234+
if (stats.number_of_successful_operations() == 0) {
235+
break;
236+
}
237+
}
238+
239+
break;
240+
}
241+
case PrimitiveType::Tetrahedron: { // tet split
242+
composite::TetCellSplit op_tet_split(m_mesh);
243+
op_tet_split.add_invariant(std::make_shared<TodoInvariant>(
244+
m_mesh,
245+
std::get<attribute::TypedAttributeHandle<int64_t>>(todo_handle.handle())));
246+
247+
// todos
248+
for (const attribute::MeshAttributeHandle& h : todo_handles) {
249+
op_tet_split.split().set_new_attribute_strategy(
250+
h,
251+
SplitBasicStrategy::None,
252+
SplitRibBasicStrategy::None);
253+
op_tet_split.collapse().set_new_attribute_strategy(h, CollapseBasicStrategy::None);
254+
}
255+
// labels
256+
for (const attribute::MeshAttributeHandle& h : m_label_attributes) {
257+
op_tet_split.split().set_new_attribute_strategy(
258+
h,
259+
SplitBasicStrategy::Copy,
260+
SplitRibBasicStrategy::None);
261+
op_tet_split.collapse().set_new_attribute_strategy(h, CollapseBasicStrategy::None);
262+
}
263+
// pass_through
264+
for (const auto& attr : m_pass_through_attributes) {
265+
op_tet_split.split().set_new_attribute_strategy(attr);
266+
op_tet_split.collapse().set_new_attribute_strategy(
267+
attr,
268+
CollapseBasicStrategy::None);
269+
}
270+
271+
logger().info("split {} tetrahedra", count_todos);
272+
while (true) {
273+
const auto stats = scheduler.run_operation_on_all(op_tet_split);
274+
count_done += stats.number_of_successful_operations();
275+
if (stats.number_of_successful_operations() == 0) {
276+
break;
277+
}
278+
}
279+
280+
break;
281+
}
282+
default: log_and_throw_error("unknown primitive type"); break;
283+
}
284+
285+
if (count_done == count_todos) {
286+
++attr_it;
287+
} else {
288+
logger().info(
289+
"{} remain. Regularize same primitive type once more.",
290+
count_todos - count_done);
291+
}
292+
}
293+
294+
// check for inversions
295+
if (m_mesh.has_attribute<double>("vertices", PrimitiveType::Vertex)) {
296+
const auto pos_handle =
297+
m_mesh.get_attribute_handle<double>("vertices", PrimitiveType::Vertex);
298+
SimplexInversionInvariant<double> inv(pos_handle.mesh(), pos_handle.as<double>());
299+
300+
if (pos_handle.dimension() == m_mesh.top_cell_dimension() - 1) {
301+
SimplexInversionInvariant<double> inv(pos_handle.mesh(), pos_handle.as<double>());
302+
303+
if (!inv.after({}, m_mesh.get_all(m_mesh.top_simplex_type()))) {
304+
logger().error("Mesh is not inversion free after simplicial embedding!");
305+
}
306+
}
307+
}
308+
}
309+
310+
311+
} // namespace wmtk::components::internal

0 commit comments

Comments
 (0)