libMesh
Public Member Functions | Private Member Functions | Static Private Member Functions | Private Attributes | List of all members
libMesh::VariationalSmootherConstraint Class Reference

Constraint class for the VariationalMeshSmoother. More...

#include <variational_smoother_constraint.h>

Inheritance diagram for libMesh::VariationalSmootherConstraint:
[legend]

Public Member Functions

 VariationalSmootherConstraint (System &sys, const bool &preserve_subdomain_boundaries)
 Constructor. More...
 
virtual ~VariationalSmootherConstraint () override
 
virtual void constrain () override
 Constraint function. More...
 

Private Member Functions

void fix_node (const Node &node)
 Constrain (i.e., fix) a node to not move during mesh smoothing. More...
 
void constrain_node_to_plane (const Node &node, const Point &ref_normal_vec)
 Constrain a node to remain in the given plane during mesh smoothing. More...
 
void constrain_node_to_line (const Node &node, const Point &line_vec)
 Constrain a node to remain on the given line during mesh smoothing. More...
 
void impose_constraint (const Node &node, const ConstraintVariant &constraint)
 Applies a given constraint to a node (e.g., fixing it, restricting it to a line or plane). More...
 

Static Private Member Functions

static void find_nodal_or_face_neighbors (const MeshBase &mesh, const Node &node, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map, std::vector< const Node *> &neighbors)
 Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to the given one. More...
 
static bool nodes_share_boundary_id (const Node &boundary_node, const Node &neighbor_node, const Elem &containing_elem, const BoundaryInfo &boundary_info)
 Determines whether two neighboring nodes share a common boundary id. More...
 
static std::set< std::set< const Node * > > get_neighbors_for_subdomain_constraint (const MeshBase &mesh, const Node &node, const subdomain_id_type sub_id, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map)
 Get the relevant nodal neighbors for a subdomain constraint. More...
 
static std::set< std::set< const Node * > > get_neighbors_for_boundary_constraint (const MeshBase &mesh, const Node &node, const std::unordered_set< dof_id_type > &boundary_node_ids, const BoundaryInfo &boundary_info, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map)
 Get the relevant nodal neighbors for an external boundary constraint. More...
 
static ConstraintVariant determine_constraint (const Node &node, const unsigned int dim, const std::set< std::set< const Node *>> &side_grouped_boundary_neighbors)
 Determines the appropriate constraint (PointConstraint, LineConstraint, or PlaneConstraint) for a node based on its neighbors. More...
 

Private Attributes

System_sys
 
const bool _preserve_subdomain_boundaries
 Whether nodes on subdomain boundaries are subject to change via smoothing. More...
 

Detailed Description

Constraint class for the VariationalMeshSmoother.

Currently, all mesh boundary nodes are constrained to not move during smoothing. If requested (preserve_subdomain_boundaries = true), nodes on subdomain boundaries are also constrained to not move.

Definition at line 389 of file variational_smoother_constraint.h.

Constructor & Destructor Documentation

◆ VariationalSmootherConstraint()

libMesh::VariationalSmootherConstraint::VariationalSmootherConstraint ( System sys,
const bool &  preserve_subdomain_boundaries 
)

Constructor.

Parameters
sysSystem to constrain.
preserve_subdomain_boundariesWhether to constrain nodes on subdomain boundaries to not move.

Definition at line 311 of file variational_smoother_constraint.C.

313  : Constraint(), _sys(sys), _preserve_subdomain_boundaries(preserve_subdomain_boundaries)
314 {
315 }
const bool _preserve_subdomain_boundaries
Whether nodes on subdomain boundaries are subject to change via smoothing.

◆ ~VariationalSmootherConstraint()

libMesh::VariationalSmootherConstraint::~VariationalSmootherConstraint ( )
overridevirtualdefault

Member Function Documentation

◆ constrain()

void libMesh::VariationalSmootherConstraint::constrain ( )
overridevirtual

Constraint function.

This function will be called to constrain the system prior to a solve and must be provided by the user in a derived class.

Implements libMesh::System::Constraint.

Definition at line 319 of file variational_smoother_constraint.C.

References _preserve_subdomain_boundaries, _sys, libMesh::MeshTools::build_nodes_to_elem_map(), determine_constraint(), dim, libMesh::MeshTools::find_boundary_nodes(), libMesh::System::get_mesh(), get_neighbors_for_boundary_constraint(), get_neighbors_for_subdomain_constraint(), impose_constraint(), libMesh::intersect_constraints(), and mesh.

