sectionalForcesBase.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) 2026 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 \*---------------------------------------------------------------------------*/
25 
26 #include "indirectPrimitivePatch.H"
27 #include "sectionalForcesBase.H"
28 #include "surfaceFields.H"
29 #include "volFields.H"
30 #include "writeFile.H"
31 
32 #include "fvcGrad.H"
33 #include "surfaceInterpolate.H"
34 
39 
40 #include "polyTopoChangeMap.H"
41 #include "polyMeshMap.H"
42 #include "polyDistributionMap.H"
43 
44 #include "forces.H"
45 
46 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
47 
48 namespace Foam
49 {
50 namespace functionObjects
51 {
53 }
54 }
55 
56 
57 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
58 
59 template<class Type>
61 Foam::functionObjects::sectionalForcesBase::timesAlpha
62 (
64 ) const
65 {
66  if (phaseName_ != word::null)
67  {
68  const volScalarField& alpha =
70  (
71  IOobject::groupName("alpha", phaseName_)
72  );
73 
74  psi.ref() *= fvc::interpolate(alpha);
75  }
76 
77  return psi;
78 }
79 
80 
81 template<class Type>
83 Foam::functionObjects::sectionalForcesBase::timesAlpha
84 (
85  const tmp<Field<Type>> psi,
86  const label patchi
87 ) const
88 {
89  if (phaseName_ != word::null)
90  {
91  const volScalarField& alpha =
93  (
94  IOobject::groupName("alpha", phaseName_)
95  );
96 
97  psi.ref() *= alpha.boundaryField()[patchi];
98  }
99 
100  return psi;
101 }
102 
103 
104 template<class Type>
106 Foam::functionObjects::sectionalForcesBase::timesRho
107 (
108  const tmp<SurfaceField<Type>> psi
109 ) const
110 {
111  if (rhoName_ != "rhoInf")
112  {
113  const volScalarField & rho =
114  mesh().lookupObject<volScalarField>(rhoName_);
115 
116  psi.ref() *= fvc::interpolate(rho);
117  }
118  else
119  {
120  psi.ref() *= dimensionedScalar(dimDensity, rhoRef_);
121  }
122 
123  return psi;
124 }
125 
126 
127 template<class Type>
129 Foam::functionObjects::sectionalForcesBase::timesAlphaRho
130 (
131  const tmp<SurfaceField<Type>> psi
132 ) const
133 {
134  if (phaseName_ != word::null && rhoName_ != "rhoInf")
135  {
136  const volScalarField& alpha =
138  (
139  IOobject::groupName("alpha", phaseName_)
140  );
141 
142  const volScalarField & rho =
143  mesh().lookupObject<volScalarField>(rhoName_);
144 
145  psi.ref() *= fvc::interpolate(alpha*rho);
146 
147  return psi;
148  }
149  else
150  {
151  return timesRho(timesAlpha(psi));
152  }
153 }
154 
155 
157 Foam::functionObjects::sectionalForcesBase::p() const
158 {
159  const volScalarField& p = obr_.lookupObject<volScalarField>(pName_);
160 
161  if (p.dimensions() == dimPressure)
162  {
163  return p;
164  }
165  else if (p.dimensions() == dimPressure/dimDensity)
166  {
167  if (rhoName_ != "rhoInf")
168  {
170  << "kinematic pressure found but no 'rhoInf' specified"
171  << exit(FatalError);
172  }
173 
174  return dimensionedScalar(dimDensity, rhoRef_)*p;
175  }
176  else
177  {
179  << "pressure dimensions not recognised"
180  << exit(FatalError);
181 
182  return tmp<volScalarField>(nullptr);
183  }
184 }
185 
186 
188 Foam::functionObjects::sectionalForcesBase::devTau() const
189 {
191  typedef compressible::momentumTransportModel cmpModel;
192  typedef phaseIncompressible::momentumTransportModel phaseIcoModel;
193  typedef phaseCompressible::momentumTransportModel phaseCmpModel;
194 
196  const word phaseModelName = IOobject::groupName(modelName, phaseName_);
197 
198  if (obr_.foundObject<icoModel>(modelName))
199  {
201  obr_.lookupObject<icoModel>(modelName);
202 
203  return timesAlphaRho(model.devSigma());
204  }
205  else if (obr_.foundObject<cmpModel>(modelName))
206  {
207  const cmpModel& model =
208  obr_.lookupObject<cmpModel>(modelName);
209 
210  return timesAlpha(model.devTau());
211  }
212  else if (obr_.foundObject<phaseIcoModel>(phaseModelName))
213  {
214  const phaseIcoModel& model =
215  obr_.lookupObject<phaseIcoModel>(phaseModelName);
216 
217  return timesRho(model.devSigma());
218  }
219  else if (obr_.foundObject<phaseCmpModel>(phaseModelName))
220  {
221  const phaseCmpModel& model =
222  obr_.lookupObject<phaseCmpModel>(phaseModelName);
223 
224  return model.devTau();
225  }
226  else
227  {
229  << "No valid model for viscous stress calculation"
230  << exit(FatalError);
231 
232  return surfaceVectorField::null();
233  }
234 }
235 
236 
237 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
238 
240 {
241  weightsPtr_.clear();
242 }
243 
244 
247 {
248  if (!patchPtr_.valid())
249  {
250  labelList patchFaces;
251  forAllConstIter(labelHashSet, patchSet_, iter)
252  {
253  const label ppi = iter.key();
254  const polyPatch& pp = mesh().poly().boundary()[ppi];
255 
256  patchFaces.append(identityMap(pp.start(), pp.size()));
257  }
258 
259  patchPtr_.reset
260  (
262  (
263  IndirectList<face>(mesh().faces(), patchFaces),
264  mesh().points()
265  )
266  );
267  }
268 
269  return patchPtr_();
270 }
271 
272 
274 {
275  if (patchPtr_.valid())
276  {
277  patchPtr_.clear();
278  }
279 }
280 
281 
283 {
284  if (patchPtr_.valid())
285  {
286  patchPtr_->clearGeom();
287  }
288 }
289 
290 
293 {
294  return (patch().localPoints() - origin()) & normal();
295 }
296 
297 
300 {
301  if (!weightsPtr_.valid())
302  {
303  weightsPtr_.reset
304  (
306  (
308  (
309  patch(),
310  patchPointDistances(),
311  distances(),
312  false,
313  false
314  )
315  )
316  );
317  }
318 
319  return weightsPtr_();
320 }
321 
322 
324 {
325  return
326  time_.globalPath()
328  /(mesh_.name() != polyMesh::defaultRegion ? mesh_.name() : word())
329  /name()
330  /time_.name();
331 }
332 
333 
335 (
337  vectorField& moment
338 ) const
339 {
340  tmp<scalarField> tdistances = this->distances();
341  const scalarField& distances = tdistances();
342 
343  // Get the pressure
344  tmp<volScalarField> tp = this->p();
345  const volScalarField& p = tp();
346 
347  // Get the stress tensor
348  tmp<surfaceVectorField> tdevTau = this->devTau();
349  const surfaceVectorField& devTau = tdevTau();
350 
351  // Compute patch-face fluid forces and moments around the origin
352  label patchFacei = 0;
353  vectorField patchFaceForces(patch().size());
354  vectorField patchFaceMoments(patch().size());
355  forAllConstIter(labelHashSet, patchSet_, iter)
356  {
357  const label ppi = iter.key();
358  const polyPatch& pp = mesh().poly().boundary()[ppi];
359 
360  const vectorField f
361  (
362  timesAlpha
363  (
364  pp.faceNormals()*(p.boundaryField()[ppi] - pRef_)
365  + devTau.boundaryField()[ppi],
366  ppi
367  )
368  );
369 
370  SubList<vector>(patchFaceForces, pp.size(), patchFacei) =
371  f;
372  SubList<vector>(patchFaceMoments, pp.size(), patchFacei) =
373  (pp.faceCentres() - origin()) ^ f;
374 
375  patchFacei += pp.size();
376  }
377 
378  // Construct the total fluid forces on the intervals
379  const vectorField intervalForces
380  (
382  (
383  distances.size() - 1,
384  weights(),
385  patchFaceForces
386  )
387  );
388  const vectorField intervalMoments
389  (
391  (
392  distances.size() - 1,
393  weights(),
394  patchFaceMoments
395  )
396  );
397 
398  // Cumulatively sum the interval forces to obtain the sectional forces
400  forAllReverse(intervalForces, i)
401  {
402  f += intervalForces[i];
403  m += intervalMoments[i];
404  force[i] += f;
405  moment[i] += m - (distances[i]*normal() ^ f);
406  }
407 
408  // Check consistency with the forces function object if the calculation
409  // range spans the entire patch
410  if
411  (
412  debug
413  && distances.first() < gMin(patchPointDistances())
414  && gMax(patchPointDistances()) < distances.last()
415  )
416  {
418  forAllConstIter(labelHashSet, patchSet_, iter)
419  {
420  const label ppi = iter.key();
421  const polyPatch& pp = mesh().poly().boundary()[ppi];
422 
423  patchNames.append(pp.name());
424  }
425 
426  functionObjects::forces forcesFunctionObject
427  (
429  mesh().time(),
431  (
433  "patches", patchNames,
434  "CofR", origin() + distances[0]*normal()
435  )
436  );
437 
438  forcesFunctionObject.calcForcesMoments();
439 
440  Info<< functionObject::typeName << "s::"
442  << " force = " << forcesFunctionObject.forceEff() << nl
443  << " moment = " << forcesFunctionObject.momentEff() << nl
444  << functionObject::typeName << "s::"
445  << type() << ":" << nl
446  << " force = " << force[0] << nl
447  << " moment = " << moment[0] << endl;
448  }
449 }
450 
451 
452 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
453 
455 (
456  const word& name,
457  const Time& runTime,
458  const dictionary& dict
459 )
460 :
461  fvMeshFunctionObject(name, runTime, dict),
462  patchSet_(),
463  patchPtr_(nullptr),
464  pName_(word::null),
465  UName_(word::null),
466  rhoName_(word::null),
467  phaseName_(word::null),
468  rhoRef_(NaN),
469  pRef_(NaN),
470  weightsPtr_(nullptr)
471 {
472  read(dict);
473 }
474 
475 
476 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
477 
479 {}
480 
481 
482 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
483 
485 {
487 
488  patchSet_ = mesh().poly().boundary().patchSet(dict);
489 
490  // Optional phase entry
491  phaseName_ = dict.lookupOrDefault<word>("phase", word::null);
492 
493  // Optional p, U, and rho entries
494  pName_ =
495  dict.lookupOrDefault<word>
496  (
497  "p",
498  IOobject::groupName("p", phaseName_)
499  );
500  UName_ =
501  dict.lookupOrDefault<word>
502  (
503  "U",
504  IOobject::groupName("U", phaseName_)
505  );
506  rhoName_ =
507  dict.lookupOrDefault<word>
508  (
509  "rho",
510  IOobject::groupName("rho", phaseName_)
511  );
512 
513  // Reference density needed for incompressible calculations
514  if (rhoName_ == "rhoInf")
515  {
516  dict.lookup("rhoInf") >> rhoRef_;
517  }
518 
519  // Reference pressure, 0 by default
520  pRef_ = dict.lookupOrDefault<scalar>("pRef", 0.0);
521 
522  return true;
523 }
524 
525 
527 {
528  return wordList::null();
529 }
530 
531 
533 {
534  return true;
535 }
536 
537 
539 {
540  return true;
541 }
542 
543 
545 (
546  const polyMesh& mesh
547 )
548 {
549  if (&mesh == &mesh_)
550  {
551  clear();
552  clearPatchGeom();
553  }
554 }
555 
556 
558 (
559  const polyTopoChangeMap& map
560 )
561 {
562  if (&map.mesh() == &mesh_)
563  {
564  clear();
565  clearPatch();
566  }
567 }
568 
569 
571 (
572  const polyMeshMap& map
573 )
574 {
575  if (&map.mesh() == &mesh_)
576  {
577  clear();
578  clearPatch();
579  }
580 }
581 
582 
584 (
585  const polyDistributionMap& map
586 )
587 {
588  if (&map.mesh() == &mesh_)
589  {
590  clear();
591  clearPatch();
592  }
593 }
594 
595 
596 // ************************************************************************* //
#define forAllReverse(list, i)
Reverse loop across all elements in list.
Definition: UList.H:461
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:492
Pre-declare SubField and related Field type.
Definition: Field.H:83
Generic GeometricField class.
const Boundary & boundaryField() const
Return const-reference to the boundary field.
static const GeometricField< Type, GeoMesh, PrimitiveField > & null()
Return a null geometric field.
static word groupName(Name name, const word &group)
A List with indirect addressing.
Definition: IndirectList.H:105
void append(const T &)
Append an element at the end of the list.
Definition: ListI.H:178
void size(const label)
Override size to be inconsistent with allocated storage.
Definition: ListI.H:164
static const List< word > & null()
Return a null List.
Definition: ListI.H:118
const Field< PointType > & faceNormals() const
Return face normals for patch.
A List obtained as a section of another List.
Definition: SubList.H:56
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:76
T & first()
Return the first element of the list.
Definition: UListI.H:114
label size() const
Return the number of elements in the UList.
Definition: UListI.H:311
T & last()
Return the last element of the list.
Definition: UListI.H:128
static const Form zero
Definition: VectorSpace.H:118
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
static std::tuple< const Entries &... > entries(const Entries &...)
Construct an entries tuple from which to make a dictionary.
A class for handling file names.
Definition: fileName.H:82
void calcForcesMoments(const vector &CofR)
Calculate the forces and moments.
Definition: forcesBase.C:781
virtual vector forceEff() const
Return the total force.
Definition: forcesBase.C:917
virtual vector momentEff() const
Return the total moment.
Definition: forcesBase.C:923
Calculates the forces and moments by integrating the pressure and skin-friction forces over a given l...
Definition: forces.H:188
Specialisation of Foam::functionObject for an Foam::fvMesh, providing a reference to the Foam::fvMesh...
const fvMesh & mesh() const
Return a reference to the mesh.
virtual bool read(const dictionary &)
Read optional controls.
Base class for sectional forces function objects.
virtual wordList fields() const
Return the list of fields required.
virtual void topoChange(const polyTopoChangeMap &)
Update topology using the given map.
virtual void distribute(const polyDistributionMap &)
Redistribute or update using the given distribution map.
fileName outputPath() const
Return the output path.
virtual void mapMesh(const polyMeshMap &)
Update from another mesh using the given map.
const indirectPrimitivePatch & patch() const
Access the primitive patch.
virtual void movePoints(const polyMesh &)
Update for mesh point-motion.
void clearPatchGeom()
Clear the patch geometry.
sectionalForcesBase(const word &name, const Time &runTime, const dictionary &dict)
Construct from Time and dictionary.
void addFluid(vectorField &force, vectorField &moment) const
Calculate the forces and moments at the cuts.
virtual void clear()
Clear the cached weights.
const List< patchCutPlot::weight > & weights() const
Access the weights.
virtual bool execute()
Execute, currently does nothing.
tmp< scalarField > patchPointDistances() const
Return the distance from the origin to the patch points.
virtual bool end()
Execute at the final time-loop, currently does nothing.
virtual bool read(const dictionary &)
Read the sectionalForcesBase data.
static const word outputPrefix
Directory prefix.
Definition: writeFile.H:72
const polyMesh & poly() const
Return reference to polyMesh.
Definition: fvMesh.H:456
const Type & lookupObject(const word &name) const
Lookup and return the object of the given Type and name.
const word & name() const
Return name.
labelHashSet patchSet(const UList< wordRe > &patchNames, const bool warnNotFound=true, const bool usePatchGroups=true) const
Return the patch set corresponding to the given names.
Class containing mesh-to-mesh mapping information after a mesh distribution where we send parts of me...
const polyMesh & mesh() const
Return polyMesh.
Class containing mesh-to-mesh mapping information.
Definition: polyMeshMap.H:51
const polyMesh & mesh() const
Return polyMesh.
Definition: polyMeshMap.H:75
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:78
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:260
const polyBoundaryMesh & boundary() const
Return boundary mesh.
Definition: polyMesh.H:393
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:71
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:277
const vectorField::subField faceCentres() const
Return face centres.
Definition: polyPatch.C:227
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
const polyMesh & mesh() const
Return polyMesh.
A class for managing temporary objects.
Definition: tmp.H:55
A class for handling words, derived from string.
Definition: word.H:63
static const word null
An empty word.
Definition: word.H:78
Foam::fvMesh mesh(Foam::IOobject(regionName, runTime.name(), runTime, Foam::IOobject::MUST_READ), false)
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
Calculate the gradient of the given field.
label patchi
const pointField & points
const volScalarField & psi
rho
Definition: pEqn.H:1
tUEqn clear()
volScalarField alpha(IOobject("alpha", runTime.name(), mesh, IOobject::READ_IF_PRESENT, IOobject::AUTO_WRITE), lambda *max(Ua &U, zeroSensitivity))
compressibleMomentumTransportModel momentumTransportModel
tmp< Field< Type > > applyWeights(const label n, const List< cutPlot::weight > &weights, const Field< Type > &cellValues)
Construct plot values from cell values given a set of weights.
const dimensionSet force
const dimensionSet time
defineTypeNameAndDebug(fvMeshFunctionObject, 0)
static tmp< SurfaceField< Type > > interpolate(const VolField< Type > &tvf, const surfaceScalarField &faceFlux, Istream &schemeData)
Interpolate field onto faces using scheme given by Istream.
incompressibleMomentumTransportModel momentumTransportModel
List< weight > calcWeights(const faceList &faces, const UList< vector > &faceAreas, const UList< vector > &faceNormals, const pointField &points, const scalarField &pointXs, const scalarField &faceMinXs, const scalarField &faceMaxXs, const labelList &faceMinOrder, const scalarField &cutXs, const bool interpolate, const bool normalise)
Definition: patchCutPlot.C:397
phaseCompressibleMomentumTransportModel momentumTransportModel
phaseIncompressibleMomentumTransportModel momentumTransportModel
Namespace for OpenFOAM.
Type gMin(const UList< Type > &f, const label comm)
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
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
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
String typeName(const std::type_info &info)
Return the un-mangled name given the standard type info.
messageStream Info
const dimensionSet & dimDensity
Definition: dimensions.C:158
const dimensionSet & dimPressure
Definition: dimensions.C:163
Type gMax(const UList< Type > &f, const label comm)
word name(const LagrangianState state)
Return a string representation of a Lagrangian state enumeration.
error FatalError
labelList identityMap(const label len)
Create identity map (map[i] == i) of given length.
Definition: ListOps.C:104
static const char nl
Definition: Ostream.H:297
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
fileType type(const fileName &, const bool checkVariants=true, const bool followLink=true)
Return the file type: directory or file.
Definition: POSIX.C:488
wordList patchNames(nPatches)
labelList f(nPoints)
dictionary dict
volScalarField & p
Foam::surfaceFields.