Skip to content

Commit 1e1c25e

Browse files
committed
1 parent fd2b4f7 commit 1e1c25e

File tree

3 files changed

+123
-17
lines changed

3 files changed

+123
-17
lines changed

include/mesh/mesh_tools.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,13 @@ std::unordered_set<dof_id_type> find_boundary_nodes(const MeshBase & mesh);
129129
*/
130130
std::unordered_set<dof_id_type> find_block_boundary_nodes(const MeshBase & mesh);
131131

132+
/**
133+
* Returns a std::multimap for all block boundary nodes, listing all subdomain id pairs
134+
* for each block boundary the node is on
135+
*/
136+
std::map<dof_id_type, std::set<std::pair<subdomain_id_type, subdomain_id_type>>>
137+
build_block_boundary_node_map(const MeshBase & mesh);
138+
132139
/**
133140
* \returns A BoundingBox that bounds the mesh.
134141
*/

src/mesh/mesh_smoother_laplace.C

Lines changed: 108 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -54,10 +54,7 @@ void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
5454
auto on_boundary = MeshTools::find_boundary_nodes(_mesh);
5555

5656
// Also: don't smooth block boundary nodes
57-
auto on_block_boundary = MeshTools::find_block_boundary_nodes(_mesh);
58-
59-
// Merge them
60-
on_boundary.insert(on_block_boundary.begin(), on_block_boundary.end());
57+
auto block_boundary_map = MeshTools::build_block_boundary_node_map(_mesh);
6158