320 {
321  const auto & mesh = _sys.get_mesh();
322  const auto dim = mesh.mesh_dimension();
323 
324  // Only compute the node to elem map once
325  std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
326  MeshTools::build_nodes_to_elem_map(mesh, nodes_to_elem_map);
327 
328  const auto & boundary_info = mesh.get_boundary_info();
329 
330  const auto boundary_node_ids = MeshTools::find_boundary_nodes(mesh);
331 
332  // Identify/constrain subdomain boundary nodes, if requested
333  std::unordered_map<dof_id_type, ConstraintVariant> subdomain_boundary_map;
335  {
336  for (const auto * elem : mesh.active_element_ptr_range())
337  {
338  const auto & sub_id1 = elem->subdomain_id();
339  for (const auto side : elem->side_index_range())
340  {
341  const auto * neighbor = elem->neighbor_ptr(side);
342  if (neighbor == nullptr)
343  continue;
344 
345  const auto & sub_id2 = neighbor->subdomain_id();
346  if (sub_id1 == sub_id2)
347  continue;
348 
349  // elem and neighbor are in different subdomains, and share nodes
350  // that need to be constrained
351  for (const auto local_node_id : elem->nodes_on_side(side))
352  {
353  const auto & node = mesh.node_ref(elem->node_id(local_node_id));
354  // Make sure we haven't already processed this node
355  if (subdomain_boundary_map.count(node.id()))
356  continue;
357 
358  // Get the relevant nodal neighbors for the subdomain constraint
359  const auto side_grouped_boundary_neighbors =
361  mesh, node, sub_id1, nodes_to_elem_map);
362 
363  // Determine which constraint should be imposed
364  const auto subdomain_constraint =
365  determine_constraint(node, dim, side_grouped_boundary_neighbors);
366 
367  // This subdomain boundary node does not lie on an external boundary,
368  // go ahead and impose constraint
369  if (boundary_node_ids.find(node.id()) == boundary_node_ids.end())
370  this->impose_constraint(node, subdomain_constraint);
371 
372  // This subdomain boundary node could lie on an external boundary, save it
373  // for later to combine with the external boundary constraint.
374  // We also save constraints for non-boundary nodes so we don't try to
375  // re-constrain the node when accessed from the neighboring elem.
376  // See subdomain_boundary_map.count call above.
377  subdomain_boundary_map[node.id()] = subdomain_constraint;
378 
379  } // for local_node_id
380 
381  } // for side
382  } // for elem
383  }
384 
385  // Loop through boundary nodes and impose constraints
386  for (const auto & bid : boundary_node_ids)
387  {
388  const auto & node = mesh.node_ref(bid);
389 
390  // Get the relevant nodal neighbors for the boundary constraint
391  const auto side_grouped_boundary_neighbors = get_neighbors_for_boundary_constraint(
392  mesh, node, boundary_node_ids, boundary_info, nodes_to_elem_map);
393 
394  // Determine which constraint should be imposed
395  const auto boundary_constraint =
396  determine_constraint(node, dim, side_grouped_boundary_neighbors);
397 
398  // Check for the case where this boundary node is also part of a subdomain id boundary
399  if (const auto it = subdomain_boundary_map.find(bid); it != subdomain_boundary_map.end())
400  {
401  const auto & subdomain_constraint = it->second;
402  // Combine current boundary constraint with previously determined
403  // subdomain_constraint
404  auto combined_constraint = intersect_constraints(subdomain_constraint, boundary_constraint);
405 
406  // This will catch cases where constraints have no intersection
407  // Fall back to fixed node constraint
408  if (std::holds_alternative<InvalidConstraint>(combined_constraint))
409  combined_constraint = PointConstraint(node);
410 
411  this->impose_constraint(node, combined_constraint);
412  }
413 
414  else
415  this->impose_constraint(node, boundary_constraint);
416 
417  } // end bid
418 }
unsigned int dim
MeshBase & mesh
void build_nodes_to_elem_map(const MeshBase &mesh, std::vector< std::vector< dof_id_type >> &nodes_to_elem_map)
After calling this function the input vector nodes_to_elem_map will contain the node to element conne...
Definition: mesh_tools.C:449
const MeshBase & get_mesh() const
Definition: system.h:2358
std::unordered_set< dof_id_type > find_boundary_nodes(const MeshBase &mesh)
Calling this function on a 2D mesh will convert all the elements to triangles.
Definition: mesh_tools.C:517
ConstraintVariant intersect_constraints(const ConstraintVariant &a, const ConstraintVariant &b)
Dispatch intersection between two constraint variants.
static std::set< std::set< const Node * > > get_neighbors_for_boundary_constraint(const MeshBase &mesh, const Node &node, const std::unordered_set< dof_id_type > &boundary_node_ids, const BoundaryInfo &boundary_info, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map)
Get the relevant nodal neighbors for an external boundary constraint.
static std::set< std::set< const Node * > > get_neighbors_for_subdomain_constraint(const MeshBase &mesh, const Node &node, const subdomain_id_type sub_id, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map)
Get the relevant nodal neighbors for a subdomain constraint.
const bool _preserve_subdomain_boundaries
Whether nodes on subdomain boundaries are subject to change via smoothing.
static ConstraintVariant determine_constraint(const Node &node, const unsigned int dim, const std::set< std::set< const Node *>> &side_grouped_boundary_neighbors)
Determines the appropriate constraint (PointConstraint, LineConstraint, or PlaneConstraint) for a nod...
void impose_constraint(const Node &node, const ConstraintVariant &constraint)
Applies a given constraint to a node (e.g., fixing it, restricting it to a line or plane)...

