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