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