◆ constrain_node_to_line()

void libMesh::VariationalSmootherConstraint::constrain_node_to_line ( const Node node,
const Point line_vec 
)
private

Constrain a node to remain on the given line during mesh smoothing.

Parameters
nodeNode to constrain
line_vecvector parallel to the constraining line. This, along with the coordinates of node, are used to define the constraining line.

Definition at line 484 of file variational_smoother_constraint.C.

References _sys, libMesh::DofMap::add_constraint_row(), dim, distance(), libMesh::DofObject::dof_number(), libMesh::System::get_dof_map(), libMesh::System::get_mesh(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::MeshBase::mesh_dimension(), libMesh::System::number(), and libMesh::relative_fuzzy_equals().

Referenced by impose_constraint().

486 {
487  const auto dim = _sys.get_mesh().mesh_dimension();
488 
489  // We will free the dimension most parallel to line_vec to keep the
490  // constraint coefficients small
491  const std::vector<Real> line_vec_coefs{line_vec(0), line_vec(1), line_vec(2)};
492  auto it = std::max_element(line_vec_coefs.begin(), line_vec_coefs.end(), [](double a, double b) {
493  return std::abs(a) < std::abs(b);
494  });
495  const unsigned int free_dim = std::distance(line_vec_coefs.begin(), it);
496  const auto free_dof_index = node.dof_number(_sys.number(), free_dim, 0);
497 
498  // A line is parameterized as r(t) = node + t * line_vec, so
499  // x(t) = node(x) + t * line_vec(x)
500  // y(t) = node(y) + t * line_vec(y)
501  // z(t) = node(z) + t * line_vec(z)
502  // Let's say we leave x free. Then t = (x(t) - node(x)) / line_vec(x)
503  // Then y and z can be constrained as
504  // y = node(y) + line_vec_y * (x(t) - node(x)) / line_vec(x)
505  // = x(t) * line_vec(y) / line_vec(x) + (node(y) - node(x) * line_vec(y) / line_vec(x))
506  // z = x(t) * line_vec(z) / line_vec(x) + (node(z) - node(x) * line_vec(z) / line_vec(x))
507 
508  libmesh_assert(!relative_fuzzy_equals(line_vec(free_dim), 0.));
509  for (const auto constrained_dim : make_range(dim))
510  {
511  if (constrained_dim == free_dim)
512  continue;
513 
514  DofConstraintRow constraint_row;
515  constraint_row[free_dof_index] = line_vec(constrained_dim) / line_vec(free_dim);
516  const auto inhomogeneous_part =
517  node(constrained_dim) - node(free_dim) * line_vec(constrained_dim) / line_vec(free_dim);
518  const auto constrained_dof_index = node.dof_number(_sys.number(), constrained_dim, 0);
520  constrained_dof_index, constraint_row, inhomogeneous_part, true);
521  }
522 }
unsigned int dim
const MeshBase & get_mesh() const
Definition: system.h:2358
Real distance(const Point &p)
unsigned int number() const
Definition: system.h:2350
void add_constraint_row(const dof_id_type dof_number, const DofConstraintRow &constraint_row, const Number constraint_rhs, const bool forbid_constraint_overwrite)
Adds a copy of the user-defined row to the constraint matrix, using an inhomogeneous right-hand-side ...
libmesh_assert(ctx)
std::map< dof_id_type, Real, std::less< dof_id_type >, Threads::scalable_allocator< std::pair< const dof_id_type, Real > > > DofConstraintRow
A row of the Dof constraint matrix.
Definition: dof_map.h:100
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
bool relative_fuzzy_equals(const T &var1, const T2 &var2, const Real tol=TOLERANCE *TOLERANCE)
Function to check whether two variables are equal within a relative tolerance.
Definition: fuzzy_equals.h:78
const DofMap & get_dof_map() const
Definition: system.h:2374

◆ constrain_node_to_plane()

void libMesh::VariationalSmootherConstraint::constrain_node_to_plane ( const Node node,
const Point ref_normal_vec 
)
private

Constrain a node to remain in the given plane during mesh smoothing.

Parameters
nodeNode to constrain
ref_normal_vecReference normal vector to the constraining plane. This, along with the coordinates of node, are used to define the constraining plane.

Definition at line 434 of file variational_smoother_constraint.C.

