19 #include "libmesh/libmesh_config.h" 22 #include "libmesh/mesh_triangle_holes.h" 24 #include "libmesh/boundary_info.h" 25 #include "libmesh/elem.h" 26 #include "libmesh/int_range.h" 27 #include "libmesh/mesh_base.h" 28 #include "libmesh/mesh_serializer.h" 29 #include "libmesh/node.h" 30 #include "libmesh/parallel_algebra.h" 31 #include "libmesh/simple_range.h" 43 int signof(
Real val) {
44 return (0 < val) - (val < 0);
50 int orientation(
const Point & p0,
54 const Real detleft = (p0(0)-p2(0))*(p1(1)-p2(1));
55 const Real detright = (p0(1)-p2(1))*(p1(0)-p2(0));
57 return signof(detleft - detright);
61 int ray_orientation(
const Point & p0,
64 const Point & ray_target)
66 const Point rayvec = ray_target - source;
67 const Point edgevec = p1 - p0;
68 const Real det = edgevec(0)*rayvec(1)-edgevec(1)*rayvec(0);
73 bool is_intersection(
const Point & source,
74 const Point & ray_target,
75 const Point & edge_pt0,
76 const Point & edge_pt1)
78 int orient_st0 = orientation(source, ray_target, edge_pt0);
79 int orient_st1 = orientation(source, ray_target, edge_pt1);
80 int orient_edge_s = orientation(edge_pt0, edge_pt1, source);
81 int orient_edge_t = ray_orientation(edge_pt0, edge_pt1, source, ray_target);
84 if ((orient_st0 == -orient_st1) &&
85 (orient_edge_s != orient_edge_t))
107 Real find_intersection(
const Point & source,
108 const Point & ray_target,
109 const Point & edge_pt0,
110 const Point & edge_pt1,
111 const Point & edge_pt2)
114 if (!is_intersection(source, ray_target, edge_pt0, edge_pt1))
119 const Real raydx = ray_target(0)-source(0),
120 raydy = ray_target(1)-source(1),
121 edgedx = edge_pt1(0)-edge_pt0(0),
122 edgedy = edge_pt1(1)-edge_pt0(1);
123 const Real denom = edgedx * raydy - edgedy * raydx;
129 const Real one_over_denom = 1 / denom;
131 const Real targetsdx = edge_pt1(0)-ray_target(0),
132 targetsdy = edge_pt1(1)-ray_target(1);
134 const Real t_num = targetsdx * raydy -
136 const Real t = t_num * one_over_denom;
146 const Real prevdx = edge_pt0(0)-ray_target(0),
147 prevdy = edge_pt0(1)-ray_target(1);
148 const Real p_num = prevdx * raydy -
151 const Real nextdx = edge_pt2(0)-ray_target(0),
152 nextdy = edge_pt2(1)-ray_target(1);
153 const Real n_num = nextdx * raydy -
156 if (signof(p_num) != -signof(n_num))
160 const Real u_num = targetsdx * edgedy - targetsdy * edgedx;
161 const Real u = u_num * one_over_denom;
162 const Real ray_fraction = (1-u);
165 if (ray_fraction < 0)
169 ray_fraction * std::sqrt(raydx*raydx + raydy*raydy);
192 const unsigned int np = this->n_points();
197 const Point p0 = this->point(0);
210 for (
unsigned int i=2; i != np; ++i)
212 const Point e_0im = this->point(i-1) - p0,
213 e_0i = this->point(i) - p0;
215 areavec += e_0i.
cross(e_0im);
225 Point ray_target)
const 227 const auto np = this->n_points();
229 std::vector<Real> intersection_distances;
233 const Point & p0 = this->point(i),
234 & p1 = this->point((i+1)%np),
235 & p2 = this->point((i+2)%np);
236 const Real intersection_distance =
237 find_intersection(ray_start, ray_target, p0, p1, p2);
238 if (intersection_distance >= 0)
239 intersection_distances.push_back
240 (intersection_distance);
243 return intersection_distances;
257 inside += this->point(i);
259 inside /= this->n_points();
264 std::vector<Real> intersection_distances =
265 this->find_ray_intersections(inside, ray_target);
269 if (!intersection_distances.size())
271 ray_target = inside -
Point(1);
272 intersection_distances =
273 this->find_ray_intersections(inside, ray_target);
279 (!intersection_distances.size(),
280 "Can't find a center for a MeshedHole!");
282 if (intersection_distances.size() % 2)
289 Real min_distance = std::numeric_limits<Real>::max(),
290 second_distance = std::numeric_limits<Real>::max();
291 for (
Real d : intersection_distances)
292 if (d < min_distance)
294 second_distance = min_distance;
298 const Point ray = ray_target - inside;
299 inside += ray * (min_distance + second_distance)/2;
310 std::vector<Real> intersection_distances =
311 this->find_ray_intersections(p, ray_target);
315 return intersection_distances.size() % 2;
325 unsigned int n_points_in) :
328 _n_points(n_points_in)
341 const Real theta =
static_cast<Real>(n) * 2.0 *
libMesh::pi / static_cast<Real>(_n_points);
343 return Point(_center(0) + _radius*std::cos(theta),
344 _center(1) + _radius*std::sin(theta),
361 return this->transform(_underlying.point(n));
367 return this->transform(_underlying.inside());
373 const Real cos_a = std::cos(_angle);
374 const Real sin_a = std::sin(_angle);
375 return Point(p(0)*cos_a-p(1)*sin_a + _shift(0),
376 p(1)*cos_a+p(1)*sin_a + _shift(1));
384 std::vector<Point> points)
386 _points(
std::move(points))
394 std::vector<Point> points,
395 std::vector<unsigned int> segment_indices)
397 _points(
std::move(points)),
398 _segment_indices(
std::move(segment_indices))
403 : _points(
std::move(points))
412 : _center(orig.inside())
414 const unsigned int np = orig.
n_points();
423 return _points.size();
429 libmesh_assert_less (n, _points.size());
442 return _segment_indices;
452 std::set<std::size_t> ids)
453 : _center(
std::numeric_limits<
Real>::max())
465 std::string error_reported;
468 error_reported = std::move(er);
470 libmesh_error_msg(error_reported);
477 libmesh_error_msg_if(!error_reported.empty(), error_reported);
491 std::multimap<
const Node *,
492 std::pair<const Node *, int>> hole_edge_map;
497 std::map<std::pair<const Node *, const Node *>,
498 std::vector<const Node *>> hole_midpoint_map;
500 std::vector<boundary_id_type> bcids;
504 for (
const auto & elem :
mesh.active_element_ptr_range())
506 if (elem->dim() == 1)
508 if (ids.empty() || ids.count(elem->subdomain_id()))
510 hole_edge_map.emplace(elem->node_ptr(0),
511 std::make_pair(elem->node_ptr(1),
513 hole_edge_map.emplace(elem->node_ptr(1),
514 std::make_pair(elem->node_ptr(0),
516 if (elem->type() ==
EDGE3)
518 hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(0),
520 std::vector<const Node *>{elem->node_ptr(2)});
521 hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(1),
523 std::vector<const Node *>{elem->node_ptr(2)});
525 else if (elem->type() ==
EDGE4)
527 hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(0),
529 std::vector<const Node *>{elem->node_ptr(2),
531 hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(1),
533 std::vector<const Node *>{elem->node_ptr(3),
537 libmesh_assert_equal_to(elem->default_side_order(), 1);
542 if (elem->dim() == 2)
544 const auto ns = elem->n_sides();
549 bool add_edge =
false;
550 if (!elem->neighbor_ptr(s) && ids.empty())
560 hole_edge_map.emplace(elem->node_ptr(s),
561 std::make_pair(elem->node_ptr((s+1)%ns),
564 hole_edge_map.emplace(elem->node_ptr((s+1)%ns),
565 std::make_pair(elem->node_ptr(s),
568 if (elem->default_side_order() == 2)
570 hole_midpoint_map.emplace(std::make_pair(elem->node_ptr(s),
571 elem->node_ptr((s+1)%ns)),
572 std::vector<const Node *>{elem->node_ptr(s+ns)});
573 hole_midpoint_map.emplace(std::make_pair(elem->node_ptr((s+1)%ns),
575 std::vector<const Node *>{elem->node_ptr(s+ns)});
578 libmesh_assert_equal_to(elem->default_side_order(), 1);
586 if (hole_edge_map.empty())
595 auto extract_edge_vector =
597 std::tuple<std::vector<const Node *>, std::vector<const Node *>,
int>
598 hole_points_and_edge_type
599 {{hole_edge_map.begin()->first, hole_edge_map.begin()->second.first},
600 {}, hole_edge_map.begin()->second.second};
602 auto & hole_points = std::get<0>(hole_points_and_edge_type);
603 auto & midpoint_points = std::get<1>(hole_points_and_edge_type);
604 int & edge_type = std::get<2>(hole_points_and_edge_type);
607 hole_edge_map.erase(hole_points.front());
610 for (
const Node * last = hole_points.front(),
611 * n = hole_points.back();
612 n != hole_points.front();
614 n = hole_points.back())
616 auto [next_it_begin, next_it_end] = hole_edge_map.equal_range(n);
622 for (
const auto & [key, val] :
as_range(next_it_begin, next_it_end))
624 libmesh_assert_equal_to(key, n);
626 libmesh_assert_not_equal_to(val.first, n);
629 if (val.first == last)
634 if (val.second != edge_type &&
638 edge_type = val.second;
640 report_error(
"MeshedHole sees inconsistent triangle orientations on boundary");
646 hole_edge_map.erase(next_it_begin, next_it_end);
648 hole_points.push_back(
next);
651 for (
auto i :
make_range(hole_points.size()-1))
653 const auto & midpoints = hole_midpoint_map[{hole_points[i],hole_points[i+1]}];
654 midpoint_points.insert(midpoint_points.end(),
655 midpoints.begin(), midpoints.end());
658 hole_points.pop_back();
660 return hole_points_and_edge_type;
667 int n_negative_areas = 0,
668 n_positive_areas = 0,
669 n_edgeelem_loops = 0;
671 std::vector<const Node *> outer_hole_points, outer_mid_points;
672 int outer_edge_type = -1;
673 Real twice_outer_area = 0,
674 abs_twice_outer_area = 0;
678 std::vector<std::pair<Real, int>> areas;
681 while (!hole_edge_map.empty()) {
682 auto [hole_points, mid_points, edge_type] = extract_edge_vector();
687 if (n_edgeelem_loops > 1)
688 report_error(
"MeshedHole is confused by multiple loops of Edge elements");
689 if (n_positive_areas || n_negative_areas)
690 report_error(
"MeshedHole is confused by meshes with both Edge and 2D-side boundaries");
693 const std::size_t n_hole_points = hole_points.size();
694 if (n_hole_points < 3)
695 report_error(
"Loop with only " + std::to_string(n_hole_points) +
696 " hole edges found in mesh!");
698 Real twice_this_area = 0;
699 const Point p0 = *hole_points[0];
700 for (
unsigned int i=2; i != n_hole_points; ++i)
702 const Point e_0im = *hole_points[i-1] - p0,
703 e_0i = *hole_points[i] - p0;
705 twice_this_area += e_0i.
cross(e_0im)(2);
708 auto abs_twice_this_area = std::abs(twice_this_area);
710 if (((twice_this_area > 0) && edge_type == 2) ||
711 ((twice_this_area < 0) && edge_type == 1))
713 else if (edge_type != 0)
717 areas.push_back({twice_this_area/2,edge_type});
720 if (abs_twice_this_area > abs_twice_outer_area)
722 twice_outer_area = twice_this_area;
723 abs_twice_outer_area = abs_twice_this_area;
724 outer_hole_points = std::move(hole_points);
725 outer_mid_points = std::move(mid_points);
726 outer_edge_type = edge_type;
730 _points.resize(outer_hole_points.size());
731 std::transform(outer_hole_points.begin(),
732 outer_hole_points.end(),
734 [](
const Node * n){
return Point(*n); });
736 std::transform(outer_mid_points.begin(),
737 outer_mid_points.end(),
739 [](
const Node * n){
return Point(*n); });
741 if (!twice_outer_area)
742 report_error(
"Zero-area MeshedHoles are not currently supported");
746 if (twice_outer_area > 0)
761 auto print_areas = [areas](){
763 static const std::vector<std::string> edgenames {
"E",
"CW",
"CCW"};
764 for (
auto area : areas)
770 auto print_areas = [](){};
773 if (((twice_outer_area > 0) && outer_edge_type == 2) ||
774 ((twice_outer_area < 0) && outer_edge_type == 1))
776 if (n_positive_areas > 1)
780 std::to_string(n_positive_areas) +
781 " counter-clockwise boundaries and cannot choose one!");
785 else if (outer_edge_type != 0)
787 if (n_negative_areas > 1)
791 std::to_string(n_negative_areas) +
792 " clockwise boundaries and cannot choose one!");
806 return _points.size();
813 return _midpoints.size() / _points.size();
819 libmesh_assert_less (n, _points.size());
825 const unsigned int n)
const 827 const unsigned int n_mid = this->n_midpoints();
828 libmesh_assert_less (m, n_mid);
829 libmesh_assert_less (n, _points.size());
830 return _midpoints[n*n_mid+m];
837 if (_center(0) == std::numeric_limits<Real>::max())
838 _center = this->calculate_inside_point();
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
std::vector< Point > _points
The sorted vector of points which makes up the hole.
A Node is like a Point, but with more information.
auto norm() const -> decltype(std::norm(T()))
virtual unsigned int n_points() const override
The number of geometric points which define the hole.
An abstract class for defining a 2-dimensional hole.
std::vector< Point > _points
Reference to the vector of points which makes up the hole.
ArbitraryHole(const Point ¢er, std::vector< Point > points)
The fastest constructor requires a point which lies in the interior of the hole and a reference to a ...
std::vector< Real > find_ray_intersections(Point ray_start, Point ray_target) const
Helper function for contains(), also useful for MeshedHole::inside()
Real area() const
Return the area of the hole.
const Parallel::Communicator & comm() const
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.
The libMesh namespace provides an interface to certain functionality in the library.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
const BoundaryInfo & get_boundary_info() const
The information about boundary ids on the mesh.
MeshedHole(const MeshBase &mesh, std::set< std::size_t > ids={})
The constructor requires a mesh defining the hole, and optionally boundary+subdomain ids restricting ...
Real distance(const Point &p)
This is the MeshBase class.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
void libmesh_ignore(const Args &...)
Point transform(const Point &p) const
Rotate-and-shift equations.
void report_error(const char *file, int line, const char *date, const char *time, std::ostream &os=libMesh::err)
SimpleRange< IndexType > as_range(const std::pair< IndexType, IndexType > &p)
Helper function that allows us to treat a homogenous pair as a range.
The BoundaryInfo class contains information relevant to boundary conditions including storing faces...
virtual unsigned int n_points() const override
The number of geometric points which define the hole.
TypeVector< typename CompareTypes< T, T2 >::supertype > cross(const TypeVector< T2 > &v) const
virtual unsigned int n_midpoints() const override
The number of geometric midpoints along each of the sides defining the hole.
virtual unsigned int n_points() const override
The number of geometric points which define the hole.
void broadcast(T &data, const unsigned int root_id=0, const bool identical_sizes=false) const
Point calculate_inside_point() const
Calculate an inside point based on our boundary.
virtual std::vector< unsigned int > segment_indices() const override
Starting indices of points for a hole with multiple disconnected boundaries.
bool contains(Point p) const
Return true iff p lies inside the hole.
DIE A HORRIBLE DEATH HERE typedef LIBMESH_DEFAULT_SCALAR_TYPE Real
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
Temporarily serialize a DistributedMesh for non-distributed-mesh capable code paths.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
std::vector< Point > _midpoints
The sorted vector of midpoints in between points along the edges of the hole.
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
virtual Point inside() const override
Return an (arbitrary) point which lies inside the hole.
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...
virtual Point point(const unsigned int n) const override
Return the nth point defining the hole.
std::vector< unsigned int > _segment_indices
RealGradient areavec() const
Return a vector with right-hand-rule orientation and length of twice area() squared.
virtual Point midpoint(const unsigned int m, const unsigned int n) const override
Return the midpoint m along the side n defining the hole.
Point _center
arbitrary (x,y) location inside the hole
virtual Point point(const unsigned int n) const =0
Return the nth point defining the hole.
processor_id_type processor_id() const
PolygonHole(const Point ¢er, Real radius, unsigned int n_points)
Constructor specifying the center, radius, and number of points which comprise the hole...
A Point defines a location in LIBMESH_DIM dimensional Real space.
virtual unsigned int n_points() const =0
The number of geometric points which define the hole.