sixDoFRigidBodyMotionAxisConstraint.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-2015 OpenFOAM Foundation
9  Copyright (C) 2023 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
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 
27 \*---------------------------------------------------------------------------*/
28 
32 #include "unitConversion.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
36 namespace Foam
37 {
38 namespace sixDoFRigidBodyMotionConstraints
39 {
41 
43  (
45  axis,
47  );
48 }
49 }
50 
51 
52 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
53 
54 Foam::label Foam::sixDoFRigidBodyMotionConstraints::axis::rotationSector
55 (
56  const vector& oldDir,
57  const vector& newDir
58 ) const
59 {
60  const scalar thetaDir = (oldDir ^ newDir) & axis_;
61 
62  if (equal(thetaDir, 0))
63  {
64  return 0;
65  }
66 
67  return label(sign(thetaDir));
68 }
69 
70 
71 bool Foam::sixDoFRigidBodyMotionConstraints::axis::calcDir
72 (
73  const vector& fm,
74  const bool rotationSector
75 ) const
76 {
77  const scalar fmDir = axis_ & fm;
78 
79  if (equal(fmDir, 0))
80  {
81  return rotationSector;
82  }
83 
84  return (label(sign(fmDir)) == 1) ? true : false;
85 }
86 
87 
88 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
89 
91 (
92  const word& name,
93  const dictionary& sDoFRBMCDict,
94  const sixDoFRigidBodyMotion& motion
95 )
96 :
97  sixDoFRigidBodyMotionConstraint(name, sDoFRBMCDict, motion),
98  refQ_(),
99  axis_(),
100  maxCWThetaPtr_(),
101  maxCCWThetaPtr_(),
102  degrees_(false)
103 {
104  read(sDoFRBMCDict);
105 }
106 
107 
108 // * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * * //
109 
111 (
112  pointConstraint& pc
113 ) const
114 {}
115 
116 
118 (
119  pointConstraint& pc
120 ) const
121 {
122  if (!(maxCWThetaPtr_ && maxCCWThetaPtr_))
123  {
125  return;
126  }
127 
128 
129  // Calculate principal directions of the body
130  const vector refDir
131  (
132  rotationTensor(vector(1, 0 ,0), axis_) & vector(0, 1, 0)
133  );
134  const vector oldDir
135  (
136  (refQ_ & refDir).removeCollinear(axis_).normalise()
137  );
138  const vector newDir
139  (
140  (motion().orientation() & refDir).removeCollinear(axis_).normalise()
141  );
142 
143 
144  // Find the index of the rotation sector that the body resides
145  const label rotationSectorIndex = rotationSector(oldDir, newDir);
146 
147  if (!rotationSectorIndex)
148  {
149  // The body resides at the reference orientation
151  return;
152  }
153 
154  const bool rotationSector = (rotationSectorIndex == 1) ? true : false;
155 
156 
157  // Calculate the directions of total momentum and force acting on the body
158  const bool angularMomentumDir =
159  calcDir
160  (
161  motion().state().pi(),
162  rotationSector
163  );
164  const bool torqueDir = calcDir(motion().state().tau(), rotationSector);
165 
166 
167  // Calculate the rotation angle of the body wrt the reference orientation
168  const scalar theta = mag(acos(min(oldDir & newDir, scalar(1))));
169 
170 
171  // Calculate maximum clockwise and counterclockwise rotation angles
172  const scalar t = motion().time().timeOutputValue();
173  const scalar maxCWTheta =
174  degrees_
175  ? mag(degToRad(maxCWThetaPtr_->value(t)))
176  : mag(maxCWThetaPtr_->value(t));
177 
178  const scalar maxCCWTheta =
179  degrees_
180  ? mag(degToRad(maxCCWThetaPtr_->value(t)))
181  : mag(maxCCWThetaPtr_->value(t));
182 
183 
184  // Apply the constraints according to various conditions
185  if
186  (
187  (rotationSector && (theta < maxCCWTheta))
188  || (!rotationSector && (theta < maxCWTheta))
189  )
190  {
191  pc.combine(pointConstraint(Tuple2<label, vector>(2, axis_)));
192  }
193  else
194  {
195  if (rotationSector == angularMomentumDir)
196  {
197  const scalar magPi = mag(motion().state().pi());
198 
199  if (equal(magPi, scalar(0)) && rotationSector != torqueDir)
200  {
201  pc.combine(pointConstraint(Tuple2<label, vector>(2, axis_)));
202  }
203  else
204  {
205  // Constrain all rotational motions
206  pc.combine(pointConstraint(Tuple2<label, vector>(3, Zero)));
207  }
208  }
209  else
210  {
211  // If there is a difference between the directions of
212  // body rotation and of torque, release the body
213  pc.combine(pointConstraint(Tuple2<label, vector>(2, axis_)));
214  }
215  }
216 
217 
218  if (motion().report())
219  {
220  Info
221  << " old direction = " << oldDir << nl
222  << " new direction = " << newDir << nl
223  << " rotationSector = " << rotationSector << nl
224  << " theta = " << sign((oldDir ^ newDir) & axis_)*theta << nl
225  << " torque = " << motion().state().tau() << nl
226  << " torque dir = " << torqueDir << nl
227  << " angular momentum = " << motion().state().pi() << nl
228  << " angular momentum dir = " << angularMomentumDir << nl
229  << endl;
230  }
231 }
232 
233 
235 (
236  const dictionary& sDoFRBMCDict
237 )
238 {
239  if (!sixDoFRigidBodyMotionConstraint::read(sDoFRBMCDict))
240  {
241  return false;
242  }
243 
244  sDoFRBMCCoeffs_.readEntry("axis", axis_);
245 
246  axis_.normalise();
247 
248  if (mag(axis_) < VSMALL)
249  {
250  FatalIOErrorInFunction(sDoFRBMCDict)
251  << "The entry 'axis' cannot have zero length: " << axis_
252  << exit(FatalIOError);
253  }
254 
255 
256  if (sDoFRBMCCoeffs_.found("thetaUnits"))
257  {
258  const word thetaUnits(sDoFRBMCCoeffs_.getWord("thetaUnits"));
259 
260  if (thetaUnits == "degrees")
261  {
262  degrees_ = true;
263  }
264  else if (thetaUnits == "radians")
265  {
266  degrees_ = false;
267  }
268  else
269  {
270  FatalIOErrorInFunction(sDoFRBMCCoeffs_)
271  << "The units of thetaUnits can be either degrees or radians"
272  << exit(FatalIOError);
273  }
274 
275 
276  maxCWThetaPtr_.reset
277  (
278  Function1<scalar>::New
279  (
280  "maxClockwiseTheta",
281  sDoFRBMCCoeffs_,
282  &motion().time()
283  )
284  );
285 
286  maxCCWThetaPtr_.reset
287  (
288  Function1<scalar>::New
289  (
290  "maxCounterclockwiseTheta",
291  sDoFRBMCCoeffs_,
292  &motion().time()
293  )
294  );
295 
296 
297  refQ_ =
298  sDoFRBMCCoeffs_.getOrDefault<tensor>("referenceOrientation", I);
299 
300  if (mag(mag(refQ_) - sqrt(3.0)) > ROOTSMALL)
301  {
302  FatalIOErrorInFunction(sDoFRBMCCoeffs_)
303  << "The entry 'referenceOrientation' " << refQ_
304  << " is not a rotation tensor. "
305  << "mag(referenceOrientation) - sqrt(3) = "
306  << mag(refQ_) - sqrt(3.0) << nl
307  << exit(FatalIOError);
308  }
309  }
310 
311  return true;
312 }
313 
314 
316 (
317  Ostream& os
318 ) const
319 {
320  os.writeEntry("axis", axis_);
321 
322 
323  if (maxCWThetaPtr_ && maxCCWThetaPtr_)
324  {
325  if (degrees_)
326  {
327  os.writeEntry("thetaUnits", "degrees");
328  }
329  else
330  {
331  os.writeEntry("thetaUnits", "radians");
332  }
333 
334  if (maxCWThetaPtr_)
335  {
336  maxCWThetaPtr_->writeData(os);
337  }
338 
339  if (maxCCWThetaPtr_)
340  {
341  maxCCWThetaPtr_->writeData(os);
342  }
343 
344  os.writeEntry("referenceOrientation", refQ_);
345  }
346 }
347 
348 
349 // ************************************************************************* //
Six degree of freedom motion for a rigid body.
dimensionedScalar sign(const dimensionedScalar &ds)
dimensionedScalar acos(const dimensionedScalar &ds)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
bool equal(const T &a, const T &b)
Compare two values for equality.
Definition: label.H:164
Unit conversion functions.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
dimensionedScalar sqrt(const dimensionedScalar &ds)
Tensor< scalar > tensor
Definition: symmTensor.H:57
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
addToRunTimeSelectionTable(sixDoFRigidBodyMotionConstraint, axis, dictionary)
tensor rotationTensor(const vector &n1, const vector &n2)
Rotational transformation tensor from vector n1 to n2.
Definition: transform.H:47
void combine(const pointConstraint &)
Combine constraints.
const vector & tau() const
Return access to torque.
Base class for defining constraints for sixDoF motions.
Macros for easy insertion into run-time selection tables.
const sixDoFRigidBodyMotionState & state() const
Return the motion state.
This constraint imposes an orientation limitation where bodies are restricted to rotate only around a...
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
static const Identity< scalar > I
Definition: Identity.H:100
A class for handling words, derived from Foam::string.
Definition: word.H:63
const vector & pi() const
Return access to angular momentum.
virtual bool read(const dictionary &sDoFRBMCCoeff)
Update properties from given dictionary.
constexpr scalar pi(M_PI)
Vector< scalar > vector
Definition: vector.H:57
Accumulates point constraints through successive applications of the applyConstraint function...
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
An Ostream is an abstract base class for all output systems (streams, files, token lists...
Definition: Ostream.H:56
virtual bool read(const dictionary &sDoFRBMCDict)
Update properties from given dictionary.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
OBJstream os(runTime.globalPath()/outputName)
axis(const word &name, const dictionary &sDoFRBMCDict, const sixDoFRigidBodyMotion &motion)
Construct from components.
virtual void constrainTranslation(pointConstraint &) const
Apply and accumulate translational constraints.
const Time & time() const
Return time.
scalar timeOutputValue() const
Return the current user-time value. (ie, after applying any timeToUserTime() conversion) ...
Definition: TimeStateI.H:24
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:627
messageStream Info
Information stream (stdout output on master, null elsewhere)
virtual void constrainRotation(pointConstraint &) const
Apply and accumulate rotational constraints.
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
Namespace for OpenFOAM.
IOerror FatalIOError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL IO ERROR&#39; header text and ...
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127