References _sys, libMesh::DofMap::add_constraint_row(), dim, libMesh::DofObject::dof_number(), libMesh::System::get_dof_map(), libMesh::System::get_mesh(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::MeshBase::mesh_dimension(), libMesh::TypeVector< T >::norm(), libMesh::System::number(), libMesh::Real, and libMesh::TOLERANCE.

Referenced by impose_constraint().

436 {
437  const auto dim = _sys.get_mesh().mesh_dimension();
438  // determine equation of plane: c_x * x + c_y * y + c_z * z + c = 0
439  std::vector<Real> xyz_coefs; // vector to hold c_x, c_y, c_z
440  Real c = 0.;
441 
442  // We choose to constrain the dimension with the largest magnitude coefficient
443  // This approach ensures the coefficients added to the constraint_row
444  // (i.e., -c_xyz / c_max) have as small magnitude as possible
445 
446  // We initialize this to avoid maybe-uninitialized compiler error
447  unsigned int constrained_dim = 0;
448 
449  // Let's assert that we have a nonzero normal to ensure that constrained_dim
450  // is always set
451  libmesh_assert(ref_normal_vec.norm() > TOLERANCE);
452 
453  Real max_abs_coef = 0.;
454  for (const auto d : make_range(dim))
455  {
456  const auto coef = ref_normal_vec(d);
457  xyz_coefs.push_back(coef);
458  c -= coef * node(d);
459 
460  const auto coef_abs = std::abs(coef);
461  if (coef_abs > max_abs_coef)
462  {
463  max_abs_coef = coef_abs;
464  constrained_dim = d;
465  }
466  }
467 
468  DofConstraintRow constraint_row;
469  for (const auto free_dim : make_range(dim))
470  {
471  if (free_dim == constrained_dim)
472  continue;
473 
474  const auto free_dof_index = node.dof_number(_sys.number(), free_dim, 0);
475  constraint_row[free_dof_index] = -xyz_coefs[free_dim] / xyz_coefs[constrained_dim];
476  }
477 
478  const auto inhomogeneous_part = -c / xyz_coefs[constrained_dim];
479  const auto constrained_dof_index = node.dof_number(_sys.number(), constrained_dim, 0);
481  constrained_dof_index, constraint_row, inhomogeneous_part, true);
482 }
static constexpr Real TOLERANCE
unsigned int dim
const MeshBase & get_mesh() const
Definition: system.h:2358
unsigned int number() const
Definition: system.h:2350
void add_constraint_row(const dof_id_type dof_number, const DofConstraintRow &constraint_row, const Number constraint_rhs, const bool forbid_constraint_overwrite)
Adds a copy of the user-defined row to the constraint matrix, using an inhomogeneous right-hand-side ...
libmesh_assert(ctx)
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
std::map< dof_id_type, Real, std::less< dof_id_type >, Threads::scalable_allocator< std::pair< const dof_id_type, Real > > > DofConstraintRow
A row of the Dof constraint matrix.
Definition: dof_map.h:100
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
const DofMap & get_dof_map() const
Definition: system.h:2374

◆ determine_constraint()

ConstraintVariant libMesh::VariationalSmootherConstraint::determine_constraint ( const Node node,
const unsigned int  dim,
const std::set< std::set< const Node *>> &  side_grouped_boundary_neighbors 
)
staticprivate

Determines the appropriate constraint (PointConstraint, LineConstraint, or PlaneConstraint) for a node based on its neighbors.

Parameters
nodeThe node to constrain.
dimThe mesh dimension.
Returns
The best-fit constraint for the given geometry.

Definition at line 788 of file variational_smoother_constraint.C.

References libMesh::TypeVector< T >::cross(), dim, libMesh::index_range(), libMesh::intersect_constraints(), libMesh::libmesh_assert(), libMesh::make_range(), libMesh::TypeVector< T >::norm(), libMesh::TOLERANCE, and libMesh::TypeVector< T >::unit().

Referenced by constrain().

