19 #include "libmesh/variational_smoother_constraint.h" 20 #include "libmesh/mesh_tools.h" 21 #include "libmesh/boundary_info.h" 28 libmesh_error_msg_if(vec.norm() <
TOLERANCE,
29 "Can't define a positively-oriented vector with a zero vector.");
33 Point canonical{0, 0, 0};
38 canonical(dim_id) = 1.;
42 const auto dot_prod = vec * canonical;
45 return (dot_prod > 0) ? vec.unit() : -vec.unit();
70 if (!o.contains_point(*
this))
83 "Can't define a line with zero magnitude direction vector.");
128 using T = std::decay_t<decltype(o)>;
129 if constexpr (std::is_same_v<T, LineConstraint>)
147 const Real cross_dot = (delta.
cross(o.direction())) * cross_d1_d2;
150 const Real t = cross_dot / denom;
154 if (o.direction().cross(intersection - o.point()).
norm() >
_tol)
161 else if constexpr (std::is_same_v<T, PlaneConstraint>)
162 return o.intersect(*
this);
164 else if constexpr (std::is_same_v<T, PointConstraint>)
174 libmesh_error_msg(
"Unsupported constraint type in Line::intersect.");
183 "Can't define a plane with zero magnitude direction vector.");
214 return std::abs(dist) <
_tol;
221 return base_on_plane && dir_orthogonal;
231 using T = std::decay_t<decltype(o)>;
232 if constexpr (std::is_same_v<T, PlaneConstraint>)
261 const Real n2_dot_n2 = o.normal() * o.normal();
263 const Real n2_dot_w = o.normal() * w;
265 const Real denom = -(n1_dot_n1 * n2_dot_n2 - n1_dot_n2 * n1_dot_n2);
268 const Real s = -(n1_dot_n2 * n2_dot_w - n2_dot_n2 * n1_dot_w) / denom;
274 else if constexpr (std::is_same_v<T, LineConstraint>)
296 else if constexpr (std::is_same_v<T, PointConstraint>)
306 libmesh_error_msg(
"Unsupported constraint type in Plane::intersect.");
312 System & sys,
const bool & preserve_subdomain_boundaries)
313 : Constraint(), _sys(sys), _preserve_subdomain_boundaries(preserve_subdomain_boundaries)
322 const auto dim =
mesh.mesh_dimension();
325 std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
328 const auto & boundary_info =
mesh.get_boundary_info();
333 std::unordered_map<dof_id_type, ConstraintVariant> subdomain_boundary_map;
336 for (
const auto * elem :
mesh.active_element_ptr_range())
338 const auto & sub_id1 = elem->subdomain_id();
339 for (
const auto side : elem->side_index_range())
341 const auto * neighbor = elem->neighbor_ptr(side);
342 if (neighbor ==
nullptr)
345 const auto & sub_id2 = neighbor->subdomain_id();
346 if (sub_id1 == sub_id2)
351 for (
const auto local_node_id : elem->nodes_on_side(side))
353 const auto & node =
mesh.node_ref(elem->node_id(local_node_id));
355 if (subdomain_boundary_map.count(node.id()))
359 const auto side_grouped_boundary_neighbors =
361 mesh, node, sub_id1, nodes_to_elem_map);
364 const auto subdomain_constraint =
369 if (boundary_node_ids.find(node.id()) == boundary_node_ids.end())
377 subdomain_boundary_map[node.id()] = subdomain_constraint;
386 for (
const auto & bid : boundary_node_ids)
388 const auto & node =
mesh.node_ref(bid);
392 mesh, node, boundary_node_ids, boundary_info, nodes_to_elem_map);
395 const auto boundary_constraint =
399 if (
const auto it = subdomain_boundary_map.find(bid); it != subdomain_boundary_map.end())
401 const auto & subdomain_constraint = it->second;
408 if (std::holds_alternative<InvalidConstraint>(combined_constraint))
427 const auto constrained_value = node(d);
430 constrained_dof_index, constraint_row, constrained_value,
true);
435 const Point & ref_normal_vec)
439 std::vector<Real> xyz_coefs;
447 unsigned int constrained_dim = 0;
453 Real max_abs_coef = 0.;
456 const auto coef = ref_normal_vec(d);
457 xyz_coefs.push_back(coef);
460 const auto coef_abs = std::abs(coef);
461 if (coef_abs > max_abs_coef)
463 max_abs_coef = coef_abs;
471 if (free_dim == constrained_dim)
475 constraint_row[free_dof_index] = -xyz_coefs[free_dim] / xyz_coefs[constrained_dim];
478 const auto inhomogeneous_part = -c / xyz_coefs[constrained_dim];
481 constrained_dof_index, constraint_row, inhomogeneous_part,
true);
485 const Point & line_vec)
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);
495 const unsigned int free_dim =
std::distance(line_vec_coefs.begin(), it);
511 if (constrained_dim == free_dim)
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);
520 constrained_dof_index, constraint_row, inhomogeneous_part,
true);
527 const std::unordered_map<
dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map,
528 std::vector<const Node *> & neighbors)
536 if (!neighbors.size())
539 const auto * elem = libmesh_map_find(nodes_to_elem_map, node.
id()).front();
541 for (
const auto &side : elem->side_index_range())
543 const auto &nodes_on_side = elem->nodes_on_side(side);
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();
549 if (it != nodes_on_side.end())
551 for (
const auto &local_node_id : nodes_on_side)
553 if (
const auto *node_ptr = elem->node_ptr(local_node_id);
555 neighbors.push_back(node_ptr);
591 const Node & neighbor_node,
592 const Elem & containing_elem,
595 bool nodes_share_bid =
false;
598 const auto node_id = containing_elem.
get_node_index(&boundary_node);
599 const auto neighbor_id = containing_elem.
get_node_index(&neighbor_node);
610 std::vector<boundary_id_type> boundary_ids;
612 if (boundary_ids.size())
614 nodes_share_bid =
true;
618 return nodes_share_bid;
621 std::set<std::set<const Node *>>
626 const std::unordered_map<
dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
632 std::vector<const Node *> neighbors;
634 mesh, node, nodes_to_elem_map, neighbors);
638 std::set<std::set<const Node *>> side_grouped_boundary_neighbors;
640 for (
const auto * neigh : neighbors)
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)
649 if ((std::find(elems_containing_node.begin(), elems_containing_node.end(), neigh_elem) !=
650 elems_containing_node.end())
652 && (neigh_elem->subdomain_id() == sub_id))
653 common_elem = neigh_elem;
659 for (
const auto common_side : common_elem->side_index_range())
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))
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;
671 if (!(node_found_on_side && neigh_found_on_side &&
672 common_elem->neighbor_ptr(common_side)))
675 const auto matched_side = common_side;
681 const auto matched_neighbor_sub_id =
683 const bool is_matched_side_on_subdomain_boundary = matched_neighbor_sub_id != sub_id;
685 if (is_matched_side_on_subdomain_boundary)
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);
703 return side_grouped_boundary_neighbors;
706 std::set<std::set<const Node *>>
710 const std::unordered_set<dof_id_type> & boundary_node_ids,
712 const std::unordered_map<
dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
718 std::vector<const Node *> neighbors;
720 mesh, node, nodes_to_elem_map, neighbors);
724 std::set<std::set<const Node *>> side_grouped_boundary_neighbors;
726 for (
const auto * neigh : neighbors)
728 const bool is_neighbor_boundary_node =
729 boundary_node_ids.find(neigh->id()) != boundary_node_ids.end();
730 if (!is_neighbor_boundary_node)
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)
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)
745 common_elem = neigh_elem;
750 node, *neigh, *common_elem, boundary_info);
751 if (nodes_have_common_bid)
754 for (
const auto side : common_elem->side_index_range())
758 if (common_elem->neighbor_ptr(side))
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)
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;
771 if (!(node_found_on_side && neigh_found_on_side))
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);
785 return side_grouped_boundary_neighbors;
790 const unsigned int dim,
791 const std::set<std::set<const Node *>> & side_grouped_boundary_neighbors)
797 std::vector<const Node *> neighbors;
798 for (
const auto & side : side_grouped_boundary_neighbors)
799 neighbors.insert(neighbors.end(), side.begin(), side.end());
802 if (
dim == 1 || neighbors.size() == 1)
805 if (
dim == 2 || neighbors.size() == 2)
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()))
812 const Point delta = *(neighbors[i]) - node;
815 if (!dir.relative_fuzzy_equals(ref_dir) && !dir.relative_fuzzy_equals(-ref_dir))
817 all_colinear =
false;
829 std::set<PlaneConstraint> valid_planes;
830 for (
const auto & side_nodes : side_grouped_boundary_neighbors)
832 std::vector<const Node *> side_nodes_vec(side_nodes.begin(), side_nodes.end());
835 const Point vec_i = (*side_nodes_vec[i] - node);
838 const Point vec_j = (*side_nodes_vec[j] - node);
844 valid_planes.emplace(candidate_plane);
850 if (valid_planes.empty())
854 auto it = valid_planes.begin();
856 for (; it != valid_planes.end(); ++it)
863 if (std::holds_alternative<InvalidConstraint>(current))
879 libmesh_assert_msg(!std::holds_alternative<InvalidConstraint>(constraint),
880 "Cannot impose constraint using InvalidConstraint.");
882 if (std::holds_alternative<PointConstraint>(constraint))
884 else if (std::holds_alternative<LineConstraint>(constraint))
886 else if (std::holds_alternative<PlaneConstraint>(constraint))
890 libmesh_assert_msg(
false,
"Unknown constraint type.");
bool operator<(const LineConstraint &other) const
Comparison operator for ordering LineConstraint objects.
const Point & normal() const
Const getter for the _normal attribute.
dof_id_type dof_number(const unsigned int s, const unsigned int var, const unsigned int comp) const
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.
A Node is like a Point, but with more information.
auto norm() const -> decltype(std::norm(T()))
unsigned int get_node_index(const Node *node_ptr) const
bool contains_point(const PointConstraint &p) const
Query whether a point lies on the plane.
IntRange< unsigned short > side_index_range() const
static constexpr Real TOLERANCE
const boundary_id_type side_id
This is the base class from which all geometric element types are derived.
Real _tol
Tolerance to use for numerical comparisons.
virtual bool is_node_on_side(const unsigned int n, const unsigned int s) const =0
void boundary_ids(const Node *node, std::vector< boundary_id_type > &vec_to_fill) const
Fills a user-provided std::vector with the boundary ids associated with Node node.
bool operator==(const PointConstraint &other) const
Equality operator.
PointConstraint()=default
ConstraintVariant intersect(const ConstraintVariant &other) const
Computes the intersection of this line with another constraint.
The libMesh namespace provides an interface to certain functionality in the library.
Point _point
A point on the constraining plane.
bool contains_line(const LineConstraint &l) const
Query whether a line lies on the plane.
const MeshBase & get_mesh() const
Real distance(const Point &p)
This is the MeshBase class.
Point _normal
The direction normal to the constraining plane.
ConstraintVariant intersect_constraints(const ConstraintVariant &a, const ConstraintVariant &b)
Dispatch intersection between two constraint variants.
bool is_parallel(const LineConstraint &l) const
Query whether a line is parallel to this line.
bool operator<(const PlaneConstraint &other) const
Comparison operator for ordering PlaneConstraint objects.
TypeVector< T > unit() const
unsigned int number() const
auto norm_sq() const -> decltype(std::norm(T()))
Represents an invalid constraint (i.e., when the two constraints don't intersect) ...
PlaneConstraint()=default
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.
Point _point
A point on the constraining line.
Point _direction
Direction of the constraining line.
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 ...
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 Point & direction() const
Const getter for the _direction attribute.
void constrain_node_to_line(const Node &node, const Point &line_vec)
Constrain a node to remain on the given line during mesh smoothing.
Manages consistently variables, degrees of freedom, and coefficient vectors.
const Point & point() const
Const getter for the _point attribute.
Represents a plane constraint defined by a point and normal vector.
virtual ~VariationalSmootherConstraint() override
bool operator<(const PointConstraint &other) const
Comparison operator for ordering PointConstraint objects.
Real _tol
Tolerance to use for numerical comparisons.
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
bool operator==(const LineConstraint &other) const
Equality operator.
const bool _preserve_subdomain_boundaries
Whether nodes on subdomain boundaries are subject to change via smoothing.
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
bool absolute_fuzzy_equals(const TypeVector< T > &rhs, Real tol=TOLERANCE) const
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.
Point _point
Location of constraint.
virtual void constrain() override
Constraint function.
ConstraintVariant intersect(const ConstraintVariant &other) const
Computes the intersection of this point with another constraint.
const Elem * neighbor_ptr(unsigned int i) const
bool absolute_fuzzy_equals(const T &var1, const T2 &var2, const Real tol=TOLERANCE *TOLERANCE)
Function to check whether two variables are equal within an absolute tolerance.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
subdomain_id_type subdomain_id() const
const Point & point() const
Const getter for the _point attribute.
Represents a fixed point constraint.
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.
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...
Real _tol
Tolerance to use for numerical comparisons.
unsigned int mesh_dimension() const
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)...
Represents a line constraint defined by a base point and direction vector.
const Point & point() const
Const getter for the _point attribute.
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.
std::variant< PointConstraint, LineConstraint, PlaneConstraint, InvalidConstraint > ConstraintVariant
Type used to store a constraint that may be a PlaneConstraint, LineConstraint, or PointConstraint...
bool is_parallel(const PlaneConstraint &p) const
Query whether a plane is parallel to this plane.
const DofMap & get_dof_map() const
A Point defines a location in LIBMESH_DIM dimensional Real space.
bool operator==(const PlaneConstraint &other) const
Equality operator.
auto index_range(const T &sizable)
Helper function that returns an IntRange<std::size_t> representing all the indices of the passed-in v...
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...
void fix_node(const Node &node)
Constrain (i.e., fix) a node to not move during mesh smoothing.
VariationalSmootherConstraint(System &sys, const bool &preserve_subdomain_boundaries)
Constructor.
bool contains_point(const PointConstraint &p) const
Query whether a point lies on the line.
ConstraintVariant intersect(const ConstraintVariant &other) const
Computes the intersection of this plane with another constraint.