@@ -54,10 +54,7 @@ void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
54
54
auto on_boundary = MeshTools ::find_boundary_nodes (_mesh );
55
55
56
56
// 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 );
61
58
62
59
// We can only update the nodes after all new positions were
63
60
// determined. We store the new positions here
@@ -67,11 +64,11 @@ void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
67
64
{
68
65
new_positions .resize (_mesh .max_node_id ());
69
66
70
- auto calculate_new_position = [this , & on_boundary , & new_positions ](const Node * node ) {
67
+ auto calculate_new_position = [this , & new_positions ](const Node * node ) {
71
68
// leave the boundary intact
72
69
// Only relocate the nodes which are vertices of an element
73
70
// 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 )
75
72
{
76
73
Point avg_position (0. ,0. ,0. );
77
74
@@ -100,16 +97,117 @@ void LaplaceMeshSmoother::smooth(unsigned int n_iterations)
100
97
_mesh .pid_nodes_end (DofObject ::invalid_processor_id )))
101
98
calculate_new_position (node );
102
99
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
+ };
103
203
104
204
// now update the node positions (local and unpartitioned nodes only)
105
205
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 );
108
207
109
208
for (auto & node : as_range (_mesh .pid_nodes_begin (DofObject ::invalid_processor_id ),
110
209
_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 );
113
211
114
212
// Now the nodes which are ghosts on this processor may have been moved on
115
213
// the processors which own them. So we need to synchronize with our neighbors
0 commit comments