Nektar++
ProcessPhiFromFile.cpp
Go to the documentation of this file.
1///////////////////////////////////////////////////////////////////////////////
2//
3// File: ProcessPhiFromFile.cpp
4//
5// For more information, please see: http://www.nektar.info/
6//
7// The MIT License
8//
9// Copyright (c) 2006 Division of Applied Mathematics, Brown University (USA),
10// Department of Aeronautics, Imperial College London (UK), and Scientific
11// Computing and Imaging Institute, University of Utah (USA).
12//
13// Permission is hereby granted, free of charge, to any person obtaining a
14// copy of this software and associated documentation files (the "Software"),
15// to deal in the Software without restriction, including without limitation
16// the rights to use, copy, modify, merge, publish, distribute, sublicense,
17// and/or sell copies of the Software, and to permit persons to whom the
18// Software is furnished to do so, subject to the following conditions:
19//
20// The above copyright notice and this permission notice shall be included
21// in all copies or substantial portions of the Software.
22//
23// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
24// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
25// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
26// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
27// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
28// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
29// DEALINGS IN THE SOFTWARE.
30//
31// Description: Reads an STL file.
32//
33///////////////////////////////////////////////////////////////////////////////
34
35#include "ProcessPhiFromFile.h"
38
39using namespace Nektar;
40using namespace std;
41
42namespace Nektar::FieldUtils
43{
44
48 "Computes the Phi function from a file, used in IB methods.")};
49
50/**
51 * @brief Set up ProcessPhiFromFile object.
52 *
53 */
55{
56 m_config["scale"] =
57 ConfigOption(false, "NotSet", "Scale coefficient for Phi.");
58 m_config["file"] =
59 ConfigOption(false, "NotSet", "File with the IB definition.");
60}
61
62/**
63 *
64 */
66{
67}
68
69/**
70 *
71 */
72void ProcessPhiFromFile::v_Process([[maybe_unused]] po::variables_map &vm)
73{
74 // Do not run in parallel
75 ASSERTL0(m_f->m_session->GetComm()->GetSpaceComm()->IsSerial(),
76 "Parallel execution is "
77 "not supported yet in "
78 "this module.")
79
80 // Check if required params are defined
81 ASSERTL0(m_f->m_graph, "A session file file must be provided before the "
82 "STL file.")
83
84 // Skip in case of empty partition
85 if (m_f->m_exp[0]->GetNumElmts() == 0)
86 {
87 return;
88 }
89
90 // Read Phi function from the session file...
91 if (m_config["file"].as<string>().compare("NotSet") == 0)
92 {
93 WARNINGL0(m_config["scale"].as<string>().compare("NotSet") == 0,
94 "Reading Phi function from session file, the provided scale "
95 "value will not be used");
97 }
98 // ...or Read STL file and append Phi values to the existing expansions
99 else
100 {
101 ASSERTL0(m_config["scale"].as<string>().compare("NotSet") != 0,
102 "Need to specify a scale coefficient, scale=value");
103
104 STLobject phiFile = ReadSTL(m_config["file"].as<string>());
105 GetPhifromSTL(phiFile);
106 }
107}
108
109/**
110 * @brief Read one 3D vector from a STL file, starting from the next line
111 * of the input 'ifstream'. Numbers in ifstream are defined as 'float'
112 *
113 * @param in
114 * @return Array<OneD, NekDouble>
115 */
117{
118 Array<OneD, NekDouble> out(3, 0.0);
119 float tmp;
120 char buf[4];
121
122 in.read(buf, sizeof(buf));
123 memcpy(&tmp, buf, sizeof(buf));
124 out[0] = tmp;
125 in.read(buf, sizeof(buf));
126 memcpy(&tmp, buf, sizeof(buf));
127 out[1] = tmp;
128 in.read(buf, sizeof(buf));
129 memcpy(&tmp, buf, sizeof(buf));
130 out[2] = tmp;
131
132 return out;
133}
134
135/**
136 * @brief Read an STL binary file and returns a struct of type 'STLobject'
137 * containing the parsed data
138 *
139 * @param filename
140 * @return ProcessPhiFromFile::STLobject
141 */
143{
144 STLobject out;
145
146 // Open file
147 ifstream fileStl(filename.c_str(), ios::binary);
148 ASSERTL0(fileStl, "An error occurred while trying to open the STL file.")
149
150 // Buffers
151 char headerBuf[80];
152 char numTriBuf[4];
153 char dumpBuf[2];
154
155 // Read header and num of triangles
156 fileStl.read(headerBuf, sizeof(headerBuf));
157 fileStl.read(numTriBuf, sizeof(numTriBuf));
158 memcpy(&out.numTri, numTriBuf, sizeof(numTriBuf));
159
160 // Read triangle data
162 for (NekUInt32 i = 0; i < out.numTri; ++i)
163 {
164 // Read normal vector
165 triangle tmpTri;
166 tmpTri.normal = ReadVector(fileStl);
167
168 // Read three vertices
169 tmpTri.v0 = ReadVector(fileStl);
170 tmpTri.v1 = ReadVector(fileStl);
171 tmpTri.v2 = ReadVector(fileStl);
172
173 // Add centroid to the triangle object
174 Array<OneD, NekDouble> centre(3);
175 centre[0] = (tmpTri.v0[0] + tmpTri.v1[0] + tmpTri.v2[0]) / 3.0;
176 centre[1] = (tmpTri.v0[1] + tmpTri.v1[1] + tmpTri.v2[1]) / 3.0;
177 centre[2] = (tmpTri.v0[2] + tmpTri.v1[2] + tmpTri.v2[2]) / 3.0;
178 tmpTri.centroid = centre;
179
180 out.triangles[i] = tmpTri;
181
182 // Dump triangle type
183 fileStl.read(dumpBuf, sizeof(dumpBuf));
184 }
185
186 // Close the file
187 fileStl.close();
188
189 return out;
190}
191
192/**
193 * @brief Smoothing function for the SPM method given a distance value
194 * and a scaling coefficient
195 *
196 * @param dist
197 * @param coeff
198 * @return NekDouble
199 */
201{
202 return -0.5 * (std::tanh(dist / coeff) - 1.0);
203}
204
205/**
206 * @brief Assigns to 'phi' the values indicated by 'ShapeFunction'
207 *
208 */
210{
211 // Check that 'ShapeFunction' appears in the session file
212 ASSERTL0(m_f->m_session->DefinesFunction("ShapeFunction"),
213 "If file=file.stl is not supplied as an argument, a "
214 "'ShapeFunction' block must be provided in the session "
215 "file.")
216
217 // Phi function in session file
219 m_f->m_session->GetFunction("ShapeFunction", "Phi");
220
221 // Get info about the domain
222 int nPts = m_f->m_exp[0]->GetNpoints();
223 int nVars = m_f->m_variables.size();
224 int nStrips;
225 m_f->m_session->LoadParameter("Strip_Z", nStrips, 1);
226
227 // Add new variable
228 m_f->m_variables.push_back("phi");
229
230 for (int s = 0; s < nStrips; ++s) // homogeneous strip varient
231 {
233 m_f->AppendExpList(m_f->m_numHomogeneousDir);
234 auto it = m_f->m_exp.begin() + s * (nVars + 1) + nVars;
235 m_f->m_exp.insert(it, Exp);
236 }
237
238 for (int s = 0; s < nStrips; ++s)
239 {
240 // Get current coords of the point
242 for (int i = 0; i < 3; ++i)
243 {
244 coords[i] = Array<OneD, NekDouble>(nPts, 0.0);
245 }
246 m_f->m_exp[s * nVars]->GetCoords(coords[0], coords[1], coords[2]);
247
248 // Append Phi expansion to 'm_f'
249 int fid = s * (nVars + 1) + nVars;
250
251 phiFunction->Evaluate(coords[0], coords[1], coords[2],
252 m_f->m_exp[fid]->UpdatePhys());
253 m_f->m_exp[fid]->FwdTrans(m_f->m_exp[fid]->GetPhys(),
254 m_f->m_exp[fid]->UpdateCoeffs());
255 }
256}
257
258/**
259 * @brief Assigns to 'phi' the corresponding values of Phi
260 *
261 * @param file
262 */
265{
266 // Get info about the domain
267 int nPts = m_f->m_exp[0]->GetNpoints();
268 int nVars = m_f->m_variables.size();
269
270 // Get coordinates
272 for (int i = 0; i < 3; ++i)
273 {
274 coords[i] = Array<OneD, NekDouble>(nPts, 0.0);
275 }
276 m_f->m_exp[0]->GetCoords(coords[0], coords[1], coords[2]);
277
278 // Add new variable
279 m_f->m_variables.push_back("phi");
280
281 // Number of homogeneous strips
282 int nStrips;
283 m_f->m_session->LoadParameter("Strip_Z", nStrips, 1);
284
285 // Find bounds of the mesh
286 Array<OneD, NekDouble> bounds(6);
287 bounds[0] = coords[0][0];
288 bounds[1] = coords[0][0];
289 bounds[2] = coords[1][0];
290 bounds[3] = coords[1][0];
291 bounds[4] = coords[2][0];
292 bounds[5] = coords[2][0];
293 for (int i = 1; i < nPts; ++i)
294 {
295 bounds[0] = (bounds[0] < coords[0][i]) ? bounds[0] : coords[0][i];
296 bounds[1] = (bounds[1] > coords[0][i]) ? bounds[1] : coords[0][i];
297 bounds[2] = (bounds[2] < coords[1][i]) ? bounds[2] : coords[1][i];
298 bounds[3] = (bounds[3] > coords[1][i]) ? bounds[3] : coords[1][i];
299 bounds[4] = (bounds[4] < coords[2][i]) ? bounds[4] : coords[2][i];
300 bounds[5] = (bounds[5] > coords[2][i]) ? bounds[5] : coords[2][i];
301 }
302 // and add a margin to avoid rounding errors
303 for (int i = 0; i < 6; ++i)
304 {
305 bounds[i] -= pow(-1, i) * 1e-10;
306 }
307
308 // Array of centroids of triangles in the STL object
310 for (int i = 0; i < file.numTri; ++i)
311 {
312 centroids[i] = file.triangles[i].centroid;
313 }
314
315 // Initialise octree
316 m_tree = Octree(centroids, 10, bounds);
317
318 for (int s = 0; s < nStrips; ++s) // homogeneous strip varient
319 {
321 m_f->AppendExpList(m_f->m_numHomogeneousDir);
322 auto it = m_f->m_exp.begin() + s * (nVars + 1) + nVars;
323 m_f->m_exp.insert(it, Exp);
324 }
325
326 // For each strip...
327 for (int s = 0; s < nStrips; ++s)
328 {
329 int fid = s * (nVars + 1) + nVars;
330
331 Array<OneD, NekDouble> phys = m_f->m_exp[fid]->UpdatePhys();
332
333 // Parallelisation is highly recommended here
334 for (int i = 0; i < nPts; ++i)
335 {
336 // Get coordinates of each point
337 Array<OneD, NekDouble> tmpCoords(3);
338 tmpCoords[2] = coords[2][i];
339 tmpCoords[1] = coords[1][i];
340 tmpCoords[0] = coords[0][i];
341
342 // Find the shortest distance to the body(ies)
343 double dist;
344 FindShortestDist(file, tmpCoords, dist);
345
346 // Get corresponding value of Phi
347 phys[i] = PhiFunction(dist, m_config["scale"].as<double>());
348 }
349
350 // Update vector of expansions
351 m_f->m_exp[fid]->FwdTrans(phys, m_f->m_exp[fid]->UpdateCoeffs());
352 }
353}
354
355/**
356 * @brief Checks if a ray traced from 'Origin' with direction 'Dvec' hits
357 * the triangle defined by 'tri'. Returns the distance to the plane
358 * defined by 'tri' in any case. A negative distance means that the hit
359 * happened in the direction oposite that of the ray. Approach to calculate
360 * the intersection point found in:
361 *
362 * Fast, minimum storage ray/triangle intersection,
363 * Tomas Moeller, Ben Trumbore
364 *
365 * @param tri
366 * @param Origin
367 * @param Dvec
368 * @param distance
369 * @param u
370 * @param v
371 * @return true
372 * @return false
373 */
375 const Array<OneD, NekDouble> &Origin,
376 const Array<OneD, NekDouble> &Dvec,
377 double &distance, double &u, double &v)
378{
379 // Edge vectors
382 Vmath::Vsub(3, tri.v1, 1, tri.v0, 1, E1, 1);
383 Vmath::Vsub(3, tri.v2, 1, tri.v0, 1, E2, 1);
384
385 // If det == 0, ray parallel to triangle
386 Array<OneD, NekDouble> Pvec = Cross(Dvec, E2);
387 double det = Vmath::Dot(3, Pvec, E1);
388 double inv_det = 1.0 / det;
389 if (IsEqual(0.0, det, 1e-10))
390 {
391 distance = numeric_limits<double>::max();
392 u = numeric_limits<double>::max();
393 v = numeric_limits<double>::max();
394 return false;
395 }
396
397 // Vector T and parameter u = (0.0, 1.0)
399 Vmath::Vsub(3, Origin, 1, tri.v0, 1, Tvec, 1);
400 u = Vmath::Dot(3, Pvec, Tvec) * inv_det;
401
402 // Vector Q and parameter v = (0.0, 1.0)
403 Array<OneD, NekDouble> Qvec = Cross(Tvec, E1);
404 v = Vmath::Dot(3, Qvec, Dvec) * inv_det;
405
406 // There is a hit if (u,v) coordinates are bounded
407 distance = Vmath::Dot(3, Qvec, E2) * inv_det;
408 if ((u < 0.0 || u > 1.0) || (v < 0.0 || u + v > 1.0))
409 {
410 return false;
411 }
412 else
413 {
414 return true;
415 }
416}
417
418/**
419 * @brief Calculates the shortest distance from a point \f$x\f$ to the closed
420 * body contained in the STL file
421 *
422 * @param file
423 * @param x
424 * @param dist
425 */
428 double &dist)
429{
430 // Find closest triangles
431 // First, use the ones in the node of 'x'
432 int node = m_tree.QueryNode(x);
433
434 // Set 'dist' to an unreal value
435 dist = numeric_limits<double>::max();
436
437 // If the node's depth is less than 3 the point is far from the object
438 int depth = m_tree.QueryDepth(node);
439
440 if (depth > 2)
441 {
442 vector<int> treeTriangles;
443 vector<int> tmpTriangles = m_tree.QueryPoints(node);
444 for (int point : tmpTriangles)
445 {
446 treeTriangles.push_back(point);
447 }
448
449 // Then, save the ones in the neighbouring nodes
450 vector<int> neighbours = m_tree.QueryNeighbours(node);
451 for (int neighNode : neighbours)
452 {
453 tmpTriangles = m_tree.QueryPoints(neighNode);
454 for (int point : tmpTriangles)
455 {
456 treeTriangles.push_back(point);
457 }
458 }
459
460 // Keep the sign (interior or exterior),
461 int distSign = 1;
462 // the normal vector of closest triangle so far,
463 Array<OneD, NekDouble> triNormal =
464 file.triangles[treeTriangles[0]].normal;
465 // and the distance to the triangle PLANE
466 double tParam = dist;
467
468 for (int i = 0; i < treeTriangles.size(); ++i)
469 {
470 // Find distance to triangle
471 triangle tri = file.triangles[treeTriangles[i]];
472 double currentTparam;
473 double u, v;
474 bool hit = CheckHit(tri, x, tri.normal, currentTparam, u, v);
475
476 // Save "sign" of 'currentTparam'
477 int currentDistSign = (currentTparam >= 0) - (currentTparam < 0);
478 // and distance to the triangle
479 double currentDist;
480
481 // Vector linking the hit point with the node
482 Array<OneD, NekDouble> distVector(3);
483
484 if (hit)
485 {
486 Vmath::Smul(3, -currentTparam, tri.normal, 1, distVector, 1);
487 currentDist = fabs(currentTparam);
488 }
489 else
490 {
491 // The minimum has to be in one of the edges
492 if (v < 0) // Edge V0-V1
493 {
494 distVector = Vector2edge(x, tri.v0, tri.v1);
495 }
496 else if (u < 0) // Edge V0-V2
497 {
498 distVector = Vector2edge(x, tri.v0, tri.v2);
499 }
500 else // Edge V1-V2
501 {
502 distVector = Vector2edge(x, tri.v1, tri.v2);
503 }
504 currentDist = sqrt(Vmath::Dot(3, distVector, distVector));
505 }
506
507 // Update 'dist', MAGIC CONSTANT AHEAD!
508 // In the case of corners, check that the new triangle is not
509 // giving contradictory information and, if so, use the one
510 // closer to 'x'. Otherwise, some exterior points will be treated
511 // as interior and viceversa
512 if (dist - currentDist > 1e-5 * currentDist ||
513 (IsEqual(dist, currentDist, 1e-5) &&
514 IsNegative(Vmath::Dot(3, triNormal, tri.normal), 1e-5) &&
515 fabs(currentTparam) > fabs(tParam)))
516 {
517 dist = currentDist;
518 tParam = currentTparam;
519 distSign = currentDistSign;
520 triNormal = tri.normal;
521 }
522 }
523
524 // Update distance sign
525 dist *= -distSign;
526 }
527}
528
529/**
530 * @brief Returns true if \f$x=y\f$ within the relative tolerance 'relTol'
531 * (relative to 'y')
532 *
533 * @param x
534 * @return true
535 * @return false
536 */
537bool ProcessPhiFromFile::IsEqual(double x, double y, double relTol)
538{
539 return (fabs(x - y) <= relTol * y);
540}
541
542/**
543 * @brief Returns true if \f$x<tol\f$
544 *
545 * @param x
546 * @param relTol
547 * @return true
548 * @return false
549 */
550bool ProcessPhiFromFile::IsNegative(double x, double tol)
551{
552 return (x < tol);
553}
554
555/**
556 * @brief Returns the cross product of vectors 'v0' y 'v1'
557 *
558 * @param v0
559 * @param v1
560 * @return Array<OneD, NekDouble>
561 */
564{
566
567 out[0] = v0[1] * v1[2] - v0[2] * v1[1];
568 out[1] = v0[2] * v1[0] - v0[0] * v1[2];
569 out[2] = v0[0] * v1[1] - v0[1] * v1[0];
570
571 return out;
572}
573
574/**
575 * @brief Determines the shortest distance from a point 'x' to the segment
576 * defined by the points 'e1' and 'e2'. Note that this distance may be
577 * equal to that to one of the end points. The vector returned points towards
578 * the point 'x'
579 *
580 * @param x
581 * @param e1
582 * @param e2
583 * @return Array<OneD, NekDouble>
584 */
587 const Array<OneD, NekDouble> &e2)
588{
589 size_t n = x.size();
592 Vmath::Vsub(n, x, 1, e1, 1, e1x, 1);
593 Vmath::Vsub(n, e2, 1, e1, 1, e1e2, 1);
594 double norm = sqrt(Vmath::Dot(n, e1e2, e1e2));
595 for (size_t i = 0; i < n; ++i)
596 {
597 e1e2[i] /= norm;
598 }
599
601 double proj = Vmath::Dot(n, e1x, e1e2);
602 if (proj < 0.0)
603 {
604 Vmath::Vsub(n, x, 1, e1, 1, out, 1);
605 }
606 else if (proj > norm)
607 {
608 Vmath::Vsub(n, x, 1, e2, 1, out, 1);
609 }
610 else
611 {
612 Vmath::Svtsvtp(n, 1.0, e1x, 1, -proj, e1e2, 1, out, 1);
613 }
614
615 return out;
616}
617} // namespace Nektar::FieldUtils
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:208
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:215
FieldSharedPtr m_f
Field object.
Definition: Module.h:239
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:272
int QueryDepth(int nodeID)
Definition: Octree.h:79
std::vector< int > QueryNeighbours(int nodeID)
Returns the IDs of the octants that surround the queried node. First, it finds the neighbouring nodes...
Definition: Octree.cpp:250
std::vector< int > QueryPoints(int nodeID)
Returns the indices of the points of the mesh contained in the tree.
Definition: Octree.cpp:235
int QueryNode(const Array< OneD, NekDouble > &coords, int depth=std::numeric_limits< int >::max())
Given the coordinates 'coords' of a point, returns the leaf octant that contains it....
Definition: Octree.cpp:137
Abstract base class for processing modules.
Definition: Module.h:301
static ModuleKey m_className
ModuleKey for class.
bool CheckHit(const triangle &tri, const Array< OneD, NekDouble > &Origin, const Array< OneD, NekDouble > &Dvec, double &distance, double &u, double &v)
Checks if a ray traced from 'Origin' with direction 'Dvec' hits the triangle defined by 'tri'....
void GetPhifromSession()
Assigns to 'phi' the values indicated by 'ShapeFunction'.
Array< OneD, NekDouble > Vector2edge(const Array< OneD, NekDouble > &x, const Array< OneD, NekDouble > &e1, const Array< OneD, NekDouble > &e2)
Determines the shortest distance from a point 'x' to the segment defined by the points 'e1' and 'e2'....
Array< OneD, NekDouble > Cross(const Array< OneD, NekDouble > &v0, const Array< OneD, NekDouble > &v1)
Returns the cross product of vectors 'v0' y 'v1'.
ProcessPhiFromFile(FieldSharedPtr f)
Set up ProcessPhiFromFile object.
STLobject ReadSTL(std::string filename)
Read an STL binary file and returns a struct of type 'STLobject' containing the parsed data.
void GetPhifromSTL(const STLobject &file)
Assigns to 'phi' the corresponding values of Phi.
static ModuleSharedPtr create(FieldSharedPtr f)
Creates an instance of this class.
void FindShortestDist(const STLobject &file, const Array< OneD, NekDouble > &x, double &dist)
Calculates the shortest distance from a point to the closed body contained in the STL file.
NekDouble PhiFunction(double dist, double coeff)
Smoothing function for the SPM method given a distance value and a scaling coefficient.
bool IsNegative(double x, double tol)
Returns true if .
void v_Process(po::variables_map &vm) override
bool IsEqual(double x, double y, double relTol)
Returns true if within the relative tolerance 'relTol' (relative to 'y')
Array< OneD, NekDouble > ReadVector(std::ifstream &in)
Read one 3D vector from a STL file, starting from the next line of the input 'ifstream'....
tKey RegisterCreatorFunction(tKey idKey, CreatorFunction classCreator, std::string pDesc="")
Register a class with the factory.
Definition: NekFactory.hpp:197
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:990
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:180
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:47
std::shared_ptr< Equation > EquationSharedPtr
Definition: Equation.h:125
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
std::uint32_t NekUInt32
double NekDouble
void Svtsvtp(int n, const T alpha, const T *x, int incx, const T beta, const T *y, int incy, T *z, int incz)
Svtsvtp (scalar times vector plus scalar times vector):
Definition: Vmath.hpp:473
T Dot(int n, const T *w, const T *x)
dot product
Definition: Vmath.hpp:761
void Smul(int n, const T alpha, const T *x, const int incx, T *y, const int incy)
Scalar multiply y = alpha*x.
Definition: Vmath.hpp:100
void Vsub(int n, const T *x, const int incx, const T *y, const int incy, T *z, const int incz)
Subtract vector z = x-y.
Definition: Vmath.hpp:220
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
Represents a command-line configuration option.
Definition: Module.h:129