surfaceInertia.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2018 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 Application
25  surfaceInertia
26 
27 Description
28  Calculates the inertia tensor, principal axes and moments of a
29  command line specified triSurface. Inertia can either be of the
30  solid body or of a thin shell.
31 
32 \*---------------------------------------------------------------------------*/
33 
34 #include "argList.H"
35 #include "ListOps.H"
36 #include "triSurface.H"
37 #include "OFstream.H"
38 #include "meshTools.H"
39 #include "Random.H"
40 #include "transform.H"
41 #include "IOmanip.H"
42 #include "Pair.H"
43 #include "momentOfInertia.H"
44 
45 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
46 
47 using namespace Foam;
48 
49 int main(int argc, char *argv[])
50 {
51  #include "removeCaseOptions.H"
52 
54  (
55  "Calculates the inertia tensor and principal axes and moments "
56  "of the specified surface.\n"
57  "Inertia can either be of the solid body or of a thin shell."
58  );
59 
60  argList::validArgs.append("surface file");
62  (
63  "shellProperties",
64  "inertia of a thin shell"
65  );
66 
68  (
69  "density",
70  "scalar",
71  "Specify density, "
72  "kg/m3 for solid properties, kg/m2 for shell properties"
73  );
74 
76  (
77  "referencePoint",
78  "vector",
79  "Inertia relative to this point, not the centre of mass"
80  );
81 
82  argList args(argc, argv);
83 
84  const fileName surfFileName = args[1];
85  const scalar density = args.optionLookupOrDefault("density", 1.0);
86 
87  vector refPt = Zero;
88  bool calcAroundRefPt = args.optionReadIfPresent("referencePoint", refPt);
89 
90  triSurface surf(surfFileName);
91 
92  scalar m = 0.0;
93  vector cM = Zero;
94  tensor J = Zero;
95 
96  if (args.optionFound("shellProperties"))
97  {
98  momentOfInertia::massPropertiesShell(surf, density, m, cM, J);
99  }
100  else
101  {
102  momentOfInertia::massPropertiesSolid(surf, density, m, cM, J);
103  }
104 
105  if (m < 0)
106  {
108  << "Negative mass detected, the surface may be inside-out." << endl;
109  }
110 
111  vector eVal = eigenValues(J);
112 
113  tensor eVec = eigenVectors(J);
114 
115  label pertI = 0;
116 
117  Random rand(57373);
118 
119  while ((magSqr(eVal) < vSmall) && pertI < 10)
120  {
122  << "No eigenValues found, shape may have symmetry, "
123  << "perturbing inertia tensor diagonal" << endl;
124 
125  J.xx() *= 1.0 + small*rand.scalar01();
126  J.yy() *= 1.0 + small*rand.scalar01();
127  J.zz() *= 1.0 + small*rand.scalar01();
128 
129  eVal = eigenValues(J);
130 
131  eVec = eigenVectors(J);
132 
133  pertI++;
134  }
135 
136  bool showTransform = true;
137 
138  if
139  (
140  (mag(eVec.x() ^ eVec.y()) > (1.0 - small))
141  && (mag(eVec.y() ^ eVec.z()) > (1.0 - small))
142  && (mag(eVec.z() ^ eVec.x()) > (1.0 - small))
143  )
144  {
145  // Make the eigenvectors a right handed orthogonal triplet
146  eVec = tensor
147  (
148  eVec.x(),
149  eVec.y(),
150  eVec.z() * sign((eVec.x() ^ eVec.y()) & eVec.z())
151  );
152 
153  // Finding the most natural transformation. Using Lists
154  // rather than tensors to allow indexed permutation.
155 
156  // Cartesian basis vectors - right handed orthogonal triplet
157  List<vector> cartesian(3);
158 
159  cartesian[0] = vector(1, 0, 0);
160  cartesian[1] = vector(0, 1, 0);
161  cartesian[2] = vector(0, 0, 1);
162 
163  // Principal axis basis vectors - right handed orthogonal
164  // triplet
165  List<vector> principal(3);
166 
167  principal[0] = eVec.x();
168  principal[1] = eVec.y();
169  principal[2] = eVec.z();
170 
171  scalar maxMagDotProduct = -great;
172 
173  // Matching axis indices, first: cartesian, second:principal
174 
175  Pair<label> match(-1, -1);
176 
177  forAll(cartesian, cI)
178  {
179  forAll(principal, pI)
180  {
181  scalar magDotProduct = mag(cartesian[cI] & principal[pI]);
182 
183  if (magDotProduct > maxMagDotProduct)
184  {
185  maxMagDotProduct = magDotProduct;
186 
187  match.first() = cI;
188 
189  match.second() = pI;
190  }
191  }
192  }
193 
194  scalar sense = sign
195  (
196  cartesian[match.first()] & principal[match.second()]
197  );
198 
199  if (sense < 0)
200  {
201  // Invert the best match direction and swap the order of
202  // the other two vectors
203 
204  List<vector> tPrincipal = principal;
205 
206  tPrincipal[match.second()] *= -1;
207 
208  tPrincipal[(match.second() + 1) % 3] =
209  principal[(match.second() + 2) % 3];
210 
211  tPrincipal[(match.second() + 2) % 3] =
212  principal[(match.second() + 1) % 3];
213 
214  principal = tPrincipal;
215 
216  vector tEVal = eVal;
217 
218  tEVal[(match.second() + 1) % 3] = eVal[(match.second() + 2) % 3];
219 
220  tEVal[(match.second() + 2) % 3] = eVal[(match.second() + 1) % 3];
221 
222  eVal = tEVal;
223  }
224 
225  label permutationDelta = match.second() - match.first();
226 
227  if (permutationDelta != 0)
228  {
229  // Add 3 to the permutationDelta to avoid negative indices
230 
231  permutationDelta += 3;
232 
233  List<vector> tPrincipal = principal;
234 
235  vector tEVal = eVal;
236 
237  for (label i = 0; i < 3; i++)
238  {
239  tPrincipal[i] = principal[(i + permutationDelta) % 3];
240 
241  tEVal[i] = eVal[(i + permutationDelta) % 3];
242  }
243 
244  principal = tPrincipal;
245 
246  eVal = tEVal;
247  }
248 
249  label matchedAlready = match.first();
250 
251  match =Pair<label>(-1, -1);
252 
253  maxMagDotProduct = -great;
254 
255  forAll(cartesian, cI)
256  {
257  if (cI == matchedAlready)
258  {
259  continue;
260  }
261 
262  forAll(principal, pI)
263  {
264  if (pI == matchedAlready)
265  {
266  continue;
267  }
268 
269  scalar magDotProduct = mag(cartesian[cI] & principal[pI]);
270 
271  if (magDotProduct > maxMagDotProduct)
272  {
273  maxMagDotProduct = magDotProduct;
274 
275  match.first() = cI;
276 
277  match.second() = pI;
278  }
279  }
280  }
281 
282  sense = sign
283  (
284  cartesian[match.first()] & principal[match.second()]
285  );
286 
287  if (sense < 0 || (match.second() - match.first()) != 0)
288  {
289  principal[match.second()] *= -1;
290 
291  List<vector> tPrincipal = principal;
292 
293  tPrincipal[(matchedAlready + 1) % 3] =
294  principal[(matchedAlready + 2) % 3]*-sense;
295 
296  tPrincipal[(matchedAlready + 2) % 3] =
297  principal[(matchedAlready + 1) % 3]*-sense;
298 
299  principal = tPrincipal;
300 
301  vector tEVal = eVal;
302 
303  tEVal[(matchedAlready + 1) % 3] = eVal[(matchedAlready + 2) % 3];
304 
305  tEVal[(matchedAlready + 2) % 3] = eVal[(matchedAlready + 1) % 3];
306 
307  eVal = tEVal;
308  }
309 
310  eVec = tensor(principal[0], principal[1], principal[2]);
311 
312  // {
313  // tensor R = rotationTensor(vector(1, 0, 0), eVec.x());
314 
315  // R = rotationTensor(R & vector(0, 1, 0), eVec.y()) & R;
316 
317  // Info<< "R = " << nl << R << endl;
318 
319  // Info<< "R - eVec.T() " << R - eVec.T() << endl;
320  // }
321  }
322  else
323  {
325  << "Non-unique eigenvectors, cannot compute transformation "
326  << "from Cartesian axes" << endl;
327 
328  showTransform = false;
329  }
330 
331  // calculate the total surface area
332 
333  scalar surfaceArea = 0;
334 
335  forAll(surf, facei)
336  {
337  const labelledTri& f = surf[facei];
338 
339  if (f[0] == f[1] || f[0] == f[2] || f[1] == f[2])
340  {
342  << "Illegal triangle " << facei << " vertices " << f
343  << " coords " << f.points(surf.points()) << endl;
344  }
345  else
346  {
347  surfaceArea += triPointRef
348  (
349  surf.points()[f[0]],
350  surf.points()[f[1]],
351  surf.points()[f[2]]
352  ).mag();
353  }
354  }
355 
356  Info<< nl << setprecision(12)
357  << "Density: " << density << nl
358  << "Mass: " << m << nl
359  << "Centre of mass: " << cM << nl
360  << "Surface area: " << surfaceArea << nl
361  << "Inertia tensor around centre of mass: " << nl << J << nl
362  << "eigenValues (principal moments): " << eVal << nl
363  << "eigenVectors (principal axes): " << nl
364  << eVec.x() << nl << eVec.y() << nl << eVec.z() << endl;
365 
366  if (showTransform)
367  {
368  Info<< "Transform tensor from reference state (orientation):" << nl
369  << eVec.T() << nl
370  << "Rotation tensor required to transform "
371  "from the body reference frame to the global "
372  "reference frame, i.e.:" << nl
373  << "globalVector = orientation & bodyLocalVector"
374  << endl;
375 
376  Info<< nl
377  << "Entries for sixDoFRigidBodyDisplacement boundary condition:"
378  << nl
379  << " mass " << m << token::END_STATEMENT << nl
380  << " centreOfMass " << cM << token::END_STATEMENT << nl
381  << " momentOfInertia " << eVal << token::END_STATEMENT << nl
382  << " orientation " << eVec.T() << token::END_STATEMENT
383  << endl;
384  }
385 
386  if (calcAroundRefPt)
387  {
388  Info<< nl << "Inertia tensor relative to " << refPt << ": " << nl
390  << endl;
391  }
392 
393  OFstream str("axes.obj");
394 
395  Info<< nl << "Writing scaled principal axes at centre of mass of "
396  << surfFileName << " to " << str.name() << endl;
397 
398  scalar scale = mag(cM - surf.points()[0])/eVal.component(findMin(eVal));
399 
400  meshTools::writeOBJ(str, cM);
401  meshTools::writeOBJ(str, cM + scale*eVal.x()*eVec.x());
402  meshTools::writeOBJ(str, cM + scale*eVal.y()*eVec.y());
403  meshTools::writeOBJ(str, cM + scale*eVal.z()*eVec.z());
404 
405  for (label i = 1; i < 4; i++)
406  {
407  str << "l " << 1 << ' ' << i + 1 << endl;
408  }
409 
410  Info<< nl << "End" << nl << endl;
411 
412  return 0;
413 }
414 
415 
416 // ************************************************************************* //
Istream and Ostream manipulators taking arguments.
Various functions to operate on Lists.
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: List.H:91
Output to file stream.
Definition: OFstream.H:86
Random number generator.
Definition: Random.H:58
Tensor< Cmpt > T() const
Return transpose.
Definition: TensorI.H:331
const Cmpt & xx() const
Definition: TensorI.H:163
Vector< Cmpt > z() const
Definition: TensorI.H:303
const Cmpt & zz() const
Definition: TensorI.H:219
const Cmpt & yy() const
Definition: TensorI.H:191
Vector< Cmpt > y() const
Definition: TensorI.H:296
Vector< Cmpt > x() const
Definition: TensorI.H:289
const Cmpt & component(const direction) const
Definition: VectorSpaceI.H:91
const Cmpt & z() const
Definition: VectorI.H:87
const Cmpt & y() const
Definition: VectorI.H:81
const Cmpt & x() const
Definition: VectorI.H:75
Extract command arguments and options from the supplied argc and argv parameters.
Definition: argList.H:103
static void addOption(const word &opt, const string &param="", const string &usage="")
Add to an option to validOptions with usage information.
Definition: argList.C:128
static void addNote(const string &)
Add extra notes for the usage information.
Definition: argList.C:159
static void addBoolOption(const word &opt, const string &usage="")
Add to a bool option to validOptions with usage information.
Definition: argList.C:118
bool optionFound(const word &opt) const
Return true if the named option is found.
Definition: argListI.H:114
bool optionReadIfPresent(const word &opt, T &) const
Read a value from the named option if present.
Definition: argListI.H:204
static SLList< string > validArgs
A list of valid (mandatory) arguments.
Definition: argList.H:153
T optionLookupOrDefault(const word &opt, const T &deflt) const
Read a value from the named option if present.
Definition: argListI.H:243
A class for handling file names.
Definition: fileName.H:82
word name() const
Return file name (part beyond last /)
Definition: fileName.C:195
Triangle with additional region number.
Definition: labelledTri.H:60
static tensor applyParallelAxisTheorem(scalar mass, const vector &cM, const tensor &J, const vector &refPt)
static void massPropertiesShell(const pointField &pts, const triFaceList &triFaces, scalar density, scalar &mass, vector &cM, tensor &J)
static void massPropertiesSolid(const pointField &pts, const triFaceList &triFaces, scalar density, scalar &mass, vector &cM, tensor &J)
@ END_STATEMENT
Definition: token.H:105
Triangulated surface description with patch information.
Definition: triSurface.H:69
int main(int argc, char *argv[])
Definition: financialFoam.C:44
#define WarningInFunction
Report a warning using Foam::Warning.
void writeOBJ(Ostream &os, const point &pt)
Write obj representation of point.
Definition: meshTools.C:203
Namespace for OpenFOAM.
static const zero Zero
Definition: zero.H:97
Omanip< int > setprecision(const int i)
Definition: IOmanip.H:205
dimensionedScalar sign(const dimensionedScalar &ds)
dimensionedTensor eigenVectors(const dimensionedTensor &dt)
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Tensor< scalar > tensor
Tensor of scalars.
Definition: tensor.H:51
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
messageStream Info
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:49
dimensioned< scalar > mag(const dimensioned< Type > &)
dimensionedVector eigenValues(const dimensionedTensor &dt)
label findMin(const ListType &, const label start=0)
Find index of min element (and less than given element).
triangle< point, const point & > triPointRef
Definition: triPointRef.H:44
static const char nl
Definition: Ostream.H:260
dimensioned< scalar > magSqr(const dimensioned< Type > &)
labelList f(nPoints)
Foam::argList args(argc, argv)
3D tensor transformation operations.