@@ -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 subdomain_boundary_map = MeshTools ::build_subdomain_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,119 @@ 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 , & 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
+ };
103
205
104
206
// now update the node positions (local and unpartitioned nodes only)
105
207
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 );
108
209
109
210
for (auto & node : as_range (_mesh .pid_nodes_begin (DofObject ::invalid_processor_id ),
110
211
_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 );
113
213
114
214
// Now the nodes which are ghosts on this processor may have been moved on
115
215
// the processors which own them. So we need to synchronize with our neighbors
0 commit comments