Skip to content

Commit d8130a1

Browse files
committed
Add "input_names" and "length_region" + change how sizing field is updated.
1 parent cdef884 commit d8130a1

8 files changed

Lines changed: 294 additions & 76 deletions

File tree

components/image_simulation/wmtk/components/image_simulation/ImageSimulationMesh.cpp

Lines changed: 175 additions & 75 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,8 @@
2828

2929
#include <limits>
3030

31+
#include "expression_parser/Parser.hpp"
32+
3133
namespace wmtk::components::image_simulation {
3234

3335

@@ -274,6 +276,50 @@ CellTag wmtk::components::image_simulation::ImageSimulationMesh::string_set_to_c
274276
return cell_tag;
275277
}
276278

279+
void ImageSimulationMesh::set_length_regions(const nlohmann::json& length_region_json)
280+
{
281+
if (!length_region_json.is_array()) {
282+
log_and_throw_error(
283+
"length_region should be an array of objects, each defining a region and its target "
284+
"length.");
285+
}
286+
287+
for (const auto& region_json : length_region_json) {
288+
if (!region_json.contains("tags")) {
289+
log_and_throw_error("Each length_region entry must contain a 'tags' field.");
290+
}
291+
const std::string tags_str_set = region_json["tags"];
292+
auto& [expr, length] = m_length_regions.emplace_back();
293+
expr = expression_parser::parse(tags_str_set, m_tag_name_to_id);
294+
295+
length = region_json["length"];
296+
double length_rel = region_json["length_rel"];
297+
if (length < 0 && length_rel < 0) {
298+
log_and_throw_error(
299+
"Each length_region entry must specify at least one of 'length' or 'length_rel'.");
300+
}
301+
302+
if (length_rel > 0) {
303+
length = length_rel * m_params.diag_l;
304+
}
305+
}
306+
307+
// apply length regions to vertices
308+
for (const auto& [expr, length] : m_length_regions) {
309+
for (const Tuple& t : get_tets()) {
310+
const auto tid = t.tid(*this);
311+
if (!expr->eval(m_tet_attribute[tid].tags)) {
312+
continue;
313+
}
314+
const auto vs = oriented_tet_vids(tid);
315+
for (const size_t& vid : vs) {
316+
auto& s = m_vertex_attribute[vid].m_sizing_scalar;
317+
s = std::min(s, length / m_params.l);
318+
}
319+
}
320+
}
321+
}
322+
277323
bool ImageSimulationMesh::adjust_sizing_field_serial(double max_energy)
278324
{
279325
logger().info("#vertices {}, #tets {}", vert_capacity(), tet_capacity());
@@ -282,114 +328,164 @@ bool ImageSimulationMesh::adjust_sizing_field_serial(double max_energy)
282328
double filter_energy = std::max(max_energy / 100, stop_filter_energy);
283329
filter_energy = std::min(filter_energy, 100.);
284330

285-
const auto recover_scalar = 1.5;
286-
const auto refine_scalar = 0.5;
287-
const auto min_refine_scalar = m_params.l_min / m_params.l;
331+
const double recover_scalar = 1.5;
332+
const double refine_scalar = 0.5;
333+
const double min_refine_scalar = m_params.l_min / m_params.l;
288334

289-
// outputs scale_multipliers
290-
std::vector<double> scale_multipliers(vert_capacity(), recover_scalar);
335+
// // outputs scale_multipliers
336+
// std::vector<double> scale_multipliers(vert_capacity(), recover_scalar);
291337

292338
std::vector<Vector3d> pts;
339+
std::map<size_t, double> pts_scalars;
293340
std::queue<size_t> v_queue;
294341

295342
for (int i = 0; i < tet_capacity(); i++) {
296-
auto t = tuple_from_tet(i);
297-
if (!t.is_valid(*this)) continue;
298-
auto tid = t.tid(*this);
299-
if (std::cbrt(m_tet_attribute[tid].m_quality) < filter_energy) continue;
300-
auto vs = oriented_tet_vids(t);
343+
const Tuple t = tuple_from_tet(i);
344+
if (!t.is_valid(*this)) {
345+
continue;
346+
}
347+
const size_t tid = t.tid(*this);
348+
if (std::cbrt(m_tet_attribute[tid].m_quality) < filter_energy) {
349+
continue;
350+
}
351+
const auto vs = oriented_tet_vids(t);
301352
Vector3d c(0, 0, 0);
353+
double s = 0;
302354
for (int j = 0; j < 4; j++) {
303355
c += (m_vertex_attribute[vs[j]].m_posf);
304356
v_queue.emplace(vs[j]);
305-
// std::cout << vs[j] << " ";
357+
s = std::max(s, m_vertex_attribute[vs[j]].m_sizing_scalar);
306358
}
359+
pts_scalars[pts.size()] = s;
307360
pts.emplace_back(c / 4);
308361
}
309362

310-
// std::cout << std::endl;
311-
312-
313363
logger().info("filter energy {} Low Quality Tets {}", filter_energy, pts.size());
314364

315-
// debug code
316-
// std::queue<size_t> v_queue_serial;
317-
// for (tbb::concurrent_queue<size_t>::const_iterator i(v_queue.unsafe_begin());
318-
// i != v_queue.unsafe_end();
319-
// ++i) {
320-
// // std::cout << *i << " ";
321-
// v_queue_serial.push(*i);
322-
// }
323-
// std::cout << std::endl;
324-
325365
const double R = m_params.l * 1.8;
326366

327367
int sum = 0;
328368
int adjcnt = 0;
329369

330-
std::vector<bool> visited(vert_capacity(), false);
331-
332370
KNN knn(pts);
333371

334-
std::vector<size_t> cache_one_ring;
335-
// size_t vid;
336-
337-
while (!v_queue.empty()) {
338-
// std::cout << vid << " ";
339-
sum++;
340-
size_t vid = v_queue.front();
341-
// std::cout << vid << " ";
342-
v_queue.pop();
343-
if (visited[vid]) continue;
344-
visited[vid] = true;
345-
adjcnt++;
346-
347-
auto& pos_v = m_vertex_attribute[vid].m_posf;
348-
double sq_dist = 0.;
349-
uint32_t idx;
350-
knn.nearest_neighbor(pos_v, idx, sq_dist);
351-
const double dist = std::sqrt(sq_dist);
352-
353-
if (dist > R) { // outside R-ball, unmark.
372+
bool is_hit_min_edge_length = false;
373+
/**
374+
* Iterate through all vertices.
375+
* For each vertex, find all pts in the R-ball neighborhood.
376+
* Compute scalar based on the distance to the point.
377+
* Take smallest of all computed values.
378+
*
379+
* If no neighbor, multiply by recover_scalar.
380+
*/
381+
for (int i = 0; i < vert_capacity(); i++) {
382+
const Tuple v = tuple_from_vertex(i);
383+
if (!v.is_valid(*this)) {
354384
continue;
355385
}
386+
const size_t vid = v.vid(*this);
387+
const auto& pos_v = m_vertex_attribute[vid].m_posf;
388+
389+
std::vector<nanoflann::ResultItem<uint32_t, double>> matches;
390+
knn.r_nearest_neighbors(pos_v, R * R, matches);
356391

357-
scale_multipliers[vid] = std::min(
358-
scale_multipliers[vid],
359-
dist / R * (1 - refine_scalar) + refine_scalar); // linear interpolate
392+
auto& v_scalar = m_vertex_attribute[vid].m_sizing_scalar;
360393

361-
auto vids = get_one_ring_vids_for_vertex_adj(vid, cache_one_ring);
362-
for (size_t n_vid : vids) {
363-
if (visited[n_vid]) continue;
364-
v_queue.push(n_vid);
394+
if (matches.empty()) {
395+
v_scalar = std::min(recover_scalar * v_scalar, 1.);
396+
continue;
365397
}
366-
}
367398

368-
// std::cout << std::endl;
399+
for (const auto& [index, sq_dist] : matches) {
400+
const auto& pt = pts[index];
401+
const double dist = std::sqrt(sq_dist);
402+
// linear interpolate between refine_scalar and 1 based on distance
403+
double u = dist / R * (1 - refine_scalar) + refine_scalar;
404+
double scalar = u * pts_scalars[index];
405+
v_scalar = std::min(v_scalar, scalar);
406+
}
369407

370-
std::cout << sum << " " << adjcnt << std::endl;
408+
if (v_scalar < min_refine_scalar) {
409+
v_scalar = min_refine_scalar;
410+
is_hit_min_edge_length = true;
411+
}
412+
}
371413

372-
std::atomic_bool is_hit_min_edge_length = false;
414+
// apply length regions to vertices
415+
for (const auto& [expr, length] : m_length_regions) {
416+
for (const Tuple& t : get_tets()) {
417+
const auto tid = t.tid(*this);
418+
if (!expr->eval(m_tet_attribute[tid].tags)) {
419+
continue;
420+
}
421+
const auto vs = oriented_tet_vids(tid);
422+
for (const size_t& vid : vs) {
423+
auto& s = m_vertex_attribute[vid].m_sizing_scalar;
424+
s = std::min(s, length / m_params.l);
425+
}
426+
}
427+
}
373428

374-
for (int i = 0; i < vert_capacity(); i++) {
375-
auto v = tuple_from_vertex(i);
376-
if (!v.is_valid(*this)) continue;
377-
auto vid = v.vid(*this);
378-
// std::cout << vid << " ";
379-
auto& v_attr = m_vertex_attribute[vid];
429+
// std::vector<bool> visited(vert_capacity(), false);
430+
// std::vector<size_t> cache_one_ring;
431+
// // size_t vid;
432+
433+
// while (!v_queue.empty()) {
434+
// sum++;
435+
// size_t vid = v_queue.front();
436+
// v_queue.pop();
437+
// if (visited[vid]) {
438+
// continue;
439+
// }
440+
// visited[vid] = true;
441+
// adjcnt++;
442+
443+
// const Vector3d& pos_v = m_vertex_attribute[vid].m_posf;
444+
// double sq_dist = 0.;
445+
// uint32_t idx;
446+
// knn.nearest_neighbor(pos_v, idx, sq_dist);
447+
448+
// const double dist = std::sqrt(sq_dist);
449+
450+
// if (dist > R) { // outside R-ball, unmark.
451+
// continue;
452+
// }
453+
454+
// scale_multipliers[vid] = std::min(
455+
// scale_multipliers[vid],
456+
// dist / R * (1 - refine_scalar) + refine_scalar); // linear interpolate
457+
458+
// const auto vids = get_one_ring_vids_for_vertex_adj(vid, cache_one_ring);
459+
// for (size_t n_vid : vids) {
460+
// if (visited[n_vid]) {
461+
// continue;
462+
// }
463+
// v_queue.push(n_vid);
464+
// }
465+
// }
380466

381-
auto new_scale = v_attr.m_sizing_scalar * scale_multipliers[vid];
382-
if (new_scale > 1)
383-
v_attr.m_sizing_scalar = 1;
384-
else if (new_scale < min_refine_scalar) {
385-
is_hit_min_edge_length = true;
386-
v_attr.m_sizing_scalar = min_refine_scalar;
387-
} else
388-
v_attr.m_sizing_scalar = new_scale;
389-
}
390-
// std::cout << std::endl;
467+
// std::cout << sum << " " << adjcnt << std::endl;
468+
469+
// bool is_hit_min_edge_length = false;
470+
// for (int i = 0; i < vert_capacity(); i++) {
471+
// const Tuple v = tuple_from_vertex(i);
472+
// if (!v.is_valid(*this)) {
473+
// continue;
474+
// }
475+
// const size_t vid = v.vid(*this);
476+
// auto& v_attr = m_vertex_attribute[vid];
477+
478+
// const double new_scale = v_attr.m_sizing_scalar * scale_multipliers[vid];
479+
// if (new_scale > 1)
480+
// v_attr.m_sizing_scalar = 1;
481+
// else if (new_scale < min_refine_scalar) {
482+
// is_hit_min_edge_length = true;
483+
// v_attr.m_sizing_scalar = min_refine_scalar;
484+
// } else
485+
// v_attr.m_sizing_scalar = new_scale;
486+
// }
391487

392-
return is_hit_min_edge_length.load();
488+
return is_hit_min_edge_length;
393489
}
394490

395491
void bfs_orient(const Eigen::MatrixXi& F, Eigen::MatrixXi& FF, Eigen::VectorXi& C)
@@ -1100,7 +1196,11 @@ void ImageSimulationMesh::write_vtu(const std::string& path)
11001196
paraviewo::VTUWriter writer;
11011197

11021198
for (size_t j = 0; j < m_tags_count; ++j) {
1103-
writer.add_cell_field(fmt::format("tag_{}", j), tags[j]);
1199+
if (m_tag_id_to_name.count(j)) {
1200+
writer.add_cell_field(m_tag_id_to_name[j], tags[j]);
1201+
} else {
1202+
writer.add_cell_field(fmt::format("tag_{}", j), tags[j]);
1203+
}
11041204
}
11051205
writer.add_cell_field("quality", amips);
11061206
writer.add_field("sizing_field", v_sizing_field);

components/image_simulation/wmtk/components/image_simulation/ImageSimulationMesh.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,8 @@
3131
#include <igl/remove_unreferenced.h>
3232
#include <memory>
3333

34+
#include "expression_parser/Expression.hpp"
35+
3436
namespace wmtk::components::image_simulation {
3537

3638
class VertexAttributes
@@ -138,6 +140,9 @@ class ImageSimulationMesh : public wmtk::TetMesh
138140
std::shared_ptr<SampleEnvelope> m_envelope_orig;
139141
double m_envelope_eps = -1;
140142

143+
using ExprPtr = expression_parser::ExpressionPtr;
144+
std::vector<std::tuple<ExprPtr, double>> m_length_regions;
145+
141146
bool m_collapse_check_quality = true;
142147

143148
// for open boundary
@@ -325,6 +330,8 @@ class ImageSimulationMesh : public wmtk::TetMesh
325330

326331
CellTag string_set_to_cell_tag(const std::set<std::string>& str_set);
327332

333+
void set_length_regions(const nlohmann::json& length_region_json);
334+
328335
double get_length2(const Tuple& l) const;
329336

330337
////// Attributes related

components/image_simulation/wmtk/components/image_simulation/image_simulation.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,6 +112,7 @@ void run_3D(const nlohmann::json& json_params, const InputData& input_data)
112112
// /////////apply operation
113113
const std::string operation = params.operation;
114114
if (operation == "remeshing") {
115+
mesh.set_length_regions(json_params["length_region"]);
115116
mesh.mesh_improvement(max_its); // <-- tetwild
116117
} else if (operation == "fill_holes_topo") {
117118
const std::vector<std::string> fill_holes_tags_names = json_params["fill_holes_tags"];

0 commit comments

Comments
 (0)