Alamo
PointList.H
Go to the documentation of this file.
1//
2// Fill a domain based on a list of points to form one or more closed 2D shapes.
3// The distance from each point to the nearest edge is found and an error function is then used to compute
4// the value of phi between 0 (outside) and 1 (inside) the domain. Only works in 2 dimensions.
5// Points belonging to different polygons are distinguished using the ObjNum column of the input file:
6// whenever ObjNum changes by more than obj_num_threshold between consecutive lines, a new polygon begins.
7// Each polygon's field is computed into phi_temp. Polygons are then combined into either the "solid" field
8// (pointwise max over all non-void polygons) or the "void" field (pointwise max over all void polygons,
9// as selected by the per-polygon invert list). The void field is inverted (zero inside, one outside the
10// void polygons) and multiplied into the solid field, punching holes wherever a void polygon overlaps it.
11// invert_all is then applied once at the end to flip the whole finished field if desired.
12
13#ifndef IC_POINTLIST_H_
14#define IC_POINTLIST_H_
15
16#include "Set/Set.H"
17#include "IC/IC.H"
18#include "Util/Util.H"
19using namespace std;
20#include <iostream>
21#include <vector>
22#include <algorithm>
23#include <iterator>
24#include <mpi.h>
25#include "IO/ParmParse.H"
26
27namespace IC
28{
29class PointList : public IC<Set::Scalar>
30{
31public:
32 static constexpr const char* name = "pointlist";
33
34 enum Type
35 {
37 Values
38 };
39
40 PointList(amrex::Vector<amrex::Geometry>& _geom) : IC(_geom) {}
41
42 PointList(amrex::Vector<amrex::Geometry>& _geom, IO::ParmParse& pp, std::string name) : IC(_geom)
43 {
44 pp.queryclass(name, *this);
45 }
46 void Define() {
47
48 };
49
50 void Add(const int& lev, Set::Field<Set::Scalar>& a_phi, Set::Scalar)
51 {
52 if (AMREX_SPACEDIM != 2)
53 {
54 amrex::Abort("This code only supports 2D (AMREX_SPACEDIM must be 2)");
55 }
56
57 if ((int)phi_temp.size() != a_phi.size()) phi_temp.resize(a_phi.size());
58 if ((int)phi_void.size() != a_phi.size()) phi_void.resize(a_phi.size());
59 if (!phi_temp[lev] ||
60 phi_temp[lev]->boxArray() != a_phi[lev]->boxArray() ||
61 phi_temp[lev]->DistributionMap() != a_phi[lev]->DistributionMap())
62 {
63 phi_temp[lev] = std::make_unique<amrex::MultiFab>(a_phi[lev]->boxArray(), a_phi[lev]->DistributionMap(),
64 a_phi[lev]->nComp(), a_phi[lev]->nGrow());
65 }
66 if (!phi_void[lev] ||
67 phi_void[lev]->boxArray() != a_phi[lev]->boxArray() ||
68 phi_void[lev]->DistributionMap() != a_phi[lev]->DistributionMap())
69 {
70 phi_void[lev] = std::make_unique<amrex::MultiFab>(a_phi[lev]->boxArray(), a_phi[lev]->DistributionMap(),
71 a_phi[lev]->nComp(), a_phi[lev]->nGrow());
72 }
73
74 bool first_solid = true;
75 bool first_void = true;
76 bool any_void = false;
77
78 for (unsigned int p = 0; p < polygons.size(); p++)
79 {
80 std::vector<Set::Vector> polygon = polygons[p];
81 if (polygon.back() != polygon.front())
82 // The first and last point must match, if they do not add another point to the end
83 {
84 polygon.push_back(polygon.front());
85 }
86 bool is_void = (p < invert.size() && invert[p] != 0);
87 bool is_first = is_void ? first_void : first_solid;
88
89 for (amrex::MFIter mfi(*a_phi[lev], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
90 {
91 amrex::Box bx;
92 amrex::IndexType type = a_phi[lev]->ixType();
93 if (type == amrex::IndexType::TheCellType()) bx = mfi.growntilebox();
94 else if (type == amrex::IndexType::TheNodeType()) bx = mfi.grownnodaltilebox();
95 else Util::Abort(INFO, "Unkonwn index type");
96 amrex::Array4<Set::Scalar> const& phi = a_phi[lev]->array(mfi);
97 amrex::Array4<Set::Scalar> const& phi_t = phi_temp[lev]->array(mfi);
98 amrex::Array4<Set::Scalar> const& phi_v = phi_void[lev]->array(mfi);
99
100 std::vector<Set::Vector> X = polygon;
101
102 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
103 {
104 Set::Vector x = Set::Position(i, j, k, geom[lev], type);
105 Set::Scalar x_cross;
106 Set::Scalar min_dist_to_edge = std::numeric_limits<Set::Scalar>::max(); // Mininmum distance from point to nearest edge
107 Set::Scalar dist_to_edge; // Distance from point to nearest edge
108 Set::Scalar x1;
109 Set::Scalar y1;
110 Set::Scalar x2;
111 Set::Scalar y2;
112 int num_cross = 0;
113
114 Set::Scalar px;
115 Set::Scalar py;
116 Set::Scalar dx;
117 Set::Scalar dy;
118 Set::Scalar len2;
120 Set::Vector x_int; // x and y position of the intercept of the mesh point and the edge, assuming edge is infinite
121
122 for (unsigned int n = 0; n < X.size()-1; n++)
123 {
124
125 x1 = X[n](0);
126 y1 = X[n](1);
127 x2 = X[n+1](0);
128 y2 = X[n+1](1);
129
130 if ((y1 > x(1)) != (y2 > x(1)))
131 {
132 x_cross = x1 + (x(1) - y1) * (x2 - x1) / (y2 - y1);
133
134 if (x_cross > x(0))
135 num_cross++;
136 }
137
138 // Computes the closest point on a line segment (X[n] → X[n+1]) to a query point x,
139 // using vector projection onto the segment.
140 //
141 // Let A = X[n], B = X[n+1], and P = x.
142 //
143 // The parameter t is the normalized projection of P onto the infinite line AB:
144 //
145 // t = ((P - A) · (B - A)) / |B - A|^2
146 //
147 // If t ∈ [0, 1], the perpendicular projection lies within the segment.
148 // If t < 0, the closest point is A.
149 // If t > 1, the closest point is B.
150 //
151 // The resulting closest point x_int is then used to compute the Euclidean
152 // distance from P to the segment, and the minimum distance over all edges is tracked.
153 px = x(0);
154 py = x(1);
155 dx = x2 - x1;
156 dy = y2 - y1;
157 len2 = dx*dx + dy*dy;
158 t = ((px - x1)*dx + (py - y1)*dy) / len2;
159
160 if (t < 0.0)
161 {
162 x_int(0) = x1;
163 x_int(1) = y1;
164 }
165 else if (t > 1.0)
166 {
167 x_int(0) = x2;
168 x_int(1) = y2;
169 }
170 else
171 {
172 x_int(0) = x1 + t*dx;
173 x_int(1) = y1 + t*dy;
174 }
175
176 dist_to_edge = dist(x(0), x(1), x_int(0), x_int(1));
177 min_dist_to_edge = std::min(min_dist_to_edge, dist_to_edge);
178
179 }
180 if (num_cross % 2 == 0)
181 {
182 min_dist_to_edge = 1*min_dist_to_edge; // If the number of crossings is even, the point is inside the shape
183 } else
184 {
185 min_dist_to_edge = -1*min_dist_to_edge;
186 }
187
188 phi_t(i,j,k) = 0.5 * (1.0 - std::tanh(min_dist_to_edge /(std::sqrt(2.0) * eps)));
189
190 // Combine this polygon's field into either the running solid union or the running
191 // void union (pointwise max), depending on whether it is marked as a void polygon.
192 if (is_void) phi_v(i,j,k) = is_first ? phi_t(i,j,k) : std::max(phi_v(i,j,k), phi_t(i,j,k));
193 else phi(i,j,k) = is_first ? phi_t(i,j,k) : std::max(phi(i,j,k), phi_t(i,j,k));
194 });
195 }
196
197 if (is_void) { first_void = false; any_void = true; }
198 else first_solid = false;
199 }
200
201 if (any_void)
202 // Punch holes into the solid field wherever a void polygon overlaps it: phi_void is zero inside
203 // the void polygons and one outside them, so multiplying it in zeros out the void regions.
204 {
205 for (amrex::MFIter mfi(*a_phi[lev], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
206 {
207 amrex::Box bx;
208 amrex::IndexType type = a_phi[lev]->ixType();
209 if (type == amrex::IndexType::TheCellType()) bx = mfi.growntilebox();
210 else if (type == amrex::IndexType::TheNodeType()) bx = mfi.grownnodaltilebox();
211 else Util::Abort(INFO, "Unkonwn index type");
212 amrex::Array4<Set::Scalar> const& phi = a_phi[lev]->array(mfi);
213 amrex::Array4<Set::Scalar> const& phi_v = phi_void[lev]->array(mfi);
214
215 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
216 {
217 phi(i,j,k) = phi(i,j,k) * (1.0 - phi_v(i,j,k));
218 });
219 }
220 }
221
222 if (invert_all)
223 {
224 for (amrex::MFIter mfi(*a_phi[lev], amrex::TilingIfNotGPU()); mfi.isValid(); ++mfi)
225 {
226 amrex::Box bx;
227 amrex::IndexType type = a_phi[lev]->ixType();
228 if (type == amrex::IndexType::TheCellType()) bx = mfi.growntilebox();
229 else if (type == amrex::IndexType::TheNodeType()) bx = mfi.grownnodaltilebox();
230 else Util::Abort(INFO, "Unkonwn index type");
231 amrex::Array4<Set::Scalar> const& phi = a_phi[lev]->array(mfi);
232
233 amrex::ParallelFor(bx, [=] AMREX_GPU_DEVICE(int i, int j, int k)
234 {
235 phi(i,j,k) = 1 - phi(i,j,k);
236 });
237 }
238 }
239 }
240
241 static void Parse(PointList& value, IO::ParmParse& pp)
242 {
243 std::string filename;
244 int verbose = 0;
245 // Diffuseness of the solid boundary
246 pp.query_default("eps", value.eps, "0.0", Unit::Length());
247
248 pp.forbid("filename", "use file.name instead");
249 // Location of .xy file
250 pp.query_file("file.name", filename);
251
252 // Verbosity (used in parser only)
253 pp.query_default("verbose", verbose, 0);
254
255 // Unitless coordinate multiplier, applied after the rotation
256 pp.query_default("mult",value.mult, "1.0", Unit::Less());
257
258 // Whether to invert the finished field as a whole (make IC 1 outside of solid instead of inside solid)
259 pp.query_default("invert_all",value.invert_all,false);
260
261 // X-offsed, applied after the rotation
262 pp.queryarr_default("x0",value.x0,"0.0 0.0 0.0", Unit::Length());
263
264 // Center of rigid-body rotation
265 pp.queryarr_default("rotation.center",value.rotation.center,"0.0 0.0 0.0", Unit::Length());
266
267 // Amount of rotation about rotation center (clockwise)
268 pp.query_default("rotation.angle",value.rotation.angle,"0.0", Unit::Angle());
269
270 // ObjNum jump (between consecutive points in the file) larger than this starts a new polygon
271 pp.query_default("obj_num_threshold",value.objnum_threshold,"0.5", Unit::Less());
272
273
274 Unit unit = Unit::Length();
275 // Units of length in the file
276 pp.queryunit("file.unit",unit);
278 "Unit must be of type length but got unit ",unit.normalized_unitstring());
279
280 std::ifstream datafile(filename);
281 std::string line;
282 if (datafile.is_open())
283 {
284 value.polygons.clear();
285 bool have_prev_obj_num = false;
286 Set::Scalar prev_obj_num = 0.0;
287
288 while (getline(datafile, line))
289 {
290 std::istringstream in(line);
291
292 std::string strx, stry, strz, strObjNum;
293 in >> strx >> stry >> strz;
294
295 Set::Scalar x = (std::stod(strx) * unit).normalized_value();
296 Set::Scalar y = (std::stod(stry) * unit).normalized_value();
297 #if AMREX_SPACEDIM > 2
298 Set::Scalar z = (std::stod(strz) * unit).normalized_value();
299 #endif
300
301 // ObjNum is optional; if the file has no fourth column, treat every point as one polygon
302 Set::Scalar ObjNum = have_prev_obj_num ? prev_obj_num : 0.0;
303 if (in >> strObjNum) ObjNum = std::stod(strObjNum);
304
305 Set::Vector X(AMREX_D_DECL(x,y,z));
306
307 Set::Scalar cx = value.rotation.center[0];
308 Set::Scalar cy = value.rotation.center[1];
309
310 Set::Scalar theta = value.rotation.angle;
311
312 Set::Scalar cos_t = std::cos(theta);
313 Set::Scalar sin_t = std::sin(theta);
314
315 Set::Scalar x_shifted = x - cx;
316 Set::Scalar y_shifted = y - cy;
317
318 X[0] = cx + x_shifted * cos_t - y_shifted * sin_t;
319 X[1] = cy + x_shifted * sin_t + y_shifted * cos_t;
320
321 X = value.x0 + value.mult*X;
322
323 // A jump in ObjNum larger than the threshold marks the start of a new polygon
324 if (!have_prev_obj_num || std::abs(ObjNum - prev_obj_num) > value.objnum_threshold)
325 {
326 value.polygons.push_back(std::vector<Set::Vector>());
327 }
328 have_prev_obj_num = true;
329 prev_obj_num = ObjNum;
330
331 value.polygons.back().push_back(X);
332 if (verbose > 0)
333 Util::Message(INFO, "x=", value.polygons.back().back().transpose());
334 }
335 datafile.close();
336 }
337 else
338 {
339 Util::Abort(INFO, "Unable to open file ", filename);
340 }
341
342 // Per-polygon void flag: nonzero marks a polygon as a void/negative region that is cut out of
343 // the solid field instead of being unioned into it. Must have one entry per polygon if given.
344 if (pp.queryarr("invert", value.invert))
345 {
346 Util::AssertException(INFO, TEST(value.invert.size() == value.polygons.size()),
347 "'invert' must have one entry per polygon: expected ", value.polygons.size(),
348 " but got ", value.invert.size());
349 }
350 else
351 {
352 value.invert.assign(value.polygons.size(), 0);
353 }
354 }
355
356private:
357 std::vector<std::vector<Set::Vector>> polygons; // Each entry is the ordered list of vertices for one closed polygon
360 Set::Vector x0 = Set::Vector::Zero();
364 std::vector<int> invert; // per-polygon void flag: nonzero = void/negative region
365 bool invert_all = false;
366
367 struct{
371
372 double dist(double x1, double y1, double x2, double y2)
373 {
374 // Find distance between 2 points
375 double dx = x2 - x1;
376 double dy = y2 - y1;
377
378 return std::sqrt(dx*dx + dy*dy);
379 }
380};
381}
382#endif
#define X(name)
std::time_t t
#define TEST(x)
Definition Util.H:25
#define INFO
Definition Util.H:24
amrex::Vector< amrex::Geometry > & geom
Definition IC.H:61
Set::Vector x0
Definition PointList.H:360
Set::Field< Set::Scalar > phi_temp
Definition PointList.H:361
Set::Scalar mult
Definition PointList.H:359
std::vector< int > invert
Definition PointList.H:364
Set::Field< Set::Scalar > phi_void
Definition PointList.H:362
double dist(double x1, double y1, double x2, double y2)
Definition PointList.H:372
Set::Scalar angle
Definition PointList.H:369
Set::Scalar eps
Definition PointList.H:358
PointList(amrex::Vector< amrex::Geometry > &_geom, IO::ParmParse &pp, std::string name)
Definition PointList.H:42
void Define()
Definition PointList.H:46
Set::Scalar objnum_threshold
Definition PointList.H:363
void Add(const int &lev, Set::Field< Set::Scalar > &a_phi, Set::Scalar)
Definition PointList.H:50
static void Parse(PointList &value, IO::ParmParse &pp)
Definition PointList.H:241
Set::Vector center
Definition PointList.H:368
PointList(amrex::Vector< amrex::Geometry > &_geom)
Definition PointList.H:40
std::vector< std::vector< Set::Vector > > polygons
Definition PointList.H:357
static constexpr const char * name
Definition PointList.H:32
struct IC::PointList::@0 rotation
int queryunit(std::string name, Unit &value)
Definition ParmParse.H:192
int queryarr_default(std::string name, std::vector< std::string > &value, std::vector< std::string > defaultvalue)
Definition ParmParse.H:660
int queryarr(std::string name, std::vector< T > &value)
Definition ParmParse.H:535
void forbid(std::string name, std::string explanation)
Definition ParmParse.H:160
int query_file(std::string name, std::string &value, bool copyfile, bool checkfile)
Definition ParmParse.H:486
void queryclass(std::string name, T *value)
Definition ParmParse.H:968
int query_default(std::string name, T &value, T defaultvalue)
Definition ParmParse.H:293
Initialize a spherical inclusion.
Definition BMP.H:20
amrex::Real Scalar
Definition Base.H:19
Eigen::Matrix< amrex::Real, AMREX_SPACEDIM, 1 > Vector
Definition Base.H:21
AMREX_FORCE_INLINE Vector Position(const int &i, const int &j, const int &k, const amrex::Geometry &geom, const amrex::IndexType &ixType)
Definition Base.H:122
AMREX_FORCE_INLINE void AssertException(std::string file, std::string func, int line, std::string smt, bool pass, Args const &... args)
Definition Util.H:254
void Abort(const char *msg)
Definition Util.cpp:268
void Message(std::string file, std::string func, int line, Args const &... args)
Definition Util.H:129
Definition Unit.H:21
bool isType(const Unit &test) const
Definition Unit.H:425
std::string normalized_unitstring() const
Definition Unit.H:515
static Unit Angle()
Definition Unit.H:206
static Unit Length()
Definition Unit.H:198
static Unit Less()
Definition Unit.H:197