rotorDiskSource.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) 2018-2022 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 
29 #include "rotorDiskSource.H"
31 #include "trimModel.H"
32 #include "fvMatrices.H"
33 #include "geometricOneField.H"
34 #include "syncTools.H"
35 #include "unitConversion.H"
36 
37 using namespace Foam::constant;
38 
39 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
40 
41 namespace Foam
42 {
43  namespace fv
44  {
45  defineTypeNameAndDebug(rotorDiskSource, 0);
46  addToRunTimeSelectionTable(option, rotorDiskSource, dictionary);
47  }
48 }
49 
50 
51 const Foam::Enum
52 <
54 >
56 ({
57  { geometryModeType::gmAuto, "auto" },
58  { geometryModeType::gmSpecified, "specified" },
59 });
60 
61 
62 const Foam::Enum
63 <
65 >
67 ({
68  { inletFlowType::ifFixed, "fixed" },
69  { inletFlowType::ifSurfaceNormal, "surfaceNormal" },
70  { inletFlowType::ifLocal, "local" },
71 });
72 
73 
74 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
75 
77 {
78  // Set inflow type
79  switch (selectionMode())
80  {
81  case smCellSet:
82  case smCellZone:
83  case smAll:
84  {
85  // Set the profile ID for each blade section
86  profiles_.connectBlades(blade_.profileName(), blade_.profileID());
87  switch (inletFlow_)
88  {
89  case ifFixed:
90  {
91  coeffs_.readEntry("inletVelocity", inletVelocity_);
92  break;
93  }
94  case ifSurfaceNormal:
95  {
96  scalar UIn(coeffs_.get<scalar>("inletNormalVelocity"));
97  inletVelocity_ = -coordSys_.e3()*UIn;
98  break;
99  }
100  case ifLocal:
101  {
102  break;
103  }
104  default:
105  {
107  << "Unknown inlet velocity type" << abort(FatalError);
108  }
109  }
110 
111  break;
112  }
113  default:
114  {
116  << "Source cannot be used with '"
117  << selectionModeTypeNames_[selectionMode()]
118  << "' mode. Please use one of: " << nl
119  << selectionModeTypeNames_[smCellSet] << nl
120  << selectionModeTypeNames_[smCellZone] << nl
121  << selectionModeTypeNames_[smAll]
122  << exit(FatalError);
123  }
124  }
125 }
126 
127 
129 {
130  area_ = 0.0;
131 
132  static const scalar tol = 0.8;
133 
134  const label nInternalFaces = mesh_.nInternalFaces();
135  const polyBoundaryMesh& pbm = mesh_.boundaryMesh();
136  const vectorField& Sf = mesh_.Sf();
137  const scalarField& magSf = mesh_.magSf();
138 
139  vector n = Zero;
140 
141  // Calculate cell addressing for selected cells
142  labelList cellAddr(mesh_.nCells(), -1);
143  labelUIndList(cellAddr, cells_) = identity(cells_.size());
144  labelList nbrFaceCellAddr(mesh_.nBoundaryFaces(), -1);
145  forAll(pbm, patchi)
146  {
147  const polyPatch& pp = pbm[patchi];
148 
149  if (pp.coupled())
150  {
151  forAll(pp, i)
152  {
153  label facei = pp.start() + i;
154  label nbrFacei = facei - nInternalFaces;
155  label own = mesh_.faceOwner()[facei];
156  nbrFaceCellAddr[nbrFacei] = cellAddr[own];
157  }
158  }
159  }
160 
161  // Correct for parallel running
162  syncTools::swapBoundaryFaceList(mesh_, nbrFaceCellAddr);
163 
164  // Add internal field contributions
165  for (label facei = 0; facei < nInternalFaces; facei++)
166  {
167  const label own = cellAddr[mesh_.faceOwner()[facei]];
168  const label nbr = cellAddr[mesh_.faceNeighbour()[facei]];
169 
170  if ((own != -1) && (nbr == -1))
171  {
172  vector nf = Sf[facei]/magSf[facei];
173 
174  if ((nf & axis) > tol)
175  {
176  area_[own] += magSf[facei];
177  n += Sf[facei];
178  }
179  }
180  else if ((own == -1) && (nbr != -1))
181  {
182  vector nf = Sf[facei]/magSf[facei];
183 
184  if ((-nf & axis) > tol)
185  {
186  area_[nbr] += magSf[facei];
187  n -= Sf[facei];
188  }
189  }
190  }
191 
192 
193  // Add boundary contributions
194  forAll(pbm, patchi)
195  {
196  const polyPatch& pp = pbm[patchi];
197  const vectorField& Sfp = mesh_.Sf().boundaryField()[patchi];
198  const scalarField& magSfp = mesh_.magSf().boundaryField()[patchi];
199 
200  if (pp.coupled())
201  {
202  forAll(pp, j)
203  {
204  const label facei = pp.start() + j;
205  const label own = cellAddr[mesh_.faceOwner()[facei]];
206  const label nbr = nbrFaceCellAddr[facei - nInternalFaces];
207  const vector nf = Sfp[j]/magSfp[j];
208 
209  if ((own != -1) && (nbr == -1) && ((nf & axis) > tol))
210  {
211  area_[own] += magSfp[j];
212  n += Sfp[j];
213  }
214  }
215  }
216  else
217  {
218  forAll(pp, j)
219  {
220  const label facei = pp.start() + j;
221  const label own = cellAddr[mesh_.faceOwner()[facei]];
222  const vector nf = Sfp[j]/magSfp[j];
223 
224  if ((own != -1) && ((nf & axis) > tol))
225  {
226  area_[own] += magSfp[j];
227  n += Sfp[j];
228  }
229  }
230  }
231  }
232 
233  if (correct)
234  {
235  reduce(n, sumOp<vector>());
236  axis = n/mag(n);
237  }
238 
239  if (debug)
240  {
242  (
243  IOobject
244  (
245  name_ + ":area",
246  mesh_.time().timeName(),
247  mesh_,
250  ),
251  mesh_,
253  );
254  UIndirectList<scalar>(area.primitiveField(), cells_) = area_;
255 
256  Info<< type() << ": " << name_ << " writing field " << area.name()
257  << endl;
258 
259  area.write();
260  }
261 }
262 
263 
265 {
266  // Construct the local rotor coordinate system
267  vector origin(Zero);
268  vector axis(Zero);
269  vector refDir(Zero);
270 
271  geometryModeType gm =
272  geometryModeTypeNames_.get("geometryMode", coeffs_);
273 
274  switch (gm)
275  {
276  case gmAuto:
277  {
278  // Determine rotation origin (cell volume weighted)
279  scalar sumV = 0.0;
280  const scalarField& V = mesh_.V();
281  const vectorField& C = mesh_.C();
282  forAll(cells_, i)
283  {
284  const label celli = cells_[i];
285  sumV += V[celli];
286  origin += V[celli]*C[celli];
287  }
288  reduce(origin, sumOp<vector>());
289  reduce(sumV, sumOp<scalar>());
290  origin /= sumV;
291 
292  // Determine first radial vector
293  vector dx1(Zero);
294  scalar magR = -GREAT;
295  forAll(cells_, i)
296  {
297  const label celli = cells_[i];
298  vector test = C[celli] - origin;
299  if (mag(test) > magR)
300  {
301  dx1 = test;
302  magR = mag(test);
303  }
304  }
305  reduce(dx1, maxMagSqrOp<vector>());
306  magR = mag(dx1);
307 
308  // Determine second radial vector and cross to determine axis
309  forAll(cells_, i)
310  {
311  const label celli = cells_[i];
312  vector dx2 = C[celli] - origin;
313  if (mag(dx2) > 0.5*magR)
314  {
315  axis = dx1 ^ dx2;
316  if (mag(axis) > SMALL)
317  {
318  break;
319  }
320  }
321  }
322  reduce(axis, maxMagSqrOp<vector>());
323  axis.normalise();
324 
325  // Correct the axis direction using a point above the rotor
326  {
327  vector pointAbove(coeffs_.get<vector>("pointAbove"));
328  vector dir = pointAbove - origin;
329  dir.normalise();
330  if ((dir & axis) < 0)
331  {
332  axis *= -1.0;
333  }
334  }
335 
336  coeffs_.readEntry("refDirection", refDir);
337 
338  // Set the face areas and apply correction to calculated axis
339  // e.g. if cellZone is more than a single layer in thickness
340  setFaceArea(axis, true);
341 
342  break;
343  }
344  case gmSpecified:
345  {
346  coeffs_.readEntry("origin", origin);
347  coeffs_.readEntry("axis", axis);
348  coeffs_.readEntry("refDirection", refDir);
349 
350  setFaceArea(axis, false);
351 
352  break;
353  }
354  default:
355  {
357  << "Unknown geometryMode " << geometryModeTypeNames_[gm]
358  << ". Available geometry modes include "
359  << geometryModeTypeNames_
360  << exit(FatalError);
361  }
362  }
363 
364  coordSys_ = coordSystem::cylindrical(origin, axis, refDir);
365 
366  const scalar sumArea = gSum(area_);
367  const scalar diameter = Foam::sqrt(4.0*sumArea/mathematical::pi);
368  Info<< " Rotor gometry:" << nl
369  << " - disk diameter = " << diameter << nl
370  << " - disk area = " << sumArea << nl
371  << " - origin = " << coordSys_.origin() << nl
372  << " - r-axis = " << coordSys_.e1() << nl
373  << " - psi-axis = " << coordSys_.e2() << nl
374  << " - z-axis = " << coordSys_.e3() << endl;
375 }
376 
377 
379 {
380  const pointUIndList cc(mesh_.C(), cells_);
381 
382  // Optional: for later transform(), invTransform()
384 
385  forAll(cells_, i)
386  {
387  if (area_[i] > ROOTVSMALL)
388  {
389  // Position in (planar) rotor coordinate system
390  x_[i] = coordSys_.localPosition(cc[i]);
391 
392  // Cache max radius
393  rMax_ = max(rMax_, x_[i].x());
394 
395  // Swept angle relative to rDir axis [radians] in range 0 -> 2*pi
396  scalar psi = x_[i].y();
397 
398  // Blade flap angle [radians]
399  scalar beta =
400  flap_.beta0 - flap_.beta1c*cos(psi) - flap_.beta2s*sin(psi);
401 
402  // Determine rotation tensor to convert from planar system into the
403  // rotor cone system
404  scalar c = cos(beta);
405  scalar s = sin(beta);
406  Rcone_[i] = tensor(c, 0, -s, 0, 1, 0, s, 0, c);
407  }
408  }
409 }
410 
411 
413 (
414  const volVectorField& U
415 ) const
416 {
417  switch (inletFlow_)
418  {
419  case ifFixed:
420  case ifSurfaceNormal:
421  {
422  return tmp<vectorField>::New(mesh_.nCells(), inletVelocity_);
423 
424  break;
425  }
426  case ifLocal:
427  {
428  return U.primitiveField();
429 
430  break;
431  }
432  default:
433  {
435  << "Unknown inlet flow specification" << abort(FatalError);
436  }
437  }
438 
439  return tmp<vectorField>::New(mesh_.nCells(), Zero);
440 }
441 
442 
443 // * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * * //
444 
446 (
447  const word& name,
448  const word& modelType,
449  const dictionary& dict,
450  const fvMesh& mesh
451 
452 )
453 :
454  fv::cellSetOption(name, modelType, dict, mesh),
455  rhoRef_(1.0),
456  omega_(0.0),
457  nBlades_(0),
458  inletFlow_(ifLocal),
459  inletVelocity_(Zero),
460  tipEffect_(1.0),
461  flap_(),
462  x_(cells_.size(), Zero),
463  Rcone_(cells_.size(), I),
464  area_(cells_.size(), Zero),
465  coordSys_(),
466  rMax_(0.0),
467  trim_(trimModel::New(*this, coeffs_)),
468  blade_(coeffs_.subDict("blade")),
469  profiles_(coeffs_.subDict("profiles"))
470 {
472 }
473 
474 
475 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
476 
478 (
479  fvMatrix<vector>& eqn,
480  const label fieldi
481 )
482 {
483  volVectorField force
484  (
485  IOobject
486  (
487  name_ + ":rotorForce",
488  mesh_.time().timeName(),
489  mesh_
490  ),
491  mesh_,
493  );
494 
495  // Read the reference density for incompressible flow
496  coeffs_.readEntry("rhoRef", rhoRef_);
497 
498  const vectorField Uin(inflowVelocity(eqn.psi()));
499  trim_->correct(Uin, force);
500  calculate(geometricOneField(), Uin, trim_->thetag(), force);
501 
502  // Add source to rhs of eqn
503  eqn -= force;
504 
505  if (mesh_.time().writeTime())
506  {
507  force.write();
508  }
509 }
510 
511 
513 (
514  const volScalarField& rho,
515  fvMatrix<vector>& eqn,
516  const label fieldi
517 )
518 {
519  volVectorField force
520  (
521  IOobject
522  (
523  name_ + ":rotorForce",
524  mesh_.time().timeName(),
525  mesh_
526  ),
527  mesh_,
529  );
530 
531  const vectorField Uin(inflowVelocity(eqn.psi()));
532  trim_->correct(rho, Uin, force);
533  calculate(rho, Uin, trim_->thetag(), force);
534 
535  // Add source to rhs of eqn
536  eqn -= force;
537 
538  if (mesh_.time().writeTime())
539  {
540  force.write();
541  }
542 }
543 
544 
546 {
548  {
549  coeffs_.readEntry("fields", fieldNames_);
551 
552  // Read coordinate system/geometry invariant properties
553  omega_ = rpmToRads(coeffs_.get<scalar>("rpm"));
554 
555  coeffs_.readEntry("nBlades", nBlades_);
556 
557  inletFlowTypeNames_.readEntry("inletFlowType", coeffs_, inletFlow_);
558 
559  coeffs_.readEntry("tipEffect", tipEffect_);
560 
561  const dictionary& flapCoeffs(coeffs_.subDict("flapCoeffs"));
562  flap_.beta0 = degToRad(flapCoeffs.get<scalar>("beta0"));
563  flap_.beta1c = degToRad(flapCoeffs.get<scalar>("beta1c"));
564  flap_.beta2s = degToRad(flapCoeffs.get<scalar>("beta2s"));
565 
566 
567  // Create coordinate system
568  createCoordinateSystem();
569 
570  // Read coordinate system dependent properties
571  checkData();
572 
573  constructGeometry();
574 
575  trim_->read(coeffs_);
576 
577  if (debug)
578  {
579  writeField("thetag", trim_->thetag()(), true);
580  writeField("faceArea", area_, true);
581  }
582 
583  return true;
584  }
585 
586  return false;
587 }
588 
589 
590 // ************************************************************************* //
Different types of constants.
const polyBoundaryMesh & pbm
dictionary dict
Graphite solid properties.
Definition: C.H:46
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
A list of keyword definitions, which are a keyword followed by a number of values (eg...
Definition: dictionary.H:129
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
constexpr scalar rpmToRads(const scalar rpm) noexcept
Conversion from revolutions/minute to radians/sec.
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:40
Unit conversion functions.
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
virtual bool read(const dictionary &dict)
Read source dictionary.
Base class for trim models for handling blade characteristics and thrust-torque relations.
Definition: trimModel.H:107
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
dimensioned< vector > dimensionedVector
Dimensioned vector obtained from generic dimensioned type.
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
tmp< DimensionedField< TypeR, GeoMesh > > New(const tmp< DimensionedField< TypeR, GeoMesh >> &tf1, const word &name, const dimensionSet &dimensions, const bool initCopy=false)
Global function forwards to reuseTmpDimensionedField::New.
virtual bool read(const dictionary &dict)
Read source dictionary.
Ignore writing from objectRegistry::writeObject()
bool writeField(ensightOutput::floatBufferType &scratch, ensightFile &os, const Field< Type > &fld, const ensightCells &part, bool parallel)
Write a field of cell values as an indirect list, using the cell ids from ensightCells.
GeometricField< vector, fvPatchField, volMesh > volVectorField
Definition: volFieldsFwd.H:82
Macros for easy insertion into run-time selection tables.
rotorDiskSource(const word &name, const word &modelType, const dictionary &dict, const fvMesh &mesh)
Construct from components.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
void constructGeometry()
Construct geometry.
GeometricField< scalar, fvPatchField, volMesh > volScalarField
Definition: volFieldsFwd.H:81
geometryModeType
Options for the geometry type specification.
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
const dimensionSet dimVolume(pow3(dimLength))
Definition: dimensionSets.H:58
const GeometricField< Type, fvPatchField, volMesh > & psi(const label i=0) const
Return psi.
Definition: fvMatrix.H:485
C()
Construct null.
Definition: C.C:36
dynamicFvMesh & mesh
A class representing the concept of a GeometricField of 1 used to avoid unnecessary manipulations for...
Type gSum(const FieldField< Field, Type > &f)
dimensionedScalar cos(const dimensionedScalar &ds)
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
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
const wordList area
Standard area field types (scalar, vector, tensor, etc)
label checkData(const fvMesh &mesh, const instantList &timeDirs, wordList &objectNames)
Check if fields are good to use (available at all times)
A class for handling words, derived from Foam::string.
Definition: word.H:63
labelList fv(nPoints)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
static const Enum< inletFlowType > inletFlowTypeNames_
Names for inletFlowType.
static tmp< T > New(Args &&... args)
Construct tmp with forwarding arguments.
Definition: tmp.H:206
constexpr scalar pi(M_PI)
A special matrix type and solver, designed for finite volume solutions of scalar equations. Face addressing is used to make all matrix assembly and solution loops vectorise.
Definition: fvPatchField.H:64
Vector< scalar > vector
Definition: vector.H:57
errorManip< error > abort(error &err)
Definition: errorManip.H:139
dimensionedScalar sin(const dimensionedScalar &ds)
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
virtual void addSup(fvMatrix< vector > &eqn, const label fieldi)
Add explicit contribution to momentum equation.
inletFlowType
Options for the inlet flow type specification.
int debug
Static debugging option.
Info<< "Predicted p max-min : "<< max(p).value()<< " "<< min(p).value()<< endl;rho==max(psi *p+alphal *rhol0+((alphav *psiv+alphal *psil) - psi) *pSat, rhoMin);# 1 "/home/chef2/andy/OpenFOAM/release/v2312/OpenFOAM-v2312/applications/solvers/multiphase/cavitatingFoam/alphavPsi.H" 1{ alphav=clamp((rho - rholSat)/(rhovSat - rholSat), zero_one{});alphal=1.0 - alphav;Info<< "max-min alphav: "<< max(alphav).value()<< " "<< min(alphav).value()<< endl;psiModel-> correct()
Definition: pEqn.H:63
defineTypeNameAndDebug(combustionModel, 0)
void checkData()
Check data.
U
Definition: pEqn.H:72
const dimensionSet & dimensions() const noexcept
Definition: fvMatrix.H:528
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: error.H:64
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
static const Enum< geometryModeType > geometryModeTypeNames_
Names for geometryModeType.
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:78
A List with indirect addressing. Like IndirectList but does not store addressing. ...
Definition: faMatrix.H:56
const dimensionedScalar c
Speed of light in a vacuum.
void resetApplied()
Resize/reset applied flag list for all fieldNames_ entries.
Definition: fvOption.C:41
Nothing to be read.
A special matrix type and solver, designed for finite volume solutions of scalar equations.
void reduce(const List< UPstream::commsStruct > &comms, T &value, const BinaryOp &bop, const int tag, const label comm)
Reduce inplace (cf. MPI Allreduce) using specified communication schedule.
messageStream Info
Information stream (stdout output on master, null elsewhere)
dimensionedScalar beta("beta", dimless/dimTemperature, laminarTransport)
label n
Field< vector > vectorField
Specialisation of Field<T> for vector.
Intermediate abstract class for handling cell-set options for the derived fvOptions.
const volScalarField & psi
List< label > labelList
A List of labels.
Definition: List.H:62
void setFaceArea(vector &axis, const bool correct)
Set the face areas per cell, and optionally correct the rotor axis.
A class for managing temporary objects.
Definition: HashPtrTable.H:50
gmvFile<< "tracers "<< particles.size()<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().x()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().y()<< " ";}gmvFile<< nl;for(const passiveParticle &p :particles){ gmvFile<< p.position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
void createCoordinateSystem()
Create the coordinate system.
tmp< vectorField > inflowVelocity(const volVectorField &U) const
Return the inlet flow field.
const dimensionSet dimArea(sqr(dimLength))
Definition: dimensionSets.H:57
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues)
Swap coupled boundary face values. Uses eqOp.
Definition: syncTools.H:485
addToRunTimeSelectionTable(functionObject, pointHistory, dictionary)
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127