PeriDEM 0.3.0
PeriDEM -- Peridynamics-based high-fidelity model for granular media
Loading...
Searching...
No Matches
annulusMesh2D.cpp
Go to the documentation of this file.
1/*
2 * -------------------------------------------
3 * Copyright (c) 2021 - 2026 Prashant K. Jha
4 * -------------------------------------------
5 * PeriDEM https://github.com/prashjha/PeriDEM
6 *
7 * Distributed under the Boost Software License, Version 1.0.
8 */
9
10#include "annulusMesh2D.h"
11#include "gmshOccCompat.h"
13#include "geom/geomObjects.h"
14#include <cmath>
15#include <gmsh.h>
16#include <sstream>
17#include <stdexcept>
18#include <vector>
19
20namespace mesh_gen {
21namespace {
22
24 const auto &blo = outer.d_vertices[0];
25 const auto &bhi = outer.d_vertices[2];
26 const auto &bli = inner.d_vertices[0];
27 const auto &bhi_i = inner.d_vertices[2];
28
29 constexpr double eps = 1.0e-12;
30 if (std::abs(blo.d_z - bli.d_z) > 1.0e-9 || std::abs(bhi.d_z - bhi_i.d_z) > 1.0e-9)
31 throw std::runtime_error("buildAnnulus2DInCurrentModel: inner and outer rectangles must lie in the same z plane.");
32
33 if (!(bli.d_x > blo.d_x + eps && bhi_i.d_x < bhi.d_x - eps && bli.d_y > blo.d_y + eps &&
34 bhi_i.d_y < bhi.d_y - eps))
35 throw std::runtime_error(
36 "buildAnnulus2DInCurrentModel: inner rectangle must be strictly inside the outer rectangle.");
37}
38
39void assertCircleAnnulusValid(const geom::Circle &outer, const geom::Circle &inner) {
40 constexpr double eps = 1.0e-10;
41 if ((outer.d_x - inner.d_x).lengthSq() > eps)
42 throw std::runtime_error(
43 "buildAnnulus2DInCurrentModel: circle annulus requires coincident centers (concentric circles).");
44 if (inner.d_r <= 0. || outer.d_r <= inner.d_r)
45 throw std::runtime_error(
46 "buildAnnulus2DInCurrentModel: require 0 < r_inner < r_outer for circle − circle.");
47}
48
49void assertEllipseAnnulusValid(const geom::Ellipse &outer, const geom::Ellipse &inner) {
50 constexpr double eps = 1.0e-10;
51 if ((outer.d_x - inner.d_x).lengthSq() > eps)
52 throw std::runtime_error(
53 "buildAnnulus2DInCurrentModel: ellipse annulus requires coincident centers.");
54 if (std::abs(outer.d_theta - inner.d_theta) > 1.0e-9)
55 throw std::runtime_error(
56 "buildAnnulus2DInCurrentModel: ellipse annulus requires the same orientation θ.");
57 if (inner.d_a <= 0. || inner.d_b <= 0. || outer.d_a <= inner.d_a || outer.d_b <= inner.d_b)
58 throw std::runtime_error(
59 "buildAnnulus2DInCurrentModel: require 0 < a_in < a_out and 0 < b_in < b_out.");
60}
61
64 const double rx = std::max(e.d_a, e.d_b);
65 const double ry = std::min(e.d_a, e.d_b);
66 double theta = e.d_theta;
67 if (e.d_a < e.d_b)
68 theta += M_PI / 2.;
69 return addDiskOcc(e.d_x.d_x, e.d_x.d_y, e.d_x.d_z, rx, ry, theta);
70}
71
72static int firstTagOfDim(const std::vector<std::pair<int, int>> &ov, int dim) {
73 for (const auto &pr : ov)
74 if (pr.first == dim)
75 return pr.second;
76 return -1;
77}
78
85 if (a.d_inObj_p->d_name == "circle" && a.d_outObj_p->d_name == "circle") {
86 const auto &outer = *static_cast<const geom::Circle *>(a.d_outObj_p);
87 const auto &inner = *static_cast<const geom::Circle *>(a.d_inObj_p);
88 const double r_mid = 0.5 * (outer.d_r + inner.d_r);
89 return {outer.d_x.d_x + r_mid, outer.d_x.d_y, outer.d_x.d_z};
90 }
91 if (a.d_inObj_p->d_name == "ellipse" && a.d_outObj_p->d_name == "ellipse") {
92 const auto &outer = *static_cast<const geom::Ellipse *>(a.d_outObj_p);
93 const auto &inner = *static_cast<const geom::Ellipse *>(a.d_inObj_p);
94 // Mid-wall along the major axis direction in the ellipse frame.
95 const double a_mid = 0.5 * (outer.d_a + inner.d_a);
96 return {outer.d_x.d_x + a_mid * std::cos(outer.d_theta),
97 outer.d_x.d_y + a_mid * std::sin(outer.d_theta), outer.d_x.d_z};
98 }
99 if (a.d_inObj_p->d_name == "rectangle" && a.d_outObj_p->d_name == "rectangle") {
100 const auto &outer = *static_cast<const geom::Rectangle *>(a.d_outObj_p);
101 const auto &inner = *static_cast<const geom::Rectangle *>(a.d_inObj_p);
102 const auto &blo = outer.d_vertices[0];
103 const auto &bli = inner.d_vertices[0];
104 return {0.5 * (blo.d_x + bli.d_x), 0.5 * (blo.d_y + bli.d_y), blo.d_z};
105 }
106 return a.center();
107}
108
109void finish2DCutEmbedSurface(const std::vector<std::pair<int, int>> &ov, const geom::AnnulusGeomObject &a,
110 double h) {
111
112 int surface_tag = firstTagOfDim(ov, 2);
113 if (surface_tag < 0) {
114 std::vector<std::pair<int, int>> ents;
115 gmsh::model::getEntities(ents, 2);
116 if (!ents.empty())
117 surface_tag = ents.back().second;
118 }
119 if (surface_tag < 0) {
120 std::ostringstream oss;
121 oss << "buildAnnulus2DInCurrentModel: boolean cut did not produce a surface (ov size = "
122 << ov.size() << ").";
123 throw std::runtime_error(oss.str());
124 }
125
127 const int p = gmsh::model::occ::addPoint(c.d_x, c.d_y, c.d_z, h);
128 gmsh::model::occ::synchronize();
129 gmsh::model::mesh::embed(0, {p}, 2, surface_tag);
130 gmsh::model::occ::synchronize();
131}
132
134 const auto &outer = *static_cast<const geom::Rectangle *>(a.d_outObj_p);
135 const auto &inner = *static_cast<const geom::Rectangle *>(a.d_inObj_p);
136 assertRectangleAnnulusValid(outer, inner);
137
138 const auto &lo = outer.d_vertices[0];
139 const int out_surf = gmsh::model::occ::addRectangle(lo.d_x, lo.d_y, lo.d_z, outer.d_Lx, outer.d_Ly);
140 gmsh::model::occ::synchronize();
141
142 const auto &li = inner.d_vertices[0];
143 const int in_surf =
144 gmsh::model::occ::addRectangle(li.d_x, li.d_y, li.d_z, inner.d_Lx, inner.d_Ly);
145 gmsh::model::occ::synchronize();
146
147 std::vector<std::pair<int, int>> ov;
148 std::vector<std::vector<std::pair<int, int>>> ovv;
149 gmsh::model::occ::cut({{2, out_surf}}, {{2, in_surf}}, ov, ovv, -1, true, true);
150 gmsh::model::occ::synchronize();
151 gmsh::model::occ::removeAllDuplicates();
152 gmsh::model::occ::synchronize();
153
154 finish2DCutEmbedSurface(ov, a, h);
155}
156
158 const auto &outer = *static_cast<const geom::Circle *>(a.d_outObj_p);
159 const auto &inner = *static_cast<const geom::Circle *>(a.d_inObj_p);
160 assertCircleAnnulusValid(outer, inner);
161
162 const int out_surf = addDiskOcc(outer.d_x.d_x, outer.d_x.d_y, outer.d_x.d_z,
163 outer.d_r, outer.d_r);
164 gmsh::model::occ::synchronize();
165 const int in_surf = addDiskOcc(inner.d_x.d_x, inner.d_x.d_y, inner.d_x.d_z,
166 inner.d_r, inner.d_r);
167 gmsh::model::occ::synchronize();
168
169 std::vector<std::pair<int, int>> ov;
170 std::vector<std::vector<std::pair<int, int>>> ovv;
171 gmsh::model::occ::cut({{2, out_surf}}, {{2, in_surf}}, ov, ovv, -1, true, true);
172 gmsh::model::occ::synchronize();
173 gmsh::model::occ::removeAllDuplicates();
174 gmsh::model::occ::synchronize();
175
176 finish2DCutEmbedSurface(ov, a, h);
177}
178
180 const auto &outer = *static_cast<const geom::Ellipse *>(a.d_outObj_p);
181 const auto &inner = *static_cast<const geom::Ellipse *>(a.d_inObj_p);
182 assertEllipseAnnulusValid(outer, inner);
183
184 const int out_surf = addEllipseDiskOcc(outer);
185 gmsh::model::occ::synchronize();
186 const int in_surf = addEllipseDiskOcc(inner);
187 gmsh::model::occ::synchronize();
188
189 std::vector<std::pair<int, int>> ov;
190 std::vector<std::vector<std::pair<int, int>>> ovv;
191 gmsh::model::occ::cut({{2, out_surf}}, {{2, in_surf}}, ov, ovv, -1, true, true);
192 gmsh::model::occ::synchronize();
193 gmsh::model::occ::removeAllDuplicates();
194 gmsh::model::occ::synchronize();
195
196 finish2DCutEmbedSurface(ov, a, h);
197}
198
199} // namespace
200
202
203 if (a.d_dim != 2)
204 throw std::runtime_error("buildAnnulus2DInCurrentModel: expected d_dim == 2.");
205 if (!a.d_inObj_p || !a.d_outObj_p)
206 throw std::runtime_error("buildAnnulus2DInCurrentModel: null inner or outer geometry.");
207
208 if (a.d_inObj_p->d_name == "rectangle" && a.d_outObj_p->d_name == "rectangle") {
209 buildRectangleAnnulus2D(a, h);
210 return;
211 }
212 if (a.d_inObj_p->d_name == "circle" && a.d_outObj_p->d_name == "circle") {
213 buildCircleAnnulus2D(a, h);
214 return;
215 }
216 if (a.d_inObj_p->d_name == "ellipse" && a.d_outObj_p->d_name == "ellipse") {
217 buildEllipseAnnulus2D(a, h);
218 return;
219 }
220
221 throw std::runtime_error(
222 "buildAnnulus2DInCurrentModel: unsupported 2D pair (supported: rectangle−rectangle, "
223 "circle−circle, ellipse−ellipse).");
224}
225
226} // namespace mesh_gen
Defines annulus rectangle.
GeomObject * d_outObj_p
Outer object.
util::Point center() const override
Computes the center of object.
size_t d_dim
Dimension objects live in.
GeomObject * d_inObj_p
Inner object.
Defines circle.
double d_r
Radius.
util::Point d_x
Center.
Filled ellipse in the plane z = center.d_z, semi-axes in the xy plane.
double d_a
Semi-axis along local x before rotation (in-plane)
util::Point d_x
Center.
double d_b
Semi-axis along local y before rotation (in-plane)
double d_theta
Counter-clockwise rotation about +z through the center (radians)
const std::string d_name
name of object
Defines Rectangle.
std::vector< util::Point > d_vertices
Vertices.
void finish2DCutEmbedSurface(const std::vector< std::pair< int, int > > &ov, const geom::AnnulusGeomObject &a, double h)
void assertEllipseAnnulusValid(const geom::Ellipse &outer, const geom::Ellipse &inner)
void assertCircleAnnulusValid(const geom::Circle &outer, const geom::Circle &inner)
void buildCircleAnnulus2D(const geom::AnnulusGeomObject &a, double h)
util::Point embedPointInAnnulus2D(const geom::AnnulusGeomObject &a)
void buildEllipseAnnulus2D(const geom::AnnulusGeomObject &a, double h)
void assertRectangleAnnulusValid(const geom::Rectangle &outer, const geom::Rectangle &inner)
static int firstTagOfDim(const std::vector< std::pair< int, int > > &ov, int dim)
void buildRectangleAnnulus2D(const geom::AnnulusGeomObject &a, double h)
int addDiskOcc(double xc, double yc, double zc, double rx, double ry, double theta=0.)
OCC disk compatible with older Gmsh (no zAxis/xAxis) and newer APIs.
void buildAnnulus2DInCurrentModel(const geom::AnnulusGeomObject &a, double h)
A structure to represent 3d vectors.
Definition point.h:30
double d_y
the y coordinate
Definition point.h:36
double d_z
the z coordinate
Definition point.h:39
double d_x
the x coordinate
Definition point.h:33