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#include <boost/core/ignore_unused.hpp>
39
40using namespace Nektar;
41using namespace std;
42
43namespace Nektar
44{
45namespace FieldUtils
46{
47
51 "Computes the Phi function from a file, used in IB methods.")};
52
53/**
54 * @brief Set up ProcessPhiFromFile object.
55 *
56 */
58{
59 m_config["scale"] =
60 ConfigOption(false, "NotSet", "Scale coefficient for Phi.");
61 m_config["file"] =
62 ConfigOption(false, "NotSet", "File with the IB definition.");
63}
64
65/**
66 *
67 */
69{
70}
71
72/**
73 *
74 */
75void ProcessPhiFromFile::v_Process(po::variables_map &vm)
76{
77 // Ignore warnings due to 'vm'
78 boost::ignore_unused(vm);
79
80 // Do not run in parallel
81 ASSERTL0(m_f->m_session->GetComm()->GetSpaceComm()->IsSerial(),
82 "Parallel execution is "
83 "not supported yet in "
84 "this module.")
85
86 // Check if required params are defined
87 ASSERTL0(m_f->m_graph, "A session file file must be provided before the "
88 "STL file.")
89
90 // Skip in case of empty partition
91 if (m_f->m_exp[0]->GetNumElmts() == 0)
92 {
93 return;
94 }
95
96 // Read Phi function from the session file...
97 if (m_config["file"].as<string>().compare("NotSet") == 0)
98 {
99 WARNINGL0(m_config["scale"].as<string>().compare("NotSet") == 0,
100 "Reading Phi function from session file, the provided scale "
101 "value will not be used");
103 }
104 // ...or Read STL file and append Phi values to the existing expansions
105 else
106 {
107 ASSERTL0(m_config["scale"].as<string>().compare("NotSet") != 0,
108 "Need to specify a scale coefficient, scale=value");
109
110 STLobject phiFile = ReadSTL(m_config["file"].as<string>());
111 GetPhifromSTL(phiFile);
112 }
113}
114
115/**
116 * @brief Read one 3D vector from a STL file, starting from the next line
117 * of the input 'ifstream'. Numbers in ifstream are defined as 'float'
118 *
119 * @param in
120 * @return Array<OneD, NekDouble>
121 */
123{
124 Array<OneD, NekDouble> out(3, 0.0);
125 float tmp;
126 char buf[4];
127
128 in.read(buf, sizeof(buf));
129 memcpy(&tmp, buf, sizeof(buf));
130 out[0] = tmp;
131 in.read(buf, sizeof(buf));
132 memcpy(&tmp, buf, sizeof(buf));
133 out[1] = tmp;
134 in.read(buf, sizeof(buf));
135 memcpy(&tmp, buf, sizeof(buf));
136 out[2] = tmp;
137
138 return out;
139}
140
141/**
142 * @brief Read an STL binary file and returns a struct of type 'STLobject'
143 * containing the parsed data
144 *
145 * @param filename
146 * @return ProcessPhiFromFile::STLobject
147 */
149{
150 STLobject out;
151
152 // Open file
153 ifstream fileStl(filename.c_str(), ios::binary);
154 ASSERTL0(fileStl, "An error occurred while trying to open the STL file.")
155
156 // Buffers
157 char headerBuf[80];
158 char numTriBuf[4];
159 char dumpBuf[2];
160
161 // Read header and num of triangles
162 fileStl.read(headerBuf, sizeof(headerBuf));
163 fileStl.read(numTriBuf, sizeof(numTriBuf));
164 memcpy(&out.numTri, numTriBuf, sizeof(numTriBuf));
165
166 // Read triangle data
168 for (NekUInt32 i = 0; i < out.numTri; ++i)
169 {
170 // Read normal vector
171 triangle tmpTri;
172 tmpTri.normal = ReadVector(fileStl);
173
174 // Read three vertices
175 tmpTri.v0 = ReadVector(fileStl);
176 tmpTri.v1 = ReadVector(fileStl);
177 tmpTri.v2 = ReadVector(fileStl);
178
179 // Add centroid to the triangle object
180 Array<OneD, NekDouble> centre(3);
181 centre[0] = (tmpTri.v0[0] + tmpTri.v1[0] + tmpTri.v2[0]) / 3.0;
182 centre[1] = (tmpTri.v0[1] + tmpTri.v1[1] + tmpTri.v2[1]) / 3.0;
183 centre[2] = (tmpTri.v0[2] + tmpTri.v1[2] + tmpTri.v2[2]) / 3.0;
184 tmpTri.centroid = centre;
185
186 out.triangles[i] = tmpTri;
187
188 // Dump triangle type
189 fileStl.read(dumpBuf, sizeof(dumpBuf));
190 }
191
192 // Close the file
193 fileStl.close();
194
195 return out;
196}
197
198/**
199 * @brief Smoothing function for the SPM method given a distance value
200 * and a scaling coefficient
201 *
202 * @param dist
203 * @param coeff
204 * @return NekDouble
205 */
207{
208 return -0.5 * (std::tanh(dist / coeff) - 1.0);
209}
210
211/**
212 * @brief Assigns to 'phi' the values indicated by 'ShapeFunction'
213 *
214 */
216{
217 // Check that 'ShapeFunction' appears in the session file
218 ASSERTL0(m_f->m_session->DefinesFunction("ShapeFunction"),
219 "If file=file.stl is not supplied as an argument, a "
220 "'ShapeFunction' block must be provided in the session "
221 "file.")
222
223 // Phi function in session file
225 m_f->m_session->GetFunction("ShapeFunction", "Phi");
226
227 // Get info about the domain
228 int nPts = m_f->m_exp[0]->GetNpoints();
229 int nVars = m_f->m_variables.size();
230 int nStrips;
231 m_f->m_session->LoadParameter("Strip_Z", nStrips, 1);
232
233 // Add new variable
234 m_f->m_variables.push_back("phi");
235
236 for (int s = 0; s < nStrips; ++s) // homogeneous strip varient
237 {
239 m_f->AppendExpList(m_f->m_numHomogeneousDir);
240 auto it = m_f->m_exp.begin() + s * (nVars + 1) + nVars;
241 m_f->m_exp.insert(it, Exp);
242 }
243
244 for (int s = 0; s < nStrips; ++s)
245 {
246 // Get current coords of the point
248 for (int i = 0; i < 3; ++i)
249 {
250 coords[i] = Array<OneD, NekDouble>(nPts, 0.0);
251 }
252 m_f->m_exp[s * nVars]->GetCoords(coords[0], coords[1], coords[2]);
253
254 // Append Phi expansion to 'm_f'
255 int fid = s * (nVars + 1) + nVars;
256
257 phiFunction->Evaluate(coords[0], coords[1], coords[2],
258 m_f->m_exp[fid]->UpdatePhys());
259 m_f->m_exp[fid]->FwdTrans(m_f->m_exp[fid]->GetPhys(),
260 m_f->m_exp[fid]->UpdateCoeffs());
261 }
262}
263
264/**
265 * @brief Assigns to 'phi' the corresponding values of Phi
266 *
267 * @param file
268 */
271{
272 // Get info about the domain
273 int nPts = m_f->m_exp[0]->GetNpoints();
274 int nVars = m_f->m_variables.size();
275
276 // Get coordinates
278 for (int i = 0; i < 3; ++i)
279 {
280 coords[i] = Array<OneD, NekDouble>(nPts, 0.0);
281 }
282 m_f->m_exp[0]->GetCoords(coords[0], coords[1], coords[2]);
283
284 // Add new variable
285 m_f->m_variables.push_back("phi");
286
287 // Number of homogeneous strips
288 int nStrips;
289 m_f->m_session->LoadParameter("Strip_Z", nStrips, 1);
290
291 // Find bounds of the mesh
292 Array<OneD, NekDouble> bounds(6);
293 bounds[0] = coords[0][0];
294 bounds[1] = coords[0][0];
295 bounds[2] = coords[1][0];
296 bounds[3] = coords[1][0];
297 bounds[4] = coords[2][0];
298 bounds[5] = coords[2][0];
299 for (int i = 1; i < nPts; ++i)
300 {
301 bounds[0] = (bounds[0] < coords[0][i]) ? bounds[0] : coords[0][i];
302 bounds[1] = (bounds[1] > coords[0][i]) ? bounds[1] : coords[0][i];
303 bounds[2] = (bounds[2] < coords[1][i]) ? bounds[2] : coords[1][i];
304 bounds[3] = (bounds[3] > coords[1][i]) ? bounds[3] : coords[1][i];
305 bounds[4] = (bounds[4] < coords[2][i]) ? bounds[4] : coords[2][i];
306 bounds[5] = (bounds[5] > coords[2][i]) ? bounds[5] : coords[2][i];
307 }
308 // and add a margin to avoid rounding errors
309 for (int i = 0; i < 6; ++i)
310 {
311 bounds[i] -= pow(-1, i) * 1e-10;
312 }
313
314 // Array of centroids of triangles in the STL object
316 for (int i = 0; i < file.numTri; ++i)
317 {
318 centroids[i] = file.triangles[i].centroid;
319 }
320
321 // Initialise octree
322 m_tree = Octree(centroids, 10, bounds);
323
324 for (int s = 0; s < nStrips; ++s) // homogeneous strip varient
325 {
327 m_f->AppendExpList(m_f->m_numHomogeneousDir);
328 auto it = m_f->m_exp.begin() + s * (nVars + 1) + nVars;
329 m_f->m_exp.insert(it, Exp);
330 }
331
332 // For each strip...
333 for (int s = 0; s < nStrips; ++s)
334 {
335 int fid = s * (nVars + 1) + nVars;
336
337 Array<OneD, NekDouble> phys = m_f->m_exp[fid]->UpdatePhys();
338
339 // Parallelisation is highly recommended here
340 for (int i = 0; i < nPts; ++i)
341 {
342 // Get coordinates of each point
343 Array<OneD, NekDouble> tmpCoords(3);
344 tmpCoords[2] = coords[2][i];
345 tmpCoords[1] = coords[1][i];
346 tmpCoords[0] = coords[0][i];
347
348 // Find the shortest distance to the body(ies)
349 double dist;
350 FindShortestDist(file, tmpCoords, dist);
351
352 // Get corresponding value of Phi
353 phys[i] = PhiFunction(dist, m_config["scale"].as<double>());
354 }
355
356 // Update vector of expansions
357 m_f->m_exp[fid]->FwdTrans(phys, m_f->m_exp[fid]->UpdateCoeffs());
358 }
359}
360
361/**
362 * @brief Checks if a ray traced from 'Origin' with direction 'Dvec' hits
363 * the triangle defined by 'tri'. Returns the distance to the plane
364 * defined by 'tri' in any case. A negative distance means that the hit
365 * happened in the direction oposite that of the ray. Approach to calculate
366 * the intersection point found in:
367 *
368 * Fast, minimum storage ray/triangle intersection,
369 * Tomas Moeller, Ben Trumbore
370 *
371 * @param tri
372 * @param Origin
373 * @param Dvec
374 * @param distance
375 * @param u
376 * @param v
377 * @return true
378 * @return false
379 */
381 const Array<OneD, NekDouble> &Origin,
382 const Array<OneD, NekDouble> &Dvec,
383 double &distance, double &u, double &v)
384{
385 // Edge vectors
388 Vmath::Vsub(3, tri.v1, 1, tri.v0, 1, E1, 1);
389 Vmath::Vsub(3, tri.v2, 1, tri.v0, 1, E2, 1);
390
391 // If det == 0, ray parallel to triangle
392 Array<OneD, NekDouble> Pvec = Cross(Dvec, E2);
393 double det = Vmath::Dot(3, Pvec, E1);
394 double inv_det = 1.0 / det;
395 if (IsEqual(0.0, det, 1e-10))
396 {
397 distance = numeric_limits<double>::max();
398 u = numeric_limits<double>::max();
399 v = numeric_limits<double>::max();
400 return false;
401 }
402
403 // Vector T and parameter u = (0.0, 1.0)
405 Vmath::Vsub(3, Origin, 1, tri.v0, 1, Tvec, 1);
406 u = Vmath::Dot(3, Pvec, Tvec) * inv_det;
407
408 // Vector Q and parameter v = (0.0, 1.0)
409 Array<OneD, NekDouble> Qvec = Cross(Tvec, E1);
410 v = Vmath::Dot(3, Qvec, Dvec) * inv_det;
411
412 // There is a hit if (u,v) coordinates are bounded
413 distance = Vmath::Dot(3, Qvec, E2) * inv_det;
414 if ((u < 0.0 || u > 1.0) || (v < 0.0 || u + v > 1.0))
415 {
416 return false;
417 }
418 else
419 {
420 return true;
421 }
422}
423
424/**
425 * @brief Calculates the shortest distance from a point \f$x\f$ to the closed
426 * body contained in the STL file
427 *
428 * @param file
429 * @param x
430 * @param dist
431 */
434 double &dist)
435{
436 // Find closest triangles
437 // First, use the ones in the node of 'x'
438 int node = m_tree.QueryNode(x);
439
440 // Set 'dist' to an unreal value
441 dist = numeric_limits<double>::max();
442
443 // If the node's depth is less than 3 the point is far from the object
444 int depth = m_tree.QueryDepth(node);
445
446 if (depth > 2)
447 {
448 vector<int> treeTriangles;
449 vector<int> tmpTriangles = m_tree.QueryPoints(node);
450 for (int point : tmpTriangles)
451 {
452 treeTriangles.push_back(point);
453 }
454
455 // Then, save the ones in the neighbouring nodes
456 vector<int> neighbours = m_tree.QueryNeighbours(node);
457 for (int neighNode : neighbours)
458 {
459 tmpTriangles = m_tree.QueryPoints(neighNode);
460 for (int point : tmpTriangles)
461 {
462 treeTriangles.push_back(point);
463 }
464 }
465
466 // Keep the sign (interior or exterior),
467 int distSign = 1;
468 // the normal vector of closest triangle so far,
469 Array<OneD, NekDouble> triNormal =
470 file.triangles[treeTriangles[0]].normal;
471 // and the distance to the triangle PLANE
472 double tParam = dist;
473
474 for (int i = 0; i < treeTriangles.size(); ++i)
475 {
476 // Find distance to triangle
477 triangle tri = file.triangles[treeTriangles[i]];
478 double currentTparam;
479 double u, v;
480 bool hit = CheckHit(tri, x, tri.normal, currentTparam, u, v);
481
482 // Save "sign" of 'currentTparam'
483 int currentDistSign = (currentTparam >= 0) - (currentTparam < 0);
484 // and distance to the triangle
485 double currentDist;
486
487 // Vector linking the hit point with the node
488 Array<OneD, NekDouble> distVector(3);
489
490 if (hit)
491 {
492 Vmath::Smul(3, -currentTparam, tri.normal, 1, distVector, 1);
493 currentDist = fabs(currentTparam);
494 }
495 else
496 {
497 // The minimum has to be in one of the edges
498 if (v < 0) // Edge V0-V1
499 {
500 distVector = Vector2edge(x, tri.v0, tri.v1);
501 }
502 else if (u < 0) // Edge V0-V2
503 {
504 distVector = Vector2edge(x, tri.v0, tri.v2);
505 }
506 else // Edge V1-V2
507 {
508 distVector = Vector2edge(x, tri.v1, tri.v2);
509 }
510 currentDist = sqrt(Vmath::Dot(3, distVector, distVector));
511 }
512
513 // Update 'dist', MAGIC CONSTANT AHEAD!
514 // In the case of corners, check that the new triangle is not
515 // giving contradictory information and, if so, use the one
516 // closer to 'x'. Otherwise, some exterior points will be treated
517 // as interior and viceversa
518 if (dist - currentDist > 1e-5 * currentDist ||
519 (IsEqual(dist, currentDist, 1e-5) &&
520 IsNegative(Vmath::Dot(3, triNormal, tri.normal), 1e-5) &&
521 fabs(currentTparam) > fabs(tParam)))
522 {
523 dist = currentDist;
524 tParam = currentTparam;
525 distSign = currentDistSign;
526 triNormal = tri.normal;
527 }
528 }
529
530 // Update distance sign
531 dist *= -distSign;
532 }
533}
534
535/**
536 * @brief Returns true if \f$x=y\f$ within the relative tolerance 'relTol'
537 * (relative to 'y')
538 *
539 * @param x
540 * @return true
541 * @return false
542 */
543bool ProcessPhiFromFile::IsEqual(double x, double y, double relTol)
544{
545 return (fabs(x - y) <= relTol * y);
546}
547
548/**
549 * @brief Returns true if \f$x<tol\f$
550 *
551 * @param x
552 * @param relTol
553 * @return true
554 * @return false
555 */
556bool ProcessPhiFromFile::IsNegative(double x, double tol)
557{
558 return (x < tol);
559}
560
561/**
562 * @brief Returns the cross product of vectors 'v0' y 'v1'
563 *
564 * @param v0
565 * @param v1
566 * @return Array<OneD, NekDouble>
567 */
570{
572
573 out[0] = v0[1] * v1[2] - v0[2] * v1[1];
574 out[1] = v0[2] * v1[0] - v0[0] * v1[2];
575 out[2] = v0[0] * v1[1] - v0[1] * v1[0];
576
577 return out;
578}
579
580/**
581 * @brief Determines the shortest distance from a point 'x' to the segment
582 * defined by the points 'e1' and 'e2'. Note that this distance may be
583 * equal to that to one of the end points. The vector returned points towards
584 * the point 'x'
585 *
586 * @param x
587 * @param e1
588 * @param e2
589 * @return Array<OneD, NekDouble>
590 */
593 const Array<OneD, NekDouble> &e2)
594{
595 size_t n = x.size();
598 Vmath::Vsub(n, x, 1, e1, 1, e1x, 1);
599 Vmath::Vsub(n, e2, 1, e1, 1, e1e2, 1);
600 double norm = sqrt(Vmath::Dot(n, e1e2, e1e2));
601 for (size_t i = 0; i < n; ++i)
602 {
603 e1e2[i] /= norm;
604 }
605
607 double proj = Vmath::Dot(n, e1x, e1e2);
608 if (proj < 0.0)
609 {
610 Vmath::Vsub(n, x, 1, e1, 1, out, 1);
611 }
612 else if (proj > norm)
613 {
614 Vmath::Vsub(n, x, 1, e2, 1, out, 1);
615 }
616 else
617 {
618 Vmath::Svtsvtp(n, 1.0, e1x, 1, -proj, e1e2, 1, out, 1);
619 }
620
621 return out;
622}
623} // namespace FieldUtils
624} // namespace Nektar
#define ASSERTL0(condition, msg)
Definition: ErrorUtil.hpp:215
#define WARNINGL0(condition, msg)
Definition: ErrorUtil.hpp:222
FieldSharedPtr m_f
Field object.
Definition: Module.h:234
std::map< std::string, ConfigOption > m_config
List of configuration values.
Definition: Module.h:263
int QueryDepth(int nodeID)
Definition: Octree.h:81
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:252
std::vector< int > QueryPoints(int nodeID)
Returns the indices of the points of the mesh contained in the tree.
Definition: Octree.cpp:237
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:139
Abstract base class for processing modules.
Definition: Module.h:292
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 .
virtual 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:198
std::shared_ptr< Field > FieldSharedPtr
Definition: Field.hpp:991
std::pair< ModuleType, std::string > ModuleKey
Definition: Module.h:317
ModuleFactory & GetModuleFactory()
Definition: Module.cpp:49
std::shared_ptr< Equation > EquationSharedPtr
Definition: Equation.h:129
std::shared_ptr< ExpList > ExpListSharedPtr
Shared pointer to an ExpList object.
The above copyright notice and this permission notice shall be included.
Definition: CoupledSolver.h:2
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)
svtvvtp (scalar times vector plus scalar times vector):
Definition: Vmath.cpp:746
T Dot(int n, const T *w, const T *x)
dot (vector times vector): z = w*x
Definition: Vmath.cpp:1095
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.cpp:245
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.cpp:414
scalarT< T > sqrt(scalarT< T > in)
Definition: scalar.hpp:294
Represents a command-line configuration option.
Definition: Module.h:131