ViennaLS
Loading...
Searching...
No Matches
lsMakeGeometry.hpp
Go to the documentation of this file.
1#pragma once
2
3#include <cassert>
4
6
7#include <hrleIndexType.hpp>
8#include <hrleVectorType.hpp>
9
10#include <lsConvexHull.hpp>
11#include <lsDomain.hpp>
12#include <lsFromSurfaceMesh.hpp>
13#include <lsGeometries.hpp>
14#include <lsMesh.hpp>
15#include <lsTransformMesh.hpp>
16
17#ifndef NDEBUG
18#include <lsVTKWriter.hpp>
19#endif
20
21namespace viennals {
22
23using namespace viennacore;
24
26template <class T, int D> class MakeGeometry {
27 typedef typename Domain<T, D>::PointValueVectorType pointDataType;
28
31 enum struct GeometryEnum : unsigned {
32 SPHERE = 0,
33 PLANE = 1,
34 BOX = 2,
35 CUSTOM = 3,
36 CYLINDER = 4
37 };
38
39 SmartPointer<Domain<T, D>> levelSet;
40 GeometryEnum geometry = GeometryEnum::SPHERE;
41 SmartPointer<Sphere<T, D>> sphere;
42 SmartPointer<Plane<T, D>> plane;
43 SmartPointer<Box<T, D>> box;
44 SmartPointer<Cylinder<T, D>> cylinder;
45 SmartPointer<PointCloud<T, D>> pointCloud;
46 const double numericEps = 1e-9;
47 // bool ignoreBoundaryConditions = false;
48 std::array<bool, 3> ignoreBoundaryConditions{false, false, false};
49
50public:
52
53 MakeGeometry(SmartPointer<Domain<T, D>> passedLevelSet)
54 : levelSet(passedLevelSet) {}
55
56 MakeGeometry(SmartPointer<Domain<T, D>> passedLevelSet,
57 SmartPointer<Sphere<T, D>> passedSphere)
58 : levelSet(passedLevelSet), sphere(passedSphere) {
59 geometry = GeometryEnum::SPHERE;
60 }
61
62 MakeGeometry(SmartPointer<Domain<T, D>> passedLevelSet,
63 SmartPointer<Plane<T, D>> passedPlane)
64 : levelSet(passedLevelSet), plane(passedPlane) {
65 geometry = GeometryEnum::PLANE;
66 }
67
68 MakeGeometry(SmartPointer<Domain<T, D>> passedLevelSet,
69 SmartPointer<Box<T, D>> passedBox)
70 : levelSet(passedLevelSet), box(passedBox) {
71 geometry = GeometryEnum::BOX;
72 }
73
74 MakeGeometry(SmartPointer<Domain<T, D>> passedLevelSet,
75 SmartPointer<Cylinder<T, D>> passedCylinder)
76 : levelSet(passedLevelSet), cylinder(passedCylinder) {
77 geometry = GeometryEnum::CYLINDER;
78 }
79
80 MakeGeometry(SmartPointer<Domain<T, D>> passedLevelSet,
81 SmartPointer<PointCloud<T, D>> passedPointCloud)
82 : levelSet(passedLevelSet), pointCloud(passedPointCloud) {
83 geometry = GeometryEnum::CUSTOM;
84 }
85
86 void setLevelSet(SmartPointer<Domain<T, D>> passedlsDomain) {
87 levelSet = passedlsDomain;
88 }
89
91 void setGeometry(SmartPointer<Sphere<T, D>> passedSphere) {
92 sphere = passedSphere;
93 geometry = GeometryEnum::SPHERE;
94 }
95
97 void setGeometry(SmartPointer<Plane<T, D>> passedPlane) {
98 plane = passedPlane;
99 geometry = GeometryEnum::PLANE;
100 }
101
103 void setGeometry(SmartPointer<Box<T, D>> passedBox) {
104 box = passedBox;
105 geometry = GeometryEnum::BOX;
106 }
107
109 void setGeometry(SmartPointer<Cylinder<T, D>> passedCylinder) {
110 cylinder = passedCylinder;
111 geometry = GeometryEnum::CYLINDER;
112 }
113
116 void setGeometry(SmartPointer<PointCloud<T, D>> passedPointCloud) {
117 pointCloud = passedPointCloud;
118 geometry = GeometryEnum::CUSTOM;
119 }
120
123 void setIgnoreBoundaryConditions(bool passedIgnoreBoundaryConditions) {
124 for (unsigned i = 0; i < D; ++i) {
125 ignoreBoundaryConditions[i] = passedIgnoreBoundaryConditions;
126 }
127 }
128
132 template <std::size_t N>
134 std::array<bool, N> passedIgnoreBoundaryConditions) {
135 for (unsigned i = 0; i < D && i < N; ++i) {
136 ignoreBoundaryConditions[i] = passedIgnoreBoundaryConditions[i];
137 }
138 }
139
140 void apply() {
141 if (levelSet == nullptr) {
142 Logger::getInstance()
143 .addWarning("No level set was passed to MakeGeometry.")
144 .print();
145 return;
146 }
147
148 switch (geometry) {
149 case GeometryEnum::SPHERE:
150 makeSphere(sphere->origin, sphere->radius);
151 break;
152 case GeometryEnum::PLANE:
153 makePlane(plane->origin, plane->normal);
154 break;
155 case GeometryEnum::BOX:
156 makeBox(box->minCorner, box->maxCorner);
157 break;
158 case GeometryEnum::CYLINDER:
159 makeCylinder(cylinder);
160 break;
161 case GeometryEnum::CUSTOM:
162 makeCustom(pointCloud);
163 break;
164 default:
165 Logger::getInstance()
166 .addWarning("Invalid geometry type was specified for MakeGeometry. "
167 "Not creating geometry.")
168 .print();
169 }
170 }
171
172private:
173 void makeSphere(hrleVectorType<T, D> origin, T radius) {
174 if (levelSet == nullptr) {
175 Logger::getInstance()
176 .addWarning("No level set was passed to MakeGeometry.")
177 .print();
178 return;
179 }
180
181 // TODO, this is a stupid algorithm and scales with volume, which is madness
182 auto &grid = levelSet->getGrid();
183 hrleCoordType gridDelta = grid.getGridDelta();
184
185 // calculate indices from sphere size
186 hrleVectorType<hrleIndexType, D> index;
187 hrleVectorType<hrleIndexType, D> endIndex;
188
189 for (unsigned i = 0; i < D; ++i) {
190 index[i] = (origin[i] - radius) / gridDelta - 1;
191 endIndex[i] = (origin[i] + radius) / gridDelta + 1;
192 }
193
194 const double initialWidth = 2.;
195 const T valueLimit = initialWidth * 0.5 * gridDelta + 1e-5;
196 const T radius2 = radius * radius;
197
198 pointDataType pointData;
199 const hrleVectorType<hrleIndexType, D> minIndex = index;
200
201 while (index < endIndex) {
202 // take shortest manhatten distance to gridline intersection
203 T distance = std::numeric_limits<T>::max();
204 for (unsigned i = 0; i < D; ++i) {
205 T y = (index[(i + 1) % D] * gridDelta) - origin[(i + 1) % D];
206 T z = 0;
207 if constexpr (D == 3)
208 z = (index[(i + 2) % D] * gridDelta) - origin[(i + 2) % D];
209 T x = radius2 - y * y - z * z;
210 if (x < 0.)
211 continue;
212 T dirRadius =
213 std::abs((index[i] * gridDelta) - origin[i]) - std::sqrt(x);
214 if (std::abs(dirRadius) < std::abs(distance))
215 distance = dirRadius;
216 }
217
218 if (std::abs(distance) <= valueLimit) {
219 pointData.push_back(std::make_pair(index, distance / gridDelta));
220 }
221 int dim = 0;
222 for (; dim < D - 1; ++dim) {
223 if (index[dim] < endIndex[dim])
224 break;
225 index[dim] = minIndex[dim];
226 }
227 ++index[dim];
228 }
229
230 // Mirror indices correctly into domain, unless boundary conditions
231 // are ignored
232 for (unsigned i = 0; i < pointData.size(); ++i) {
233 for (unsigned j = 0; j < D; ++j) {
234 if (!ignoreBoundaryConditions[j] && grid.isBoundaryPeriodic(j)) {
235 pointData[i].first[j] =
236 grid.globalIndex2LocalIndex(j, pointData[i].first[j]);
237 }
238 }
239 }
240
241 levelSet->insertPoints(pointData);
242 levelSet->getDomain().segment();
243 levelSet->finalize(initialWidth);
244 }
245
248 void makePlane(hrleVectorType<T, D> origin,
249 hrleVectorType<T, D> passedNormal) {
250 if (levelSet == nullptr) {
251 Logger::getInstance()
252 .addWarning("No level set was passed to MakeGeometry.")
253 .print();
254 return;
255 }
256
257 auto &grid = levelSet->getGrid();
258 hrleCoordType gridDelta = grid.getGridDelta();
259
260 // normalise passedNormal
261 double modulus = 0.;
262 hrleVectorType<double, D> normal(passedNormal);
263 for (unsigned i = 0; i < D; ++i) {
264 modulus += normal[i] * normal[i];
265 }
266 modulus = std::sqrt(modulus);
267 for (unsigned i = 0; i < D; ++i) {
268 normal[i] /= modulus;
269 }
270
271 // check that boundary conditions are correct
272 unsigned i = 0;
273 bool infDimSet = false;
274 for (unsigned n = 0; n < D; ++n) {
275 if (grid.getBoundaryConditions(n) ==
276 hrleGrid<D>::boundaryType::INFINITE_BOUNDARY) {
277 if (!infDimSet) {
278 i = n;
279 infDimSet = true;
280 } else {
281 Logger::getInstance().addError(
282 "Planes can only be created with one Infinite Boundary "
283 "Condition. More than one found!");
284 }
285 }
286 }
287 if (!infDimSet) {
288 Logger::getInstance().addError("Planes require exactly one Infinite "
289 "Boundary Condition. None found!");
290 }
291
292 if (passedNormal[i] == 0.) {
293 Logger::getInstance().addError(
294 "MakeGeometry: Plane cannot be parallel to Infinite Boundary "
295 "direction!");
296 }
297
298 // find minimum and maximum points in infinite direction
299 // there are 2*(D-1) points in the corners of the simulation domain
300 std::vector<std::array<T, 3>> cornerPoints;
301 cornerPoints.resize(2 * (D - 1));
302
303 // cyclic permutations
304 unsigned j = (i + 1) % D;
305 unsigned k = (i + 2) % D;
306
307 double minCoord[2];
308 double maxCoord[2];
309 // Find grid boundaries, there used to be a +-1 for the coords.
310 // If an error pops up here, probably has to do with that.
311 // But if +-1 is added here, the boundaries are exceeded and
312 // the correct boundary conditions will add stray points for
313 // tilted planes in lsFromSurfaceMesh later on.
314 for (unsigned n = 0; n < D - 1; ++n) {
315 minCoord[n] = gridDelta * (grid.getMinIndex((i + n + 1) % D) - 1);
316 maxCoord[n] = gridDelta * (grid.getMaxIndex((i + n + 1) % D) + 1);
317 }
318
319 // set corner points
320 cornerPoints[0][j] = minCoord[0];
321 cornerPoints[1][j] = maxCoord[0];
322
323 if constexpr (D == 3) {
324 cornerPoints[0][k] = minCoord[1];
325 cornerPoints[1][k] = maxCoord[1];
326
327 cornerPoints[2][j] = minCoord[0];
328 cornerPoints[2][k] = maxCoord[1];
329 cornerPoints[3][j] = maxCoord[0];
330 cornerPoints[3][k] = minCoord[1];
331 }
332
333 // now find i coordinate of points
334 auto mesh = SmartPointer<Mesh<T>>::New();
335
336 for (unsigned n = 0; n < cornerPoints.size(); ++n) {
337 double numerator = (cornerPoints[n][j] - origin[j]) * normal[j];
338 if constexpr (D == 3)
339 numerator += (cornerPoints[n][k] - origin[k]) * normal[k];
340 else
341 cornerPoints[n][2] = 0.;
342 cornerPoints[n][i] = origin[i] - numerator / normal[i];
343 mesh->insertNextNode(cornerPoints[n]);
344 }
345
346 if (D == 2) {
347 std::array<unsigned, 2> line = {0, 1};
348 if (normal[i] < 0.)
349 std::swap(line[0], line[1]);
350 mesh->insertNextLine(line);
351 } else {
352 std::array<unsigned, 3> triangle = {0, 1, 2};
353 if (normal[i] < 0.)
354 std::swap(triangle[0], triangle[1]);
355 mesh->insertNextTriangle(triangle);
356 triangle = {0, 3, 1};
357 if (normal[i] < 0.)
358 std::swap(triangle[0], triangle[1]);
359 mesh->insertNextTriangle(triangle);
360 }
361
362#ifndef NDEBUG
363 static unsigned planeCounter = 0;
364 VTKWriter<T>(mesh, "plane" + std::to_string(planeCounter++) + ".vtk")
365 .apply();
366#endif
367
368 // now convert mesh to levelset
369 FromSurfaceMesh<T, D>(levelSet, mesh).apply();
370 }
371
372 // This function creates a box starting in minCorner spanning to maxCorner
373 void makeBox(hrleVectorType<T, D> minCorner, hrleVectorType<T, D> maxCorner) {
374 if (levelSet == nullptr) {
375 Logger::getInstance()
376 .addWarning("No level set was passed to MakeGeometry.")
377 .print();
378 return;
379 }
380
381 // draw all triangles for the surface and then import from the mesh
382 std::vector<std::array<T, 3>> corners;
383 corners.resize(std::pow(2, D), {0, 0, 0});
384
385 // first corner is the minCorner
386 for (unsigned i = 0; i < D; ++i)
387 corners[0][i] = minCorner[i];
388
389 // last corner is maxCorner
390 for (unsigned i = 0; i < D; ++i)
391 corners.back()[i] = maxCorner[i];
392
393 // calculate all missing corners
394 corners[1] = corners[0];
395 corners[1][0] = corners.back()[0];
396
397 corners[2] = corners[0];
398 corners[2][1] = corners.back()[1];
399
400 if constexpr (D == 3) {
401 corners[3] = corners.back();
402 corners[3][2] = corners[0][2];
403
404 corners[4] = corners[0];
405 corners[4][2] = corners.back()[2];
406
407 corners[5] = corners.back();
408 corners[5][1] = corners[0][1];
409
410 corners[6] = corners.back();
411 corners[6][0] = corners[0][0];
412 }
413
414 // now add all corners to mesh
415 auto mesh = SmartPointer<Mesh<T>>::New();
416 for (unsigned i = 0; i < corners.size(); ++i) {
417 mesh->insertNextNode(corners[i]);
418 }
419
420 if (D == 2) {
421 std::array<unsigned, 2> lines[4] = {{0, 2}, {2, 3}, {3, 1}, {1, 0}};
422 for (unsigned i = 0; i < 4; ++i)
423 mesh->insertNextLine(lines[i]);
424 } else {
425 std::array<unsigned, 3> triangles[12] = {
426 {0, 3, 1}, {0, 2, 3}, {0, 1, 5}, {0, 5, 4}, {0, 4, 2}, {4, 6, 2},
427 {7, 6, 4}, {7, 4, 5}, {7, 2, 6}, {7, 3, 2}, {1, 3, 5}, {3, 7, 5}};
428 for (unsigned i = 0; i < 12; ++i)
429 mesh->insertNextTriangle(triangles[i]);
430 }
431
432 // now convert mesh to levelset
433 FromSurfaceMesh<T, D> mesher(levelSet, mesh);
434 mesher.setRemoveBoundaryTriangles(ignoreBoundaryConditions);
435 mesher.apply();
436 }
437
438 void makeCylinder(SmartPointer<Cylinder<T, D>> cylinder) {
439 if (D != 3) {
440 Logger::getInstance()
441 .addWarning("MakeGeometry: Cylinder can only be created in 3D!")
442 .print();
443 return;
444 }
445 // generate the points on the edges of the cylinders and mesh
446 // them manually
447 // cylinder axis will be (0,0,1)
448 auto gridDelta = levelSet->getGrid().getGridDelta();
449
450 auto points = SmartPointer<PointCloud<T, D>>::New();
451 unsigned numPoints = std::ceil(2 * M_PI * cylinder->radius / gridDelta);
452 double smallAngle = 2.0 * M_PI / double(numPoints);
453
454 auto mesh = SmartPointer<Mesh<T>>::New();
455 // insert midpoint at base
456 mesh->insertNextNode(std::array<T, 3>{0.0, 0.0, 0.0});
457 {
458 constexpr double limit = 2 * M_PI - 1e-6;
459 std::vector<std::array<T, 3>> points;
460 if (cylinder->topRadius)
461 std::vector<std::array<T, 3>> pointsTop;
462
463 // create and insert points at base
464 for (double angle = 0.; angle < limit; angle += smallAngle) {
465 std::array<T, 3> point;
466 point[0] = cylinder->radius * std::cos(angle);
467 point[1] = cylinder->radius * std::sin(angle);
468 point[2] = 0.0;
469 points.push_back(point);
470 mesh->insertNextNode(point);
471 }
472
473 // insert midpoint at top
474 mesh->insertNextNode(std::array<T, 3>{0.0, 0.0, cylinder->height});
475
476 double angle = 0;
477 for (unsigned i = 0; i < numPoints; ++i) {
478 // create triangles at base
479 std::array<unsigned, 3> triangle;
480 triangle[0] = (i + 1) % numPoints + 1;
481 triangle[1] = i + 1;
482 triangle[2] = 0;
483 mesh->insertNextTriangle(triangle);
484
485 // insert points at top
486 // If topRadius is specified, update the first two coordinates of the
487 // points
488 if (cylinder->topRadius) {
489 points[i][0] = cylinder->topRadius * std::cos(angle);
490 points[i][1] = cylinder->topRadius * std::sin(angle);
491 angle += smallAngle;
492 }
493 points[i][2] = cylinder->height;
494 mesh->insertNextNode(points[i]);
495
496 // insert triangles at top
497 triangle[0] = numPoints + 1;
498 triangle[1] = numPoints + i + 2;
499 triangle[2] = (i + 1) % numPoints + 2 + numPoints;
500 mesh->insertNextTriangle(triangle);
501 }
502
503 // insert sidewall triangles
504 for (unsigned i = 0; i < numPoints; ++i) {
505 std::array<unsigned, 3> triangle;
506 triangle[0] = i + 1;
507 triangle[1] = (i + 1) % numPoints + 1;
508 triangle[2] = i + numPoints + 2;
509 mesh->insertNextTriangle(triangle);
510
511 triangle[0] = (i + 1) % numPoints + 1;
512 triangle[1] = (i + 1) % numPoints + 2 + numPoints;
513 triangle[2] = i + numPoints + 2;
514 mesh->insertNextTriangle(triangle);
515 }
516 }
517
518 // rotate mesh
519 // normalise axis vector
520 T unit =
521 std::sqrt(DotProduct(cylinder->axisDirection, cylinder->axisDirection));
522 hrleVectorType<double, 3> cylinderAxis;
523 for (unsigned i = 0; i < 3; ++i) {
524 cylinderAxis[i] = cylinder->axisDirection[i] / unit;
525 }
526 // get rotation axis via cross product of (0,0,1) and axis of cylinder
527 hrleVectorType<double, 3> rotAxis(-cylinderAxis[1], cylinderAxis[0], 0.0);
528 // angle is acos of dot product
529 T rotationAngle = std::acos(cylinderAxis[2]);
530
531 // rotate mesh
532 TransformMesh<T>(mesh, TransformEnum::ROTATION, rotAxis, rotationAngle)
533 .apply();
534
535 // translate mesh
536 hrleVectorType<double, 3> translationVector;
537 for (unsigned i = 0; i < 3; ++i) {
538 translationVector[i] = cylinder->origin[i];
539 }
540 TransformMesh<T>(mesh, TransformEnum::TRANSLATION, translationVector)
541 .apply();
542
543 // read mesh from surface
544 FromSurfaceMesh<T, D> mesher(levelSet, mesh);
545 mesher.setRemoveBoundaryTriangles(ignoreBoundaryConditions);
546 mesher.apply();
547 }
548
549 void makeCustom(SmartPointer<PointCloud<T, D>> pointCloud) {
550 // create mesh from point cloud
551 auto mesh = SmartPointer<Mesh<T>>::New();
552 ConvexHull<T, D>(mesh, pointCloud).apply();
553
554 // read mesh from surface
555 FromSurfaceMesh<T, D> mesher(levelSet, mesh);
556 mesher.setRemoveBoundaryTriangles(ignoreBoundaryConditions);
557 mesher.apply();
558 }
559};
560
561// add all template specialisations for this class
563
564} // namespace viennals
Class describing a square box from one coordinate to another.
Definition lsGeometries.hpp:56
Class describing a square box from one coordinate to another.
Definition lsGeometries.hpp:80
Class containing all information about the level set, including the dimensions of the domain,...
Definition lsDomain.hpp:28
std::vector< std::pair< hrleVectorType< hrleIndexType, D >, T > > PointValueVectorType
Definition lsDomain.hpp:36
Create level sets describing basic geometric forms.
Definition lsMakeGeometry.hpp:26
MakeGeometry(SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< Cylinder< T, D > > passedCylinder)
Definition lsMakeGeometry.hpp:74
void setIgnoreBoundaryConditions(bool passedIgnoreBoundaryConditions)
Ignore boundary conditions, meaning the parts of the generated geometry which are outside of the doma...
Definition lsMakeGeometry.hpp:123
void apply()
Definition lsMakeGeometry.hpp:140
void setLevelSet(SmartPointer< Domain< T, D > > passedlsDomain)
Definition lsMakeGeometry.hpp:86
void setGeometry(SmartPointer< Cylinder< T, D > > passedCylinder)
Set a cylinder to be created in the level set.
Definition lsMakeGeometry.hpp:109
void setGeometry(SmartPointer< PointCloud< T, D > > passedPointCloud)
Set a point cloud, which is used to create a geometry from its convex hull.
Definition lsMakeGeometry.hpp:116
void setIgnoreBoundaryConditions(std::array< bool, N > passedIgnoreBoundaryConditions)
Ignore boundary conditions, meaning the parts of the generated geometry which are outside of the doma...
Definition lsMakeGeometry.hpp:133
MakeGeometry(SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< Sphere< T, D > > passedSphere)
Definition lsMakeGeometry.hpp:56
MakeGeometry(SmartPointer< Domain< T, D > > passedLevelSet)
Definition lsMakeGeometry.hpp:53
MakeGeometry(SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< Plane< T, D > > passedPlane)
Definition lsMakeGeometry.hpp:62
MakeGeometry()
Definition lsMakeGeometry.hpp:51
MakeGeometry(SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< Box< T, D > > passedBox)
Definition lsMakeGeometry.hpp:68
void setGeometry(SmartPointer< Box< T, D > > passedBox)
Set a box to be created in the level set.
Definition lsMakeGeometry.hpp:103
void setGeometry(SmartPointer< Plane< T, D > > passedPlane)
Set a plane to be created in the level set.
Definition lsMakeGeometry.hpp:97
void setGeometry(SmartPointer< Sphere< T, D > > passedSphere)
Set sphere as geometry to be created in the level set.
Definition lsMakeGeometry.hpp:91
MakeGeometry(SmartPointer< Domain< T, D > > passedLevelSet, SmartPointer< PointCloud< T, D > > passedPointCloud)
Definition lsMakeGeometry.hpp:80
Class describing a plane via a point in it and the plane normal.
Definition lsGeometries.hpp:34
Class describing a point cloud, which can be used to create geometries from its convex hull mesh.
Definition lsGeometries.hpp:121
Class describing a sphere via origin and radius.
Definition lsGeometries.hpp:13
#define PRECOMPILE_PRECISION_DIMENSION(className)
Definition lsPreCompileMacros.hpp:24
float gridDelta
Definition AirGapDeposition.py:21
tuple maxCorner
Definition AirGapDeposition.py:44
mesh
Definition AirGapDeposition.py:36
tuple origin
Definition AirGapDeposition.py:30
Definition lsAdvect.hpp:46
constexpr int D
Definition pyWrap.cpp:65
double T
Definition pyWrap.cpp:63