@@ -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
0 commit comments