surfaceFieldValueTemplates.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-2017 OpenFOAM Foundation
9  Copyright (C) 2015-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 
29 #include "surfaceFieldValue.H"
30 #include "surfaceFields.H"
31 #include "polySurfaceFields.H"
32 #include "volFields.H"
33 #include "sampledSurface.H"
34 #include "surfaceWriter.H"
35 #include "interpolationCell.H"
37 
38 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
39 
40 template<class WeightType>
43 (
44  const Field<WeightType>& weightField,
45  const bool useMag /* ignore */
46 )
47 {
48  // The scalar form is specialized.
49  // Other types: use mag() to generate a scalar field.
50  return mag(weightField);
51 }
52 
53 
54 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
55 
56 template<class WeightType>
58 (
59  const Field<WeightType>& fld
60 ) const
61 {
62  // Non-empty on some processor
63  return returnReduceOr(!fld.empty());
64 }
65 
66 
67 template<class Type>
69 (
70  const word& fieldName
71 ) const
72 {
76 
77  return
78  (
79  foundObject<smt>(fieldName)
80  || foundObject<vf>(fieldName)
81  || (withSurfaceFields() && foundObject<sf>(fieldName))
82  );
83 }
84 
85 
86 template<class Type>
89 (
90  const word& fieldName,
91  const bool mandatory
92 ) const
93 {
97 
98  if (foundObject<smt>(fieldName))
99  {
100  return lookupObject<smt>(fieldName);
101  }
102  else if (withSurfaceFields() && foundObject<sf>(fieldName))
103  {
104  return filterField(lookupObject<sf>(fieldName));
105  }
106  else if (foundObject<vf>(fieldName))
107  {
108  const vf& fld = lookupObject<vf>(fieldName);
109 
110  if (sampledPtr_)
111  {
112  // Could be runtime selectable
113  // auto sampler = interpolation<Type>::New(sampleFaceScheme_, fld);
114 
115  // const interpolationCellPoint<Type> interp(fld);
116  const interpolationCell<Type> interp(fld);
117 
118  return sampledPtr_->sample(interp);
119  }
120  else
121  {
122  return filterField(fld);
123  }
124  }
125 
126  if (mandatory)
127  {
129  << "Field " << fieldName << " not found in database" << nl
130  << abort(FatalError);
131  }
132 
134 }
135 
136 
137 template<class Type, class WeightType>
140 (
141  const Field<Type>& values,
142  const vectorField& Sf,
143  const Field<WeightType>& weightField
144 ) const
145 {
146  Type result = Zero;
147  switch (operation_)
148  {
149  case opNone:
150  case typeScalar:
151  case typeWeighted:
152  case typeAbsolute:
153  {
154  break;
155  }
156  case opMin:
157  {
158  result = gMin(values);
159  break;
160  }
161  case opMax:
162  {
163  result = gMax(values);
164  break;
165  }
166  case opSumMag:
167  {
168  result = gSum(cmptMag(values));
169  break;
170  }
171  case opSum:
172  case opWeightedSum:
173  case opAbsWeightedSum:
174  {
175  if (is_weightedOp() && canWeight(weightField))
176  {
177  tmp<scalarField> weight
178  (
179  weightingFactor(weightField, Sf, is_magOp())
180  );
181 
182  result = gSum(weight*values);
183  }
184  else
185  {
186  // Unweighted form
187  result = gSum(values);
188  }
189  break;
190  }
191  case opSumDirection:
192  case opSumDirectionBalance:
193  {
195  << "Operation " << operationTypeNames_[operation_]
196  << " not available for values of type "
197  << pTraits<Type>::typeName
198  << exit(FatalError);
199 
200  break;
201  }
202  case opAverage:
203  case opWeightedAverage:
204  case opAbsWeightedAverage:
205  {
206  if (is_weightedOp() && canWeight(weightField))
207  {
208  const scalarField factor
209  (
210  weightingFactor(weightField, Sf, is_magOp())
211  );
212 
213  result = gSum(factor*values)/(gSum(factor) + ROOTVSMALL);
214  }
215  else
216  {
217  // Unweighted form
218  const label n = returnReduce(values.size(), sumOp<label>());
219 
220  result = gSum(values)/(scalar(n) + ROOTVSMALL);
221  }
222  break;
223  }
224  case opAreaAverage:
225  case opWeightedAreaAverage:
226  case opAbsWeightedAreaAverage:
227  {
228  if (is_weightedOp() && canWeight(weightField))
229  {
230  const scalarField factor
231  (
232  areaWeightingFactor(weightField, Sf, is_magOp())
233  );
234 
235  result = gSum(factor*values)/gSum(factor + ROOTVSMALL);
236  }
237  else
238  {
239  // Unweighted form
240  const scalarField factor(mag(Sf));
241 
242  result = gSum(factor*values)/gSum(factor + ROOTVSMALL);
243  }
244  break;
245  }
246  case opAreaIntegrate:
247  case opWeightedAreaIntegrate:
248  case opAbsWeightedAreaIntegrate:
249  {
250  if (is_weightedOp() && canWeight(weightField))
251  {
252  tmp<scalarField> factor
253  (
254  areaWeightingFactor(weightField, Sf, is_magOp())
255  );
256 
257  result = gSum(factor*values);
258  }
259  else
260  {
261  // Unweighted form
262  tmp<scalarField> factor(mag(Sf));
263 
264  result = gSum(factor*values);
265  }
266  break;
267  }
268  case opCoV:
269  {
270  const scalarField magSf(mag(Sf));
271  const scalar gSumMagSf = gSum(magSf);
272 
273  Type meanValue = gSum(values*magSf)/gSumMagSf;
274 
276  {
277  tmp<scalarField> vals(values.component(d));
278  const scalar mean = component(meanValue, d);
279  scalar& res = setComponent(result, d);
280 
281  res =
282  sqrt(gSum(magSf*sqr(vals - mean))/gSumMagSf)
283  /(mean + ROOTVSMALL);
284  }
285 
286  break;
287  }
288 
289  case opAreaNormalAverage:
290  case opAreaNormalIntegrate:
291  case opUniformity:
292  {
293  // Handled in specializations only
294  break;
295  }
296 
297  case opWeightedUniformity:
298  case opAbsWeightedUniformity:
299  {
300  if (is_weightedOp() && canWeight(weightField))
301  {
302  // Change weighting from vector -> scalar and dispatch again
303  return processValues<Type, scalar>
304  (
305  values,
306  Sf,
307  weightingFactor(weightField, is_magOp())
308  );
309  }
310 
311  break;
312  }
313  }
315  return result;
316 }
317 
318 
319 template<class Type, class WeightType>
321 (
322  const Field<Type>& values,
323  const vectorField& Sf,
324  const Field<WeightType>& weightField
325 ) const
326 {
327  return processSameTypeValues(values, Sf, weightField);
328 }
329 
330 
331 template<class WeightType>
333 (
334  const vectorField& Sf,
335  const Field<WeightType>& weightField,
336  const pointField& points,
337  const faceList& faces
338 )
339 {
340  label nProcessed = 0;
341 
342  // If using the surface writer, the points/faces parameters have already
343  // been merged on the master and the writeValues routine will also gather
344  // all data onto the master before calling the writer.
345  // Thus only call the writer on master!
346 
347  // Begin writer time step
348  if (Pstream::master() && surfaceWriterPtr_ && surfaceWriterPtr_->enabled())
349  {
350  auto& writer = *surfaceWriterPtr_;
351 
352  writer.open
353  (
354  points,
355  faces,
356  (
357  outputDir()
358  / regionTypeNames_[regionType_] + ("_" + regionName_)
359  ),
360  false // serial - already merged
361  );
362 
363  writer.beginTime(time_);
364  }
365 
366  for (const word& fieldName : fields_)
367  {
368  if
369  (
370  writeValues<scalar>(fieldName, Sf, weightField, points, faces)
371  || writeValues<vector>(fieldName, Sf, weightField, points, faces)
372  || writeValues<sphericalTensor>
373  (
374  fieldName, Sf, weightField, points, faces
375  )
376  || writeValues<symmTensor>(fieldName, Sf, weightField, points, faces)
377  || writeValues<tensor>(fieldName, Sf, weightField, points, faces)
378  )
379  {
380  ++nProcessed;
381  }
382  else
383  {
385  << "Requested field " << fieldName
386  << " not found in database and not processed"
387  << endl;
388  }
389  }
390 
391  // Finish writer time step
392  if (Pstream::master() && surfaceWriterPtr_ && surfaceWriterPtr_->enabled())
393  {
394  auto& writer = *surfaceWriterPtr_;
395 
396  // Write geometry if no fields were written so that we still
397  // can have something to look at.
398 
399  if (!writer.wroteData())
400  {
401  writer.write();
402  }
403 
404  writer.endTime();
405  writer.clear();
406  }
408  return nProcessed;
409 }
410 
411 
412 template<class Type, class WeightType>
414 (
415  const word& fieldName,
416  const vectorField& Sf,
417  const Field<WeightType>& weightField,
418  const pointField& points,
419  const faceList& faces
420 )
421 {
422  const bool ok = validField<Type>(fieldName);
423 
424  if (ok)
425  {
426  Field<Type> values(getFieldValues<Type>(fieldName, true));
427 
428  // Write raw values on surface if specified
429  if (surfaceWriterPtr_ && surfaceWriterPtr_->enabled())
430  {
431  Field<Type> allValues(values);
432  combineFields(allValues);
433 
434  if (Pstream::master())
435  {
437  surfaceWriterPtr_->write(fieldName, allValues);
438 
439  // Case-local file name with "<case>" to make relocatable
441  propsDict.add("file", time_.relativePath(outputName, true));
442  this->setProperty(fieldName, propsDict);
443  }
444  }
445 
446  if (operation_ != opNone)
447  {
448  // Apply scale factor
449  values *= scaleFactor_;
450 
451  Type result = processValues(values, Sf, weightField);
452 
453  switch (postOperation_)
454  {
455  case postOpSqrt:
456  {
457  // sqrt: component-wise - does not change the type
459  {
460  setComponent(result, d)
461  = sqrt(mag(component(result, d)));
462  }
463  break;
464  }
465  default:
466  {
467  break;
468  }
469  }
470 
471  // Write state/results information
472  word prefix, suffix;
473  {
474  if (postOperation_ != postOpNone)
475  {
476  // Adjust result name to include post-operation
477  prefix += postOperationTypeNames_[postOperation_];
478  prefix += '(';
479  suffix += ')';
480  }
481 
482  prefix += operationTypeNames_[operation_];
483  prefix += '(';
484  suffix += ')';
485  }
486 
487  word resultName = prefix + regionName_ + ',' + fieldName + suffix;
488 
489  // Write state/results information
490 
491  Log << " " << prefix << regionName_ << suffix
492  << " of " << fieldName << " = ";
493 
494 
495  // Operation or post-operation returns scalar?
496 
497  scalar sresult{0};
498 
499  bool alwaysScalar(operation_ & typeScalar);
500 
501  if (alwaysScalar)
502  {
503  sresult = component(result, 0);
504 
505  if (postOperation_ == postOpMag)
506  {
507  sresult = mag(sresult);
508  }
509  }
510  else if (postOperation_ == postOpMag)
511  {
512  sresult = mag(result);
513  alwaysScalar = true;
514  }
515 
516 
517  if (alwaysScalar)
518  {
519  file()<< tab << sresult;
520 
521  Log << sresult << endl;
522 
523  this->setResult(resultName, sresult);
524  }
525  else
526  {
527  file()<< tab << result;
528 
529  Log << result << endl;
530 
531  this->setResult(resultName, result);
532  }
533  }
534  }
535 
536  return ok;
537 }
538 
539 
540 template<class Type>
543 (
545 ) const
546 {
547  const labelList& own = field.mesh().faceOwner();
548  const labelList& nei = field.mesh().faceNeighbour();
549 
550  auto tvalues = tmp<Field<Type>>::New(faceId_.size());
551  auto& values = tvalues.ref();
552 
553  forAll(values, i)
554  {
555  const label facei = faceId_[i];
556  const label patchi = facePatchId_[i];
557 
558  if (patchi >= 0)
559  {
560  // Boundary face - face id is the patch-local face id
561  values[i] = field.boundaryField()[patchi][facei];
562  }
563  else
564  {
565  // Internal face
566  values[i] = 0.5*(field[own[facei]] + field[nei[facei]]);
567  }
568  }
569 
570  // No need to flip values - all boundary faces point outwards
571 
572  return tvalues;
573 }
574 
575 
576 template<class Type>
579 (
581 ) const
582 {
583  auto tvalues = tmp<Field<Type>>::New(faceId_.size());
584  auto& values = tvalues.ref();
585 
586  forAll(values, i)
587  {
588  const label facei = faceId_[i];
589  const label patchi = facePatchId_[i];
590 
591  if (patchi >= 0)
592  {
593  values[i] = field.boundaryField()[patchi][facei];
594  }
595  else
596  {
597  values[i] = field[facei];
598  }
599  }
600 
601  if (debug)
602  {
603  Pout<< "field " << field.name() << " oriented: "
604  << field.is_oriented() << endl;
605  }
606 
607  if (field.is_oriented())
608  {
609  forAll(values, i)
610  {
611  if (faceFlip_[i])
612  {
613  values[i] *= -1;
614  }
615  }
616  }
617 
618  return tvalues;
619 }
620 
621 
622 // ************************************************************************* //
Foam::surfaceFields.
rDeltaTY field()
uint8_t direction
Definition: direction.H:46
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
vtk::lineWriter writer(edgeCentres, edgeList::null(), fileName(aMesh.time().globalPath()/"finiteArea-edgesCentres"))
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
Type gMin(const FieldField< Field, Type > &f)
dimensionedSymmTensor sqr(const dimensionedVector &dv)
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:50
Fields (face and point) for polySurface.
dimensionedScalar sqrt(const dimensionedScalar &ds)
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.
constexpr char tab
The tab &#39;\t&#39; character(0x09)
Definition: Ostream.H:49
Generic GeometricField class.
Definition: areaFieldsFwd.H:50
::Foam::direction nComponents(const expressions::valueTypeCode) noexcept
The number of components associated with given valueTypeCode.
Definition: exprTraits.C:40
T returnReduce(const T &value, const BinaryOp &bop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Perform reduction on a copy, using specified binary operation.
tmp< Field< Type > > getFieldValues(const word &fieldName, const bool mandatory=false) const
Return field values by looking up field name.
IOdictionary propsDict(dictIO)
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:164
word outputName("finiteArea-edges.obj")
Type gSum(const FieldField< Field, Type > &f)
Type processSameTypeValues(const Field< Type > &values, const vectorField &Sf, const Field< WeightType > &weightField) const
Apply the &#39;operation&#39; to the values. Operation must preserve Type.
const pointField & points
label writeAll(const vectorField &Sf, const Field< WeightType > &weightField, const pointField &points, const faceList &faces)
Templated helper function to output field values.
Generic templated field type.
Definition: Field.H:62
A class for handling words, derived from Foam::string.
Definition: word.H:63
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
bool validField(const word &fieldName) const
Return true if the field name is known and a valid type.
tmp< Field< Type > > filterField(const GeometricField< Type, fvsPatchField, surfaceMesh > &field) const
Filter a surface field according to faceIds.
errorManip< error > abort(error &err)
Definition: errorManip.H:139
void cmptMag(FieldField< Field, Type > &cf, const FieldField< Field, Type > &f)
int debug
Static debugging option.
Type gMax(const FieldField< Field, Type > &f)
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;for(const word &name :lagrangianScalarNames){ IOField< scalar > fld(IOobject(name, runTime.timeName(), cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
#define WarningInFunction
Report a warning using Foam::Warning.
bool writeValues(const word &fieldName, const vectorField &Sf, const Field< WeightType > &weightField, const pointField &points, const faceList &faces)
Templated helper function to output field values.
Type processValues(const Field< Type > &values, const vectorField &Sf, const Field< WeightType > &weightField) const
Apply the &#39;operation&#39; to the values. Wrapper around.
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
Definition: areaFieldsFwd.H:42
static tmp< scalarField > weightingFactor(const Field< WeightType > &weightField, const bool useMag)
Weighting factor.
#define Log
Definition: PDRblock.C:28
label n
bool canWeight(const Field< WeightType > &fld) const
True if field is non-empty on any processor.
A class for managing temporary objects.
Definition: HashPtrTable.H:50
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
label & setComponent(label &val, const direction) noexcept
Non-const access to integer-type (has no components)
Definition: label.H:144
void component(FieldField< Field, typename FieldField< Field, Type >::cmptType > &sf, const FieldField< Field, Type > &f, const direction d)
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Uses the cell value for any location within the cell.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127