6259
// We can only update the nodes after all new positions were
6360
// determined. We store the new positions here
@@ -67,11 +64,11 @@ void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
6764
{
6865
new_positions.resize(_mesh.max_node_id());
6966

70-
auto calculate_new_position = [this, &on_boundary, &new_positions](const Node * node) {
67+
auto calculate_new_position = [this, &new_positions](const Node * node) {
7168
// leave the boundary intact
7269
// Only relocate the nodes which are vertices of an element
7370
// All other entries of _graph (the secondary nodes) are empty
74-
if (!on_boundary.count(node->id()) && (_graph[node->id()].size() > 0))
71+
if (_graph[node->id()].size() > 0)
7572
{
7673
Point avg_position(0.,0.,0.);
7774

@@ -100,16 +97,117 @@ void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
10097
_mesh.pid_nodes_end(DofObject::invalid_processor_id)))
10198
calculate_new_position(node);
10299

100+
// update node position
101+
auto update_node_position = [this, &new_positions, &block_boundary_map, &on_boundary](Node * node)
102+
{
103+
if (_graph[node->id()].size() == 0)
104+
return;
105+
106+
// check if a node is on a given block boundary
107+
auto is_node_on_block_boundary = [&block_boundary_map](dof_id_type node_id, const std::pair<subdomain_id_type, subdomain_id_type> & block_boundary)
108+
{
109+
const auto it = block_boundary_map.find(node_id);
110+
if (it == block_boundary_map.end())
111+
return false;
112+
return it->second.count(block_boundary) > 0;
113+
};
114+
115+
// check if a series of vectors is collinear/coplanar
116+
RealVectorValue base;
117+
std::size_t n_edges = 0;
118+
auto has_zero_curvature = [this, &node, &base, &n_edges](dof_id_type connected_id) {
119+
const auto connected_node = _mesh.point(connected_id);
120+
const RealVectorValue vec = connected_node - *node;
121+
// if any connected edge has length zero we give up and don't move the node
122+
if (vec.norm_sq() < libMesh::TOLERANCE)
123+
return false;
124+
125+
// count edges
126+
n_edges++;
127+
128+
// store first two edges for later projection
129+
if (n_edges == 1) {
130+
base = vec;
131+
return true;
132+
}
133+
134+
// 2D - collinear - check cross product of first edge with all other edges
135+
if (_mesh.mesh_dimension() == 2)
136+
return (base.cross(vec).norm_sq() < libMesh::TOLERANCE);
137+
138+
// 3D
139+
if (n_edges == 2) {
140+
const auto cross = base.cross(vec);
141+
if (cross.norm_sq() < libMesh::TOLERANCE)
142+
n_edges--;
143+
else
144+
base = cross;
145+
return true;
146+
}
147+
148+
return (base * vec < libMesh::TOLERANCE);
149+
};
150+
151+
if (on_boundary.count(node->id()))
152+
{
153+
// project to boundary
154+
for (const auto & connected_id : _graph[node->id()])
155+
if (on_boundary.count(connected_id) && !has_zero_curvature(connected_id))
156+
return;
157+
// we have not failed the curvature check, but do we have enough edges?
158+
if (n_edges < _mesh.mesh_dimension())
159+
return;
160+
161+
// now project
162+
// base /= base.norm();
163+
// auto delta = new_positions[node->id()] - *node;
164+
// if (_mesh.mesh_dimension() == 2)
165+
// delta = base * (delta * base);
166+
// else
167+
// delta -= base * (delta * base);
168+
// *node += delta;
169+
return;
170+
}
171+
172+
const auto it = block_boundary_map.find(node->id());
173+
if (it != block_boundary_map.end())
174+
{
175+
const auto num_boundaries = it->second.size();
176+
177+
// do not touch nodes at the intersection of two or more boundaries
178+
if (num_boundaries > 1)
179+
return;
180+
181+
if (num_boundaries == 1)
182+
{
183+
// project to block boundary
184+
const auto & boundary = *(it->second.begin());
185+
// iterate over neighboring nodes that share the same boundary and check if all edges are coplanar/collinear
186+
for (const auto & connected_id : _graph[node->id()])
187+
if (is_node_on_block_boundary(connected_id, boundary) && !has_zero_curvature(connected_id))
188+
return;
189+
// we have not failed the curvature check, but do we have enough edges?
190+
if (n_edges < _mesh.mesh_dimension())
191+
return;
192+
193+
// now project
194+
// if (_mesh.mesh_dimension() == 2)
195+
196+
return;
197+
}
198+
}
199+
200+
// otherwise just move the node freely
201+
*node = new_positions[node->id()];
202+
};
103203

104204
// now update the node positions (local and unpartitioned nodes only)
105205
for (auto & node : _mesh.local_node_ptr_range())
106-
if (!on_boundary.count(node->id()) && (_graph[node->id()].size() > 0))
107-
*node = new_positions[node->id()];
206+
update_node_position(node);
108207

109208
for (auto & node : as_range(_mesh.pid_nodes_begin(DofObject::invalid_processor_id),
110209
_mesh.pid_nodes_end(DofObject::invalid_processor_id)))
111-
if (!on_boundary.count(node->id()) && (_graph[node->id()].size() > 0))
112-
*node = new_positions[node->id()];
210+
update_node_position(node);
113211

114212
// Now the nodes which are ghosts on this processor may have been moved on
115213
// the processors which own them. So we need to synchronize with our neighbors

src/mesh/mesh_tools.C

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -537,7 +537,7 @@ MeshTools::find_block_boundary_nodes(const MeshBase & mesh)
537537
// mark them as true in on_boundary.
538538
for (const auto & elem : mesh.active_element_ptr_range())
539539
for (auto s : elem->side_index_range())
540-
if (elem->neighbor_ptr(s) && elem->neighbor_ptr(s)->subdomain_id() > elem->subdomain_id())
540+
if (elem->neighbor_ptr(s) && elem->neighbor_ptr(s)->subdomain_id() != elem->subdomain_id())
541541
{
542542
auto nodes_on_side = elem->nodes_on_side(s);
543543

@@ -549,27 +549,28 @@ MeshTools::find_block_boundary_nodes(const MeshBase & mesh)
549549
}
550550

551551

552-
std::multimap<dof_id_type, std::pair<subdomain_id_type, subdomain_id_type>>
552+
std::map<dof_id_type, std::set<std::pair<subdomain_id_type, subdomain_id_type>>>
553553
MeshTools::build_block_boundary_node_map(const MeshBase & mesh)
554554
{
555-
std::multimap<dof_id_type, std::pair<subdomain_id_type, subdomain_id_type>> block_boundary_node_map;
555+
std::map<dof_id_type, std::set<std::pair<subdomain_id_type, subdomain_id_type>>> block_boundary_node_map;
556556

557-
// Loop over elements, find those on boundary, and
558-
// mark them as true in on_boundary.
557+
// Loop over elements, find those on a block boundary, and
558+
// add each boundary the node is on as a subdomain id pair
559559
for (const auto & elem : mesh.active_element_ptr_range())
560560
{
561561
const auto id1 = elem->subdomain_id();
562562
for (auto s : elem->side_index_range())
563563
{
564564
const auto id2 = elem->neighbor_ptr(s)->subdomain_id();
565-
if (elem->neighbor_ptr(s) && id2 > id1)
565+
if (elem->neighbor_ptr(s) && id2 != id1)
566566
{
567567
auto nodes_on_side = elem->nodes_on_side(s);
568568

569569
for (auto & local_id : nodes_on_side)
570-
block_boundary_node_map.emplace(elem->node_ptr(local_id)->id(), id1, id2);
570+
block_boundary_node_map[elem->node_ptr(local_id)->id()].insert(std::make_pair(std::min(id1, id2), std::max(id1, id2)));
571571
}
572572
}
573+
}
573574

574575
return block_boundary_node_map;
575576
}

0 commit comments

Comments
 (0)