Skip to content

Commit bdf5bc0

Browse files
committed
WIP (#3385)
1 parent fd2b4f7 commit bdf5bc0

File tree

3 files changed

+136
-25
lines changed

3 files changed

+136
-25
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_subdomain_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: 110 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 subdomain_boundary_map = MeshTools::build_subdomain_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,119 @@ 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, &subdomain_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 = [&subdomain_boundary_map](dof_id_type node_id, const std::pair<subdomain_id_type, subdomain_id_type> & block_boundary)
108+
{
109+
const auto it = subdomain_boundary_map.find(node_id);
110+
if (it == subdomain_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+
{
131+
base = vec;
132+
return true;
133+
}
134+
135+
// 2D - collinear - check cross product of first edge with all other edges
136+
if (_mesh.mesh_dimension() == 2)
137+
return (base.cross(vec).norm_sq() < libMesh::TOLERANCE);
138+
139+
// 3D
140+
if (n_edges == 2)
141+
{
142+
const auto cross = base.cross(vec);
143+
if (cross.norm_sq() < libMesh::TOLERANCE)
144+
n_edges--;
145+
else
146+
base = cross;
147+
return true;
148+
}
149+
150+
return (base * vec < libMesh::TOLERANCE);
151+
};
152+
153+
if (on_boundary.count(node->id()))
154+
{
155+
// project to boundary
156+
for (const auto & connected_id : _graph[node->id()])
157+
if (on_boundary.count(connected_id) && !has_zero_curvature(connected_id))
158+
return;
159+
// we have not failed the curvature check, but do we have enough edges?
160+
if (n_edges < _mesh.mesh_dimension())
161+
return;
162+
163+
// now project
164+
// base /= base.norm();
165+
// auto delta = new_positions[node->id()] - *node;
166+
// if (_mesh.mesh_dimension() == 2)
167+
// delta = base * (delta * base);
168+
// else
169+
// delta -= base * (delta * base);
170+
// *node += delta;
171+
return;
172+
}
173+
174+
const auto it = subdomain_boundary_map.find(node->id());
175+
if (it != subdomain_boundary_map.end())
176+
{
177+
const auto num_boundaries = it->second.size();
178+
179+
// do not touch nodes at the intersection of two or more boundaries
180+
if (num_boundaries > 1)
181+
return;
182+
183+
if (num_boundaries == 1)
184+
{
185+
// project to block boundary
186+
const auto & boundary = *(it->second.begin());
187+
// iterate over neighboring nodes that share the same boundary and check if all edges are coplanar/collinear
188+
for (const auto & connected_id : _graph[node->id()])
189+
if (is_node_on_block_boundary(connected_id, boundary) && !has_zero_curvature(connected_id))
190+
return;
191+
// we have not failed the curvature check, but do we have enough edges?
192+
if (n_edges < _mesh.mesh_dimension())
193+
return;
194+
195+
// now project
196+
// if (_mesh.mesh_dimension() == 2)
197+
198+
return;
199+
}
200+
}
201+
202+
// otherwise just move the node freely
203+
*node = new_positions[node->id()];
204+
};
103205

104206
// now update the node positions (local and unpartitioned nodes only)
105207
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()];
208+
update_node_position(node);
108209

109210
for (auto & node : as_range(_mesh.pid_nodes_begin(DofObject::invalid_processor_id),
110211
_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()];
212+
update_node_position(node);
113213

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

src/mesh/mesh_tools.C

Lines changed: 19 additions & 15 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,31 @@ 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>>
553-
MeshTools::build_block_boundary_node_map(const MeshBase & mesh)
552+
std::map<dof_id_type, std::set<std::pair<subdomain_id_type, subdomain_id_type>>>
553+
MeshTools::build_subdomain_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())
563-
{
564-
const auto id2 = elem->neighbor_ptr(s)->subdomain_id();
565-
if (elem->neighbor_ptr(s) && id2 > id1)
566-
{
567-
auto nodes_on_side = elem->nodes_on_side(s);
563+
{
564+
if (elem->neighbor_ptr(s))
565+
{
566+
const auto id2 = elem->neighbor_ptr(s)->subdomain_id();
567+
if (id2 != id1)
568+
{
569+
auto nodes_on_side = elem->nodes_on_side(s);
568570

569-
for (auto & local_id : nodes_on_side)
570-
block_boundary_node_map.emplace(elem->node_ptr(local_id)->id(), id1, id2);
571-
}
572-
}
571+
for (auto & local_id : nodes_on_side)
572+
block_boundary_node_map[elem->node_ptr(local_id)->id()].insert(std::make_pair(std::min(id1, id2), std::max(id1, id2)));
573+
}
574+
}
575+
}
576+
}
573577

574578
return block_boundary_node_map;
575579
}

0 commit comments

Comments
 (0)