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.");
313 if (std::holds_alternative<PointConstraint>(c))
314 os <<
"point constraint: point=" << std::get<PointConstraint>(c).point();
316 else if (std::holds_alternative<LineConstraint>(c))
318 const auto & line = std::get<LineConstraint>(c);
319 os <<
"line constraint: point=" << line.point() <<
", direction=" << line.direction();
322 else if (std::holds_alternative<PlaneConstraint>(c))
324 const auto & plane = std::get<PlaneConstraint>(c);
325 os <<
"plane constraint: point=" << plane.point() <<
", normal=" << plane.normal();
328 else if (std::holds_alternative<InvalidConstraint>(c))
329 os <<
"invalid constraint";
335 System & sys,
const bool & preserve_subdomain_boundaries,
const unsigned int verbosity)
336 : Constraint(), _verbosity(verbosity), _sys(sys), _preserve_subdomain_boundaries(preserve_subdomain_boundaries) {
344 const auto dim =
mesh.mesh_dimension();
345 const auto & proc_id =
mesh.processor_id();
348 std::unordered_map<dof_id_type, std::vector<const Elem *>> nodes_to_elem_map;
351 const auto & boundary_info =
mesh.get_boundary_info();
356 std::unordered_map<dof_id_type, ConstraintVariant> subdomain_boundary_map;
359 for (
const auto * elem :
mesh.active_element_ptr_range())
361 const auto & sub_id1 = elem->subdomain_id();
362 for (
const auto side : elem->side_index_range())
364 const auto * neighbor = elem->neighbor_ptr(side);
365 if (neighbor ==
nullptr)
368 const auto & sub_id2 = neighbor->subdomain_id();
369 if (sub_id1 == sub_id2)
374 for (
const auto local_node_id : elem->nodes_on_side(side))
376 const auto & node =
mesh.node_ref(elem->node_id(local_node_id));
380 if (subdomain_boundary_map.count(node.id()) ||
381 node.processor_id() != proc_id)
385 const auto side_grouped_boundary_neighbors =
387 mesh, node, sub_id1, nodes_to_elem_map);
390 const auto subdomain_constraint =
395 if (boundary_node_ids.find(node.id()) == boundary_node_ids.end())
399 << std::endl <<
" " << node << std::endl
400 <<
" " << subdomain_constraint << std::endl;
410 subdomain_boundary_map[node.id()] = subdomain_constraint;
419 for (
const auto & bid : boundary_node_ids)
421 const auto & node =
mesh.node_ref(bid);
424 if (node.processor_id() != proc_id)
429 mesh, node, boundary_node_ids, boundary_info, nodes_to_elem_map);
432 const auto boundary_constraint =
436 if (
const auto it = subdomain_boundary_map.find(bid); it != subdomain_boundary_map.end())
438 const auto & subdomain_constraint = it->second;
445 if (std::holds_alternative<InvalidConstraint>(combined_constraint))
449 libMesh::out <<
"Imposing boundary/subdomain constraint on " 450 << std::endl <<
" " << node << std::endl
451 <<
" " << combined_constraint << std::endl;
460 << std::endl << node << std::endl
461 <<
" " << boundary_constraint << std::endl;
476 const auto constrained_value = node(d);
479 constrained_dof_index, constraint_row, constrained_value,
true);
484 const Point & ref_normal_vec)
488 std::vector<Real> xyz_coefs;
496 unsigned int constrained_dim = 0;
502 Real max_abs_coef = 0.;
505 const auto coef = ref_normal_vec(d);
506 xyz_coefs.push_back(coef);
509 const auto coef_abs = std::abs(coef);
510 if (coef_abs > max_abs_coef)
512 max_abs_coef = coef_abs;
520 if (free_dim == constrained_dim)
524 constraint_row[free_dof_index] = -xyz_coefs[free_dim] / xyz_coefs[constrained_dim];
527 const auto inhomogeneous_part = -c / xyz_coefs[constrained_dim];
530 constrained_dof_index, constraint_row, inhomogeneous_part,
true);
534 const Point & line_vec)
540 const std::vector<Real> line_vec_coefs{line_vec(0), line_vec(1), line_vec(2)};
541 auto it = std::max_element(line_vec_coefs.begin(), line_vec_coefs.end(), [](
double a,
double b) {
542 return std::abs(a) < std::abs(
b);
544 const unsigned int free_dim =
std::distance(line_vec_coefs.begin(), it);
560 if (constrained_dim == free_dim)
564 constraint_row[free_dof_index] = line_vec(constrained_dim) / line_vec(free_dim);
565 const auto inhomogeneous_part =
566 node(constrained_dim) - node(free_dim) * line_vec(constrained_dim) / line_vec(free_dim);
569 constrained_dof_index, constraint_row, inhomogeneous_part,
true);
601 const Node & neighbor_node,
602 const Elem & containing_elem,
605 bool nodes_share_bid =
false;
608 const auto node_id = containing_elem.
get_node_index(&boundary_node);
609 const auto neighbor_id = containing_elem.
get_node_index(&neighbor_node);
620 std::vector<boundary_id_type> boundary_ids;
622 if (boundary_ids.size())
624 nodes_share_bid =
true;
628 return nodes_share_bid;
631 std::set<std::set<const Node *>>
636 const std::unordered_map<
dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
642 std::vector<const Node *> neighbors;
644 mesh, node, nodes_to_elem_map, neighbors);
648 std::set<std::set<const Node *>> side_grouped_boundary_neighbors;
650 for (
const auto * neigh : neighbors)
654 const auto & elems_containing_node = libmesh_map_find(nodes_to_elem_map, node.
id());
655 const auto & elems_containing_neigh = libmesh_map_find(nodes_to_elem_map, neigh->id());
656 const Elem * common_elem =
nullptr;
657 for (
const auto * neigh_elem : elems_containing_neigh)
659 if ((std::find(elems_containing_node.begin(), elems_containing_node.end(), neigh_elem) !=
660 elems_containing_node.end())
662 && (neigh_elem->subdomain_id() == sub_id))
663 common_elem = neigh_elem;
669 for (
const auto common_side : common_elem->side_index_range())
671 bool node_found_on_side =
false;
672 bool neigh_found_on_side =
false;
673 for (
const auto local_node_id : common_elem->nodes_on_side(common_side))
675 if (common_elem->node_id(local_node_id) == node.
id())
676 node_found_on_side =
true;
677 else if (common_elem->node_id(local_node_id) == neigh->id())
678 neigh_found_on_side =
true;
681 if (!(node_found_on_side && neigh_found_on_side &&
682 common_elem->neighbor_ptr(common_side)))
685 const auto matched_side = common_side;
691 const auto matched_neighbor_sub_id =
693 const bool is_matched_side_on_subdomain_boundary = matched_neighbor_sub_id != sub_id;
695 if (is_matched_side_on_subdomain_boundary)
698 const auto nodes_on_side = common_elem->nodes_on_side(common_side);
699 std::set<const Node *> node_ptrs_on_side;
700 for (
const auto local_node_id : nodes_on_side)
701 node_ptrs_on_side.insert(common_elem->node_ptr(local_node_id));
702 node_ptrs_on_side.erase(node_ptrs_on_side.find(&node));
703 side_grouped_boundary_neighbors.insert(node_ptrs_on_side);
713 return side_grouped_boundary_neighbors;
716 std::set<std::set<const Node *>>
720 const std::unordered_set<dof_id_type> & boundary_node_ids,
722 const std::unordered_map<
dof_id_type, std::vector<const Elem *>> & nodes_to_elem_map)
728 std::vector<const Node *> neighbors;
730 mesh, node, nodes_to_elem_map, neighbors);
734 std::set<std::set<const Node *>> side_grouped_boundary_neighbors;
736 for (
const auto * neigh : neighbors)
738 const bool is_neighbor_boundary_node =
739 boundary_node_ids.find(neigh->id()) != boundary_node_ids.end();
740 if (!is_neighbor_boundary_node)
745 const auto & elems_containing_node = libmesh_map_find(nodes_to_elem_map, node.
id());
746 const auto & elems_containing_neigh = libmesh_map_find(nodes_to_elem_map, neigh->id());
747 const Elem * common_elem =
nullptr;
748 for (
const auto * neigh_elem : elems_containing_neigh)
750 const bool is_neigh_common =
751 std::find(elems_containing_node.begin(), elems_containing_node.end(), neigh_elem) !=
752 elems_containing_node.end();
753 if (!is_neigh_common)
755 common_elem = neigh_elem;
760 node, *neigh, *common_elem, boundary_info);
761 if (nodes_have_common_bid)
764 for (
const auto side : common_elem->side_index_range())
768 if (common_elem->neighbor_ptr(side))
771 bool node_found_on_side =
false;
772 bool neigh_found_on_side =
false;
773 const auto nodes_on_side = common_elem->nodes_on_side(side);
774 for (
const auto local_node_id : nodes_on_side)
776 if (common_elem->node_id(local_node_id) == node.
id())
777 node_found_on_side =
true;
778 else if (common_elem->node_id(local_node_id) == neigh->id())
779 neigh_found_on_side =
true;
781 if (!(node_found_on_side && neigh_found_on_side))
784 std::set<const Node *> node_ptrs_on_side;
785 for (
const auto local_node_id : nodes_on_side)
786 node_ptrs_on_side.insert(common_elem->node_ptr(local_node_id));
787 node_ptrs_on_side.erase(node_ptrs_on_side.find(&node));
788 side_grouped_boundary_neighbors.insert(node_ptrs_on_side);
795 return side_grouped_boundary_neighbors;
800 const unsigned int dim,
801 const std::set<std::set<const Node *>> & side_grouped_boundary_neighbors)
807 std::vector<const Node *> neighbors;
808 for (
const auto & side : side_grouped_boundary_neighbors)
809 neighbors.insert(neighbors.end(), side.begin(), side.end());
815 if (
dim == 1 || neighbors.size() <= 1)
818 if (
dim == 2 || neighbors.size() == 2)
821 bool all_colinear =
true;
822 const Point ref_dir = (*neighbors[0] - node).unit();
823 for (
const auto i :
make_range(std::size_t(1), neighbors.size()))
825 const Point delta = *(neighbors[i]) - node;
828 if (!dir.relative_fuzzy_equals(ref_dir) && !dir.relative_fuzzy_equals(-ref_dir))
830 all_colinear =
false;
842 std::set<PlaneConstraint> valid_planes;
843 for (
const auto & side_nodes : side_grouped_boundary_neighbors)
845 std::vector<const Node *> side_nodes_vec(side_nodes.begin(), side_nodes.end());
848 const Point vec_i = (*side_nodes_vec[i] - node);
851 const Point vec_j = (*side_nodes_vec[j] - node);
857 valid_planes.emplace(candidate_plane);
863 if (valid_planes.empty())
867 auto it = valid_planes.begin();
869 for (; it != valid_planes.end(); ++it)
876 if (std::holds_alternative<InvalidConstraint>(current))
892 libmesh_assert_msg(!std::holds_alternative<InvalidConstraint>(constraint),
893 "Cannot impose constraint using InvalidConstraint.");
895 if (std::holds_alternative<PointConstraint>(constraint))
897 else if (std::holds_alternative<LineConstraint>(constraint))
899 else if (std::holds_alternative<PlaneConstraint>(constraint))
903 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.
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.
std::ostream & operator<<(std::ostream &os, const OrderWrapper &order)
Overload stream operators.
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
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 ...
VariationalSmootherConstraint(System &sys, const bool &preserve_subdomain_boundaries, const unsigned int verbosity)
Constructor.
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...
const unsigned int _verbosity
Verbosity setting.
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...
void fix_node(const Node &node)
Constrain (i.e., fix) a node to not move during mesh smoothing.
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.