792 {
793  // Determines the appropriate geometric constraint for a node based on its
794  // neighbors.
795 
796  // Extract neighbors in flat vector
797  std::vector<const Node *> neighbors;
798  for (const auto & side : side_grouped_boundary_neighbors)
799  neighbors.insert(neighbors.end(), side.begin(), side.end());
800 
801  // Constrain the node to it's current location
802  if (dim == 1 || neighbors.size() == 1)
803  return PointConstraint{node};
804 
805  if (dim == 2 || neighbors.size() == 2)
806  {
807  // Determine whether node + all neighbors are colinear
808  bool all_colinear = true;
809  const Point ref_dir = (*neighbors[0] - node).unit();
810  for (const auto i : make_range(std::size_t(1), neighbors.size()))
811  {
812  const Point delta = *(neighbors[i]) - node;
813  libmesh_assert(delta.norm() >= TOLERANCE);
814  const Point dir = delta.unit();
815  if (!dir.relative_fuzzy_equals(ref_dir) && !dir.relative_fuzzy_equals(-ref_dir))
816  {
817  all_colinear = false;
818  break;
819  }
820  }
821 
822  if (all_colinear)
823  return LineConstraint{node, ref_dir};
824 
825  return PointConstraint{node};
826  }
827 
828  // dim == 3, neighbors.size() >= 3
829  std::set<PlaneConstraint> valid_planes;
830  for (const auto & side_nodes : side_grouped_boundary_neighbors)
831  {
832  std::vector<const Node *> side_nodes_vec(side_nodes.begin(), side_nodes.end());
833  for (const auto i : index_range(side_nodes_vec))
834  {
835  const Point vec_i = (*side_nodes_vec[i] - node);
836  for (const auto j : make_range(i))
837  {
838  const Point vec_j = (*side_nodes_vec[j] - node);
839  Point candidate_normal = vec_i.cross(vec_j);
840  if (candidate_normal.norm() <= TOLERANCE)
841  continue;
842 
843  const PlaneConstraint candidate_plane{node, candidate_normal};
844  valid_planes.emplace(candidate_plane);
845  }
846  }
847  }
848 
849  // Fall back to point constraint
850  if (valid_planes.empty())
851  return PointConstraint(node);
852 
853  // Combine all the planes together to get a common constraint
854  auto it = valid_planes.begin();
855  ConstraintVariant current = *it++;
856  for (; it != valid_planes.end(); ++it)
857  {
858  current = intersect_constraints(current, *it);
859 
860  // This will catch cases where constraints have no intersection
861  // (i.e., the element surface is non-planar)
862  // Fall back to fixed node constraint
863  if (std::holds_alternative<InvalidConstraint>(current))
864  {
865  current = PointConstraint(node);
866  break;
867  }
868  }
869 
870  return current;
871 }
static constexpr Real TOLERANCE
unsigned int dim
ConstraintVariant intersect_constraints(const ConstraintVariant &a, const ConstraintVariant &b)
Dispatch intersection between two constraint variants.
libmesh_assert(ctx)
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
std::variant< PointConstraint, LineConstraint, PlaneConstraint, InvalidConstraint > ConstraintVariant
Type used to store a constraint that may be a PlaneConstraint, LineConstraint, or PointConstraint...
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
Definition: int_range.h:117

◆ find_nodal_or_face_neighbors()

void libMesh::VariationalSmootherConstraint::find_nodal_or_face_neighbors ( const MeshBase mesh,
const Node node,
const std::unordered_map< dof_id_type, std::vector< const Elem *>> &  nodes_to_elem_map,
std::vector< const Node *> &  neighbors 
)
staticprivate

Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to the given one.

IF NO neighbors are found, all nodes on the containing side are treated as neighbors. This is useful when the node does not lie on an edge, such as the central face node in HEX27 elements.

Definition at line 524 of file variational_smoother_constraint.C.

References libMesh::MeshTools::find_nodal_neighbors(), libMesh::DofObject::id(), libMesh::libmesh_assert(), and mesh.

Referenced by get_neighbors_for_boundary_constraint(), and get_neighbors_for_subdomain_constraint().

529 {
530  // Find all the nodal neighbors... that is the nodes directly connected
531  // to this node through one edge.
532  MeshTools::find_nodal_neighbors(mesh, node, nodes_to_elem_map, neighbors);
533 
534  // If no neighbors are found, use all nodes on the containing side as
535  // neighbors.
536  if (!neighbors.size())
537  {
538  // Grab the element containing node
539  const auto * elem = libmesh_map_find(nodes_to_elem_map, node.id()).front();
540  // Find the element side containing node
541  for (const auto &side : elem->side_index_range())
542  {
543  const auto &nodes_on_side = elem->nodes_on_side(side);
544  const auto it =
545  std::find_if(nodes_on_side.begin(), nodes_on_side.end(), [&](auto local_node_id) {
546  return elem->node_id(local_node_id) == node.id();
547  });
548 
549  if (it != nodes_on_side.end())
550  {
551  for (const auto &local_node_id : nodes_on_side)
552  // No need to add node itself as a neighbor
553  if (const auto *node_ptr = elem->node_ptr(local_node_id);
554  *node_ptr != node)
555  neighbors.push_back(node_ptr);
556  break;
557  }
558  }
559  }
560  libmesh_assert(neighbors.size());
561 }
MeshBase & mesh
void find_nodal_neighbors(const MeshBase &mesh, const Node &n, const std::vector< std::vector< const Elem *>> &nodes_to_elem_map, std::vector< const Node *> &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...
Definition: mesh_tools.C:1036
libmesh_assert(ctx)

◆ fix_node()

void libMesh::VariationalSmootherConstraint::fix_node ( const Node node)
private

Constrain (i.e., fix) a node to not move during mesh smoothing.

Parameters
nodeNode to fix.

Definition at line 420 of file variational_smoother_constraint.C.

References _sys, libMesh::DofMap::add_constraint_row(), libMesh::DofObject::dof_number(), libMesh::System::get_dof_map(), libMesh::System::get_mesh(), libMesh::make_range(), libMesh::MeshBase::mesh_dimension(), and libMesh::System::number().

Referenced by impose_constraint().

421 {
422  for (const auto d : make_range(_sys.get_mesh().mesh_dimension()))
423  {
424  const auto constrained_dof_index = node.dof_number(_sys.number(), d, 0);
425  DofConstraintRow constraint_row;
426  // Leave the constraint row as all zeros so this dof is independent from other dofs
427  const auto constrained_value = node(d);
428  // Simply constrain this dof to retain it's current value
430  constrained_dof_index, constraint_row, constrained_value, true);
431  }
432 }
const MeshBase & get_mesh() const
Definition: system.h:2358
unsigned int number() const
Definition: system.h:2350
void add_constraint_row(const dof_id_type dof_number, const DofConstraintRow &constraint_row, const Number constraint_rhs, const bool forbid_constraint_overwrite)
Adds a copy of the user-defined row to the constraint matrix, using an inhomogeneous right-hand-side ...
std::map< dof_id_type, Real, std::less< dof_id_type >, Threads::scalable_allocator< std::pair< const dof_id_type, Real > > > DofConstraintRow
A row of the Dof constraint matrix.
Definition: dof_map.h:100
IntRange< T > make_range(T beg, T end)
The 2-parameter make_range() helper function returns an IntRange<T> when both input parameters are of...
Definition: int_range.h:140
unsigned int mesh_dimension() const
Definition: mesh_base.C:372
const DofMap & get_dof_map() const
Definition: system.h:2374

◆ get_neighbors_for_boundary_constraint()

std::set< std::set< const Node * > > libMesh::VariationalSmootherConstraint::get_neighbors_for_boundary_constraint ( const MeshBase mesh,
const Node node,
const std::unordered_set< dof_id_type > &  boundary_node_ids,
const BoundaryInfo boundary_info,
const std::unordered_map< dof_id_type, std::vector< const Elem *>> &  nodes_to_elem_map 
)
staticprivate

Get the relevant nodal neighbors for an external boundary constraint.

Parameters
meshThe mesh being smoothed.
nodeThe node (on the external boundary) being constrained.
boundary_node_idsThe set of mesh's external boundary node ids.
boundary_infoThe mesh's BoundaryInfo.
nodes_to_elem_mapA mapping from node id to containing element ids.
Returns
A set of node pointer sets containing nodal neighbors to 'node' on the external boundary. The subsets are grouped by element faces that form the external boundary. Note that 'node' itself does not appear in this set.

Definition at line 707 of file variational_smoother_constraint.C.

References find_nodal_or_face_neighbors(), libMesh::DofObject::id(), mesh, and nodes_share_boundary_id().

Referenced by constrain().

713 {
714 
715  // Find all the nodal neighbors... that is the nodes directly connected
716  // to this node through one edge, or if none exists, use other nodes on the
717  // containing face
718  std::vector<const Node *> neighbors;
720  mesh, node, nodes_to_elem_map, neighbors);
721 
722  // Each constituent set corresponds to neighbors sharing a face on the
723  // boundary
724  std::set<std::set<const Node *>> side_grouped_boundary_neighbors;
725 
726  for (const auto * neigh : neighbors)
727  {
728  const bool is_neighbor_boundary_node =
729  boundary_node_ids.find(neigh->id()) != boundary_node_ids.end();
730  if (!is_neighbor_boundary_node)
731  continue;
732 
733  // Determine whether nodes share a common boundary id
734  // First, find the common element that both node and neigh belong to
735  const auto & elems_containing_node = libmesh_map_find(nodes_to_elem_map, node.id());
736  const auto & elems_containing_neigh = libmesh_map_find(nodes_to_elem_map, neigh->id());
737  const Elem * common_elem = nullptr;
738  for (const auto * neigh_elem : elems_containing_neigh)
739  {
740  const bool is_neigh_common =
741  std::find(elems_containing_node.begin(), elems_containing_node.end(), neigh_elem) !=
742  elems_containing_node.end();
743  if (!is_neigh_common)
744  continue;
745  common_elem = neigh_elem;
746  // Keep this in the neigh_elem loop because there can be multiple common
747  // elements Now, determine whether node and neigh share a common boundary
748  // id
749  const bool nodes_have_common_bid = VariationalSmootherConstraint::nodes_share_boundary_id(
750  node, *neigh, *common_elem, boundary_info);
751  if (nodes_have_common_bid)
752  {
753  // Find the side coinciding with the shared boundary
754  for (const auto side : common_elem->side_index_range())
755  {
756  // We only care about external boundaries here, make sure side doesn't
757  // have a neighbor
758  if (common_elem->neighbor_ptr(side))
759  continue;
760 
761  bool node_found_on_side = false;
762  bool neigh_found_on_side = false;
763  const auto nodes_on_side = common_elem->nodes_on_side(side);
764  for (const auto local_node_id : nodes_on_side)
765  {
766  if (common_elem->node_id(local_node_id) == node.id())
767  node_found_on_side = true;
768  else if (common_elem->node_id(local_node_id) == neigh->id())
769  neigh_found_on_side = true;
770  }
771  if (!(node_found_on_side && neigh_found_on_side))
772  continue;
773 
774  std::set<const Node *> node_ptrs_on_side;
775  for (const auto local_node_id : nodes_on_side)
776  node_ptrs_on_side.insert(common_elem->node_ptr(local_node_id));
777  node_ptrs_on_side.erase(node_ptrs_on_side.find(&node));
778  side_grouped_boundary_neighbors.insert(node_ptrs_on_side);
779  }
780  continue;
781  }
782  }
783  }
784 
785  return side_grouped_boundary_neighbors;
786 }
MeshBase & mesh
static bool nodes_share_boundary_id(const Node &boundary_node, const Node &neighbor_node, const Elem &containing_elem, const BoundaryInfo &boundary_info)
Determines whether two neighboring nodes share a common boundary id.
static void find_nodal_or_face_neighbors(const MeshBase &mesh, const Node &node, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map, std::vector< const Node *> &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...

◆ get_neighbors_for_subdomain_constraint()

std::set< std::set< const Node * > > libMesh::VariationalSmootherConstraint::get_neighbors_for_subdomain_constraint ( const MeshBase mesh,
const Node node,
const subdomain_id_type  sub_id,
const std::unordered_map< dof_id_type, std::vector< const Elem *>> &  nodes_to_elem_map 
)
staticprivate

Get the relevant nodal neighbors for a subdomain constraint.

Parameters
meshThe mesh being smoothed.
nodeThe node (on the subdomain boundary) being constrained.
sub_idThe subdomain id of the block on one side of the subdomain boundary.
nodes_to_elem_mapA mapping from node id to containing element ids.
Returns
A set of node pointer sets containing nodal neighbors to 'node' on the sub_id1-sub_id2 boundary. The subsets are grouped by element faces that form the subdomain boundary. Note that 'node' itself does not appear in this set.

Definition at line 622 of file variational_smoother_constraint.C.

References find_nodal_or_face_neighbors(), libMesh::DofObject::id(), mesh, libMesh::Elem::neighbor_ptr(), and libMesh::Elem::subdomain_id().

Referenced by constrain().

627 {
628 
629  // Find all the nodal neighbors... that is the nodes directly connected
630  // to this node through one edge, or if none exists, use other nodes on the
631  // containing face
632  std::vector<const Node *> neighbors;
634  mesh, node, nodes_to_elem_map, neighbors);
635 
636  // Each constituent set corresponds to neighbors sharing a face on the
637  // subdomain boundary
638  std::set<std::set<const Node *>> side_grouped_boundary_neighbors;
639 
640  for (const auto * neigh : neighbors)
641  {
642  // Determine whether the neighbor is on the subdomain boundary
643  // First, find the common elements that both node and neigh belong to
644  const auto & elems_containing_node = libmesh_map_find(nodes_to_elem_map, node.id());
645  const auto & elems_containing_neigh = libmesh_map_find(nodes_to_elem_map, neigh->id());
646  const Elem * common_elem = nullptr;
647  for (const auto * neigh_elem : elems_containing_neigh)
648  {
649  if ((std::find(elems_containing_node.begin(), elems_containing_node.end(), neigh_elem) !=
650  elems_containing_node.end())
651  // We should be able to find a common element on the sub_id boundary
652  && (neigh_elem->subdomain_id() == sub_id))
653  common_elem = neigh_elem;
654  else
655  continue;
656 
657  // Now, determine whether node and neigh are on a side coincident
658  // with the subdomain boundary
659  for (const auto common_side : common_elem->side_index_range())
660  {
661  bool node_found_on_side = false;
662  bool neigh_found_on_side = false;
663  for (const auto local_node_id : common_elem->nodes_on_side(common_side))
664  {
665  if (common_elem->node_id(local_node_id) == node.id())
666  node_found_on_side = true;
667  else if (common_elem->node_id(local_node_id) == neigh->id())
668  neigh_found_on_side = true;
669  }
670 
671  if (!(node_found_on_side && neigh_found_on_side &&
672  common_elem->neighbor_ptr(common_side)))
673  continue;
674 
675  const auto matched_side = common_side;
676  // There could be multiple matched sides, so keep this next part
677  // inside the common_side loop
678  //
679  // Does matched_side, containing both node and neigh, lie on the
680  // sub_id subdomain boundary?
681  const auto matched_neighbor_sub_id =
682  common_elem->neighbor_ptr(matched_side)->subdomain_id();
683  const bool is_matched_side_on_subdomain_boundary = matched_neighbor_sub_id != sub_id;
684 
685  if (is_matched_side_on_subdomain_boundary)
686  {
687  // Store all nodes that live on this side
688  const auto nodes_on_side = common_elem->nodes_on_side(common_side);
689  std::set<const Node *> node_ptrs_on_side;
690  for (const auto local_node_id : nodes_on_side)
691  node_ptrs_on_side.insert(common_elem->node_ptr(local_node_id));
692  node_ptrs_on_side.erase(node_ptrs_on_side.find(&node));
693  side_grouped_boundary_neighbors.insert(node_ptrs_on_side);
694 
695  continue;
696  }
697 
698  } // for common_side
699 
700  } // for neigh_elem
701  }
702 
703  return side_grouped_boundary_neighbors;
704 }
MeshBase & mesh
static void find_nodal_or_face_neighbors(const MeshBase &mesh, const Node &node, const std::unordered_map< dof_id_type, std::vector< const Elem *>> &nodes_to_elem_map, std::vector< const Node *> &neighbors)
Given a mesh and a node in the mesh, the vector will be filled with every node directly attached to t...

◆ impose_constraint()

void libMesh::VariationalSmootherConstraint::impose_constraint ( const Node node,
const ConstraintVariant constraint 
)
private

Applies a given constraint to a node (e.g., fixing it, restricting it to a line or plane).

Parameters
nodeThe node to constrain.
constraintThe geometric constraint variant to apply.
Exceptions
libMesh::logicErrorIf the constraint cannot be imposed.

Definition at line 875 of file variational_smoother_constraint.C.

References constrain_node_to_line(), constrain_node_to_plane(), and fix_node().

Referenced by constrain().

877 {
878 
879  libmesh_assert_msg(!std::holds_alternative<InvalidConstraint>(constraint),
880  "Cannot impose constraint using InvalidConstraint.");
881 
882  if (std::holds_alternative<PointConstraint>(constraint))
883  fix_node(node);
884  else if (std::holds_alternative<LineConstraint>(constraint))
885  constrain_node_to_line(node, std::get<LineConstraint>(constraint).direction());
886  else if (std::holds_alternative<PlaneConstraint>(constraint))
887  constrain_node_to_plane(node, std::get<PlaneConstraint>(constraint).normal());
888 
889  else
890  libmesh_assert_msg(false, "Unknown constraint type.");
891 }
void constrain_node_to_plane(const Node &node, const Point &ref_normal_vec)
Constrain a node to remain in the given plane during mesh smoothing.
void constrain_node_to_line(const Node &node, const Point &line_vec)
Constrain a node to remain on the given line during mesh smoothing.
void fix_node(const Node &node)
Constrain (i.e., fix) a node to not move during mesh smoothing.

◆ nodes_share_boundary_id()

bool libMesh::VariationalSmootherConstraint::nodes_share_boundary_id ( const Node boundary_node,
const Node neighbor_node,
const Elem containing_elem,
const BoundaryInfo boundary_info 
)
staticprivate

Determines whether two neighboring nodes share a common boundary id.

Parameters
boundary_nodeThe first of the two prospective nodes.
neighbor_nodeThe second of the two prospective nodes.
containing_elemThe element containing node1 and node2.
boundary_infoThe mesh's BoundaryInfo.
Returns
nodes_share_bid Whether node1 and node2 share a common boundary id.

Definition at line 590 of file variational_smoother_constraint.C.

References libMesh::BoundaryInfo::boundary_ids(), libMesh::Elem::get_node_index(), libMesh::Elem::is_node_on_side(), side_id, and libMesh::Elem::side_index_range().

Referenced by get_neighbors_for_boundary_constraint().

594 {
595  bool nodes_share_bid = false;
596 
597  // Node ids local to containing_elem
598  const auto node_id = containing_elem.get_node_index(&boundary_node);
599  const auto neighbor_id = containing_elem.get_node_index(&neighbor_node);
600 
601  for (const auto side_id : containing_elem.side_index_range())
602  {
603  // We don't care about this side if it doesn't contain our boundary and neighbor nodes
604  if (!(containing_elem.is_node_on_side(node_id, side_id) &&
605  containing_elem.is_node_on_side(neighbor_id, side_id)))
606  continue;
607 
608  // If the current side, containing boundary_node and neighbor_node, lies on a boundary,
609  // we can say that boundary_node and neighbor_node have a common boundary id.
610  std::vector<boundary_id_type> boundary_ids;
611  boundary_info.boundary_ids(&containing_elem, side_id, boundary_ids);
612  if (boundary_ids.size())
613  {
614  nodes_share_bid = true;
615  break;
616  }
617  }
618  return nodes_share_bid;
619 }
const boundary_id_type side_id

Member Data Documentation

◆ _preserve_subdomain_boundaries

const bool libMesh::VariationalSmootherConstraint::_preserve_subdomain_boundaries
private

Whether nodes on subdomain boundaries are subject to change via smoothing.

Definition at line 398 of file variational_smoother_constraint.h.

Referenced by constrain().

◆ _sys

System& libMesh::VariationalSmootherConstraint::_sys
private

The documentation for this class was generated from the following files: