surfaceFieldValue.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) 2017-2020 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 "fvMesh.H"
31 #include "emptyPolyPatch.H"
32 #include "coupledPolyPatch.H"
33 #include "sampledSurface.H"
34 #include "mergePoints.H"
36 #include "PatchTools.H"
38 
39 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
40 
41 namespace Foam
42 {
43 namespace functionObjects
44 {
45 namespace fieldValues
46 {
47  defineTypeNameAndDebug(surfaceFieldValue, 0);
48  addToRunTimeSelectionTable(fieldValue, surfaceFieldValue, dictionary);
49  addToRunTimeSelectionTable(functionObject, surfaceFieldValue, dictionary);
50 }
51 }
52 }
53 
54 
55 const Foam::Enum
56 <
58 >
60 ({
61  { regionTypes::stFaceZone, "faceZone" },
62  { regionTypes::stPatch, "patch" },
63  { regionTypes::stObject, "functionObjectSurface" },
64  { regionTypes::stSampled, "sampledSurface" },
65 });
66 
67 
68 const Foam::Enum
69 <
71 >
73 ({
74  // Normal operations
75  { operationType::opNone, "none" },
76  { operationType::opMin, "min" },
77  { operationType::opMax, "max" },
78  { operationType::opSum, "sum" },
79  { operationType::opSumMag, "sumMag" },
80  { operationType::opSumDirection, "sumDirection" },
81  { operationType::opSumDirectionBalance, "sumDirectionBalance" },
82  { operationType::opAverage, "average" },
83  { operationType::opAreaAverage, "areaAverage" },
84  { operationType::opAreaIntegrate, "areaIntegrate" },
85  { operationType::opCoV, "CoV" },
86  { operationType::opAreaNormalAverage, "areaNormalAverage" },
87  { operationType::opAreaNormalIntegrate, "areaNormalIntegrate" },
88  { operationType::opUniformity, "uniformity" },
89 
90  // Using weighting
91  { operationType::opWeightedSum, "weightedSum" },
92  { operationType::opWeightedAverage, "weightedAverage" },
93  { operationType::opWeightedAreaAverage, "weightedAreaAverage" },
94  { operationType::opWeightedAreaIntegrate, "weightedAreaIntegrate" },
95  { operationType::opWeightedUniformity, "weightedUniformity" },
96 
97  // Using absolute weighting
98  { operationType::opAbsWeightedSum, "absWeightedSum" },
99  { operationType::opAbsWeightedAverage, "absWeightedAverage" },
100  { operationType::opAbsWeightedAreaAverage, "absWeightedAreaAverage" },
101  { operationType::opAbsWeightedAreaIntegrate, "absWeightedAreaIntegrate" },
102  { operationType::opAbsWeightedUniformity, "absWeightedUniformity" },
103 });
104 
105 const Foam::Enum
106 <
108 >
110 ({
111  { postOperationType::postOpNone, "none" },
112  { postOperationType::postOpMag, "mag" },
113  { postOperationType::postOpSqrt, "sqrt" },
114 });
115 
116 
117 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
118 
121 {
122  if (stObject == regionType_)
123  {
125  }
126 
127  return mesh_;
128 }
129 
130 
131 void Foam::functionObjects::fieldValues::surfaceFieldValue::setFaceZoneFaces()
132 {
133  // Indices for all matches, already sorted
134  const labelList zoneIds
135  (
136  mesh_.faceZones().indices(selectionNames_)
137  );
138 
139  // Total number of faces selected
140  label numFaces = 0;
141  for (const label zoneId : zoneIds)
142  {
143  numFaces += mesh_.faceZones()[zoneId].size();
144  }
145 
146  if (zoneIds.empty())
147  {
149  << type() << ' ' << name() << ": "
150  << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
151  << " No matching face zone(s): "
152  << flatOutput(selectionNames_) << nl
153  << " Known face zones: "
154  << flatOutput(mesh_.faceZones().names()) << nl
155  << exit(FatalError);
156  }
157 
158  // Could also check this
159  #if 0
160  if (!returnReduce(bool(numFaces), orOp<bool>()))
161  {
163  << type() << ' ' << name() << ": "
164  << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
165  << " The faceZone specification: "
166  << flatOutput(selectionNames_) << nl
167  << " resulted in 0 faces" << nl
168  << exit(FatalError);
169  }
170  #endif
171 
172  faceId_.resize(numFaces);
173  facePatchId_.resize(numFaces);
174  faceFlip_.resize(numFaces);
175 
176  numFaces = 0;
177 
178  for (const label zoneId : zoneIds)
179  {
180  const faceZone& fZone = mesh_.faceZones()[zoneId];
181 
182  forAll(fZone, i)
183  {
184  const label meshFacei = fZone[i];
185  const bool isFlip = fZone.flipMap()[i];
186 
187  // Internal faces
188  label faceId = meshFacei;
189  label facePatchId = -1;
190 
191  // Boundary faces
192  if (!mesh_.isInternalFace(meshFacei))
193  {
194  facePatchId = mesh_.boundaryMesh().whichPatch(meshFacei);
195  const polyPatch& pp = mesh_.boundaryMesh()[facePatchId];
196 
197  if (isA<coupledPolyPatch>(pp))
198  {
199  if (refCast<const coupledPolyPatch>(pp).owner())
200  {
201  faceId = pp.whichFace(meshFacei);
202  }
203  else
204  {
205  faceId = -1;
206  }
207  }
208  else if (!isA<emptyPolyPatch>(pp))
209  {
210  faceId = meshFacei - pp.start();
211  }
212  else
213  {
214  faceId = -1;
215  facePatchId = -1;
216  }
217  }
218 
219  if (faceId >= 0)
220  {
221  faceId_[numFaces] = faceId;
222  facePatchId_[numFaces] = facePatchId;
223  faceFlip_[numFaces] = isFlip;
224 
225  ++numFaces;
226  }
227  }
228  }
229 
230  // Shrink to size used
231  faceId_.resize(numFaces);
232  facePatchId_.resize(numFaces);
233  faceFlip_.resize(numFaces);
234  nFaces_ = returnReduce(faceId_.size(), sumOp<label>());
235 }
236 
237 
238 void Foam::functionObjects::fieldValues::surfaceFieldValue::setPatchFaces()
239 {
240  // Patch indices for all matches
242 
243  // Total number of faces selected
244  label numFaces = 0;
245 
246  labelList selected
247  (
248  mesh_.boundaryMesh().patchSet
249  (
250  selectionNames_,
251  false // warnNotFound - we do that ourselves
252  ).sortedToc()
253  );
254 
255  DynamicList<label> bad;
256  for (const label patchi : selected)
257  {
258  const polyPatch& pp = mesh_.boundaryMesh()[patchi];
259 
260  if (isA<emptyPolyPatch>(pp))
261  {
262  bad.append(patchi);
263  }
264  else
265  {
266  numFaces += pp.size();
267  }
268  }
269 
270  if (bad.size())
271  {
272  label nGood = (selected.size() - bad.size());
273 
274  auto& os = (nGood > 0 ? WarningInFunction : FatalErrorInFunction);
275 
276  os << "Cannot sample an empty patch" << nl;
277 
278  for (const label patchi : bad)
279  {
280  os << " "
281  << mesh_.boundaryMesh()[patchi].name() << nl;
282  }
283 
284  if (nGood)
285  {
286  os << "No non-empty patches selected" << endl
287  << exit(FatalError);
288  }
289  else
290  {
291  os << "Selected " << nGood << " non-empty patches" << nl;
292  }
293 
294  patchIds.resize(nGood);
295  nGood = 0;
296  for (const label patchi : selected)
297  {
298  if (!bad.found(patchi))
299  {
300  patchIds[nGood] = patchi;
301  ++nGood;
302  }
303  }
304  }
305  else
306  {
307  patchIds = std::move(selected);
308  }
309 
310  if (patchIds.empty())
311  {
313  << type() << ' ' << name() << ": "
314  << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
315  << " No matching patch name(s): "
316  << flatOutput(selectionNames_) << nl
317  << " Known patch names:" << nl
318  << mesh_.boundaryMesh().names() << nl
319  << exit(FatalError);
320  }
321 
322  // Could also check this
323  #if 0
324  if (!returnReduce(bool(numFaces), orOp<bool>()))
325  {
327  << type() << ' ' << name() << ": "
328  << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
329  << " The patch specification: "
330  << flatOutput(selectionNames_) << nl
331  << " resulted in 0 faces" << nl
332  << exit(FatalError);
333  }
334  #endif
335 
336  faceId_.resize(numFaces);
337  facePatchId_.resize(numFaces);
338  faceFlip_.resize(numFaces);
339  nFaces_ = returnReduce(faceId_.size(), sumOp<label>());
340 
341  numFaces = 0;
342  for (const label patchi : patchIds)
343  {
344  const polyPatch& pp = mesh_.boundaryMesh()[patchi];
345  const label len = pp.size();
346 
347  SubList<label>(faceId_, len, numFaces) = identity(len);
348  SubList<label>(facePatchId_, len, numFaces) = patchi;
349  SubList<bool>(faceFlip_, len, numFaces) = false;
350 
351  numFaces += len;
352  }
353 }
354 
355 
356 void Foam::functionObjects::fieldValues::surfaceFieldValue::combineMeshGeometry
357 (
358  faceList& faces,
360 ) const
361 {
362  List<faceList> allFaces(Pstream::nProcs());
363  List<pointField> allPoints(Pstream::nProcs());
364 
365  {
366  IndirectList<face> selectedFaces(mesh_.faces(), labelList(faceId_));
367  labelList& meshFaceIds = selectedFaces.addressing();
368 
369  forAll(meshFaceIds, i)
370  {
371  const label patchi = facePatchId_[i];
372  if (patchi != -1)
373  {
374  meshFaceIds[i] += mesh_.boundaryMesh()[patchi].start();
375  }
376  }
377 
378  // Add local faces and points to the all* lists
379  uindirectPrimitivePatch pp(selectedFaces, mesh_.points());
380 
381  allFaces[Pstream::myProcNo()] = pp.localFaces();
382  allPoints[Pstream::myProcNo()] = pp.localPoints();
383  }
384 
385  Pstream::gatherList(allFaces);
387 
388  // Renumber and flatten
389  label nFaces = 0;
390  label nPoints = 0;
391  forAll(allFaces, proci)
392  {
393  nFaces += allFaces[proci].size();
394  nPoints += allPoints[proci].size();
395  }
396 
397  faces.resize(nFaces);
398  points.resize(nPoints);
399 
400  nFaces = 0;
401  nPoints = 0;
402 
403  // My data first
404  {
405  for (const face& f : allFaces[Pstream::myProcNo()])
406  {
407  faces[nFaces++] = offsetOp<face>()(f, nPoints);
408  }
409 
410  for (const point& p : allPoints[Pstream::myProcNo()])
411  {
412  points[nPoints++] = p;
413  }
414  }
415 
416  // Other proc data follows
417  forAll(allFaces, proci)
418  {
419  if (proci == Pstream::myProcNo())
420  {
421  continue;
422  }
423 
424  for (const face& f : allFaces[proci])
425  {
426  faces[nFaces++] = offsetOp<face>()(f, nPoints);
427  }
428 
429  for (const point& p : allPoints[proci])
430  {
431  points[nPoints++] = p;
432  }
433  }
434 
435  // Merge
436  labelList oldToNew;
437  pointField newPoints;
438  bool hasMerged = mergePoints
439  (
440  points,
441  SMALL,
442  false,
443  oldToNew,
444  newPoints
445  );
446 
447  if (hasMerged)
448  {
449  if (debug)
450  {
451  Pout<< "Merged from " << points.size()
452  << " down to " << newPoints.size() << " points" << endl;
453  }
454 
455  points.transfer(newPoints);
456  for (face& f : faces)
457  {
458  inplaceRenumber(oldToNew, f);
459  }
460  }
461 }
462 
463 
464 void Foam::functionObjects::fieldValues::surfaceFieldValue::
465 combineSurfaceGeometry
466 (
467  faceList& faces,
469 ) const
470 {
471  if (stObject == regionType_)
472  {
473  const polySurface& s = dynamicCast<const polySurface>(obr());
474 
475  if (Pstream::parRun())
476  {
477  // Dimension as fraction of surface
478  const scalar mergeDim = 1e-10*boundBox(s.points(), true).mag();
479 
480  labelList pointsMap;
481 
483  (
484  mergeDim,
485  primitivePatch(SubList<face>(s.faces()), s.points()),
486  points,
487  faces,
488  pointsMap
489  );
490  }
491  else
492  {
493  faces = s.faces();
494  points = s.points();
495  }
496  }
497  else if (sampledPtr_)
498  {
499  const sampledSurface& s = *sampledPtr_;
500 
501  if (Pstream::parRun())
502  {
503  // Dimension as fraction of mesh bounding box
504  const scalar mergeDim = 1e-10*mesh_.bounds().mag();
505 
506  labelList pointsMap;
507 
509  (
510  mergeDim,
511  primitivePatch(SubList<face>(s.faces()), s.points()),
512  points,
513  faces,
514  pointsMap
515  );
516  }
517  else
518  {
519  faces = s.faces();
520  points = s.points();
521  }
522  }
523 }
524 
525 
526 Foam::scalar
527 Foam::functionObjects::fieldValues::surfaceFieldValue::totalArea() const
528 {
529  scalar totalArea = 0;
530 
531  if (stObject == regionType_)
532  {
533  const polySurface& s = dynamicCast<const polySurface>(obr());
534 
535  totalArea = gSum(s.magSf());
536  }
537  else if (sampledPtr_)
538  {
539  totalArea = gSum(sampledPtr_->magSf());
540  }
541  else
542  {
543  totalArea = gSum(filterField(mesh_.magSf()));
544  }
545 
546  return totalArea;
547 }
548 
549 
550 // * * * * * * * * * * * * Protected Member Functions * * * * * * * * * * * //
551 
553 {
554  // Only a few operations do not require the Sf field
555  switch (operation_)
556  {
557  case opNone:
558  case opMin:
559  case opMax:
560  case opSum:
561  case opSumMag:
562  case opAverage:
563  {
564  return false;
565  }
566  default:
567  {
568  return true;
569  }
570  }
571 }
572 
573 
575 {
576  if (sampledPtr_)
577  {
578  sampledPtr_->update();
579  }
580 
581  if (!needsUpdate_)
582  {
583  return false;
584  }
585 
586  switch (regionType_)
587  {
588  case stFaceZone:
589  {
590  setFaceZoneFaces();
591  break;
592  }
593  case stPatch:
594  {
595  setPatchFaces();
596  break;
597  }
598  case stObject:
599  {
600  const polySurface& s = dynamicCast<const polySurface>(obr());
601  nFaces_ = returnReduce(s.size(), sumOp<label>());
602  break;
603  }
604  case stSampled:
605  {
606  nFaces_ = returnReduce(sampledPtr_->faces().size(), sumOp<label>());
607  break;
608  }
609 
610  // Compiler warning if we forgot an enumeration
611  }
612 
613  if (nFaces_ == 0)
614  {
616  << type() << ' ' << name() << ": "
617  << regionTypeNames_[regionType_] << '(' << regionName_ << "):" << nl
618  << " Region has no faces" << exit(FatalError);
619  }
620 
621  totalArea_ = totalArea();
622 
623  Log << " total faces = " << nFaces_ << nl
624  << " total area = " << totalArea_ << nl
625  << endl;
626 
627  writeFileHeader(file());
628 
629  needsUpdate_ = false;
630  return true;
631 }
632 
633 
635 (
636  Ostream& os
637 )
638 {
639  if (canWriteHeader() && (operation_ != opNone))
640  {
641  writeCommented(os, "Region type : ");
642  os << regionTypeNames_[regionType_] << ' ' << regionName_ << nl;
643 
644  writeHeaderValue(os, "Faces", nFaces_);
645  writeHeaderValue(os, "Area", totalArea_);
646  writeHeaderValue(os, "Scale factor", scaleFactor_);
647 
648  if (weightFieldNames_.size())
649  {
650  writeHeaderValue
651  (
652  os,
653  "Weight field",
654  flatOutput(weightFieldNames_, FlatOutput::BareComma{})
655  );
656  }
657 
658  writeCommented(os, "Time");
659  if (writeArea_)
660  {
661  os << tab << "Area";
662  }
663 
664  // TBD: add in postOperation information?
665 
666  for (const word& fieldName : fields_)
667  {
668  os << tab << operationTypeNames_[operation_]
669  << '(' << fieldName << ')';
670  }
671 
672  os << endl;
673  }
674 
675  writtenHeader_ = true;
676 }
677 
678 
679 template<>
680 Foam::scalar
682 (
683  const Field<scalar>& values,
684  const vectorField& Sf,
685  const scalarField& weightField
686 ) const
687 {
688  switch (operation_)
689  {
690  case opSumDirection:
691  {
692  const vector n(dict_.get<vector>("direction"));
693  return gSum(pos0(values*(Sf & n))*mag(values));
694  }
695  case opSumDirectionBalance:
696  {
697  const vector n(dict_.get<vector>("direction"));
698  const scalarField nv(values*(Sf & n));
699 
700  return gSum(pos0(nv)*mag(values) - neg(nv)*mag(values));
701  }
702 
703  case opUniformity:
704  case opWeightedUniformity:
705  case opAbsWeightedUniformity:
706  {
707  const scalar areaTotal = gSum(mag(Sf));
708  tmp<scalarField> areaVal(values * mag(Sf));
709 
710  scalar mean, numer;
711 
712  if (canWeight(weightField))
713  {
714  // Weighted quantity = (Weight * phi * dA)
715 
716  tmp<scalarField> weight(weightingFactor(weightField));
717 
718  // Mean weighted value (area-averaged)
719  mean = gSum(weight()*areaVal()) / areaTotal;
720 
721  // Abs. deviation from weighted mean value
722  numer = gSum(mag(weight*areaVal - (mean * mag(Sf))));
723  }
724  else
725  {
726  // Unweighted quantity = (1 * phi * dA)
727 
728  // Mean value (area-averaged)
729  mean = gSum(areaVal()) / areaTotal;
730 
731  // Abs. deviation from mean value
732  numer = gSum(mag(areaVal - (mean * mag(Sf))));
733  }
734 
735  // Uniformity index
736  const scalar ui = 1 - numer/(2*mag(mean*areaTotal) + ROOTVSMALL);
737 
738  return min(max(ui, 0), 1);
739  }
740 
741  default:
742  {
743  // Fall through to other operations
744  return processSameTypeValues(values, Sf, weightField);
745  }
746  }
747 }
748 
749 
750 template<>
753 (
754  const Field<vector>& values,
755  const vectorField& Sf,
756  const scalarField& weightField
757 ) const
758 {
759  switch (operation_)
760  {
761  case opSumDirection:
762  {
763  const vector n(dict_.get<vector>("direction").normalise());
764 
765  const scalarField nv(n & values);
766  return gSum(pos0(nv)*n*(nv));
767  }
768  case opSumDirectionBalance:
769  {
770  const vector n(dict_.get<vector>("direction").normalise());
771 
772  const scalarField nv(n & values);
773  return gSum(pos0(nv)*n*(nv));
774  }
775  case opAreaNormalAverage:
776  {
777  const scalar val = gSum(values & Sf)/gSum(mag(Sf));
778  return vector(val, 0, 0);
779  }
780  case opAreaNormalIntegrate:
781  {
782  const scalar val = gSum(values & Sf);
783  return vector(val, 0, 0);
784  }
785 
786  case opUniformity:
787  case opWeightedUniformity:
788  case opAbsWeightedUniformity:
789  {
790  const scalar areaTotal = gSum(mag(Sf));
791  tmp<scalarField> areaVal(values & Sf);
792 
793  scalar mean, numer;
794 
795  if (canWeight(weightField))
796  {
797  // Weighted quantity = (Weight * phi . dA)
798 
799  tmp<scalarField> weight(weightingFactor(weightField));
800 
801  // Mean weighted value (area-averaged)
802  mean = gSum(weight()*areaVal()) / areaTotal;
803 
804  // Abs. deviation from weighted mean value
805  numer = gSum(mag(weight*areaVal - (mean * mag(Sf))));
806  }
807  else
808  {
809  // Unweighted quantity = (1 * phi . dA)
810 
811  // Mean value (area-averaged)
812  mean = gSum(areaVal()) / areaTotal;
813 
814  // Abs. deviation from mean value
815  numer = gSum(mag(areaVal - (mean * mag(Sf))));
816  }
817 
818  // Uniformity index
819  const scalar ui = 1 - numer/(2*mag(mean*areaTotal) + ROOTVSMALL);
820 
821  return vector(min(max(ui, 0), 1), 0, 0);
822  }
823 
824  default:
825  {
826  // Fall through to other operations
827  return processSameTypeValues(values, Sf, weightField);
828  }
829  }
830 }
831 
832 
833 template<>
836 (
837  const Field<scalar>& weightField
838 ) const
839 {
840  if (usesMag())
841  {
842  return mag(weightField);
843  }
844 
845  // pass through
846  return weightField;
847 }
848 
849 
850 template<>
853 (
854  const Field<scalar>& weightField,
855  const vectorField& Sf
856 ) const
857 {
858  // scalar * Area
859 
860  if (returnReduce(weightField.empty(), andOp<bool>()))
861  {
862  // No weight field - revert to unweighted form
863  return mag(Sf);
864  }
865  else if (usesMag())
866  {
867  return mag(weightField * mag(Sf));
868  }
869 
870  return (weightField * mag(Sf));
871 }
872 
873 
874 template<>
877 (
878  const Field<vector>& weightField,
879  const vectorField& Sf
880 ) const
881 {
882  // vector (dot) Area
883 
884  if (returnReduce(weightField.empty(), andOp<bool>()))
885  {
886  // No weight field - revert to unweighted form
887  return mag(Sf);
888  }
889  else if (usesMag())
890  {
891  return mag(weightField & Sf);
892  }
893 
894  return (weightField & Sf);
895 }
896 
897 
898 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
899 
901 (
902  const word& name,
903  const Time& runTime,
904  const dictionary& dict
905 )
906 :
907  fieldValue(name, runTime, dict, typeName),
908  regionType_(regionTypeNames_.get("regionType", dict)),
909  operation_(operationTypeNames_.get("operation", dict)),
910  postOperation_
911  (
912  postOperationTypeNames_.getOrDefault
913  (
914  "postOperation",
915  dict,
916  postOperationType::postOpNone,
917  true // Failsafe behaviour
918  )
919  ),
920  needsUpdate_(true),
921  writeArea_(false),
922  selectionNames_(),
923  weightFieldNames_(),
924  totalArea_(0),
925  nFaces_(0),
926  faceId_(),
927  facePatchId_(),
928  faceFlip_()
929 {
930  read(dict);
931 }
932 
933 
935 (
936  const word& name,
937  const objectRegistry& obr,
938  const dictionary& dict
939 )
940 :
941  fieldValue(name, obr, dict, typeName),
942  regionType_(regionTypeNames_.get("regionType", dict)),
943  operation_(operationTypeNames_.get("operation", dict)),
944  postOperation_
945  (
946  postOperationTypeNames_.getOrDefault
947  (
948  "postOperation",
949  dict,
950  postOperationType::postOpNone,
951  true // Failsafe behaviour
952  )
953  ),
954  needsUpdate_(true),
955  writeArea_(false),
956  selectionNames_(),
957  weightFieldNames_(),
958  totalArea_(0),
959  nFaces_(0),
960  faceId_(),
961  facePatchId_(),
962  faceFlip_()
963 {
964  read(dict);
965 }
966 
967 
968 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
969 
971 (
972  const dictionary& dict
973 )
974 {
976 
977  needsUpdate_ = true;
978  writeArea_ = dict.getOrDefault("writeArea", false);
979  weightFieldNames_.clear();
980 
981  totalArea_ = 0;
982  nFaces_ = 0;
983  faceId_.clear();
984  facePatchId_.clear();
985  faceFlip_.clear();
986  sampledPtr_.reset(nullptr);
987  surfaceWriterPtr_.reset(nullptr);
988 
989  // Can have "name" (word) and/or "names" (wordRes)
990  //
991  // If "names" exists AND contains a literal (non-regex) that can be used
992  // as a suitable value for "name", the "name" entry becomes optional.
993 
994  regionName_.clear();
995  selectionNames_.clear();
996 
997  {
998  dict.readIfPresent("names", selectionNames_);
999 
1000  for (const auto& item : selectionNames_)
1001  {
1002  if (item.isLiteral())
1003  {
1004  regionName_ = item;
1005  break;
1006  }
1007  }
1008 
1009  // Mandatory if we didn't pick up a value from selectionNames_
1010  dict.readEntry
1011  (
1012  "name",
1013  regionName_,
1015  regionName_.empty()
1016  );
1017 
1018  // Ensure there is always content for selectionNames_
1019  if (selectionNames_.empty())
1020  {
1021  selectionNames_.resize(1);
1022  selectionNames_.first() = regionName_;
1023  }
1024  }
1025 
1026 
1027  // Create sampled surface, but leave 'expired' (ie, no update) since it
1028  // may depend on fields or data that do not yet exist
1029  if (stSampled == regionType_)
1030  {
1031  sampledPtr_ = sampledSurface::New
1032  (
1033  name(),
1034  mesh_,
1035  dict.subDict("sampledSurfaceDict")
1036  );
1037  }
1038 
1039  Info<< type() << ' ' << name() << ':' << nl
1040  << " operation = ";
1041 
1042  if (postOperation_ != postOpNone)
1043  {
1044  Info<< postOperationTypeNames_[postOperation_] << '('
1045  << operationTypeNames_[operation_] << ')' << nl;
1046  }
1047  else
1048  {
1049  Info<< operationTypeNames_[operation_] << nl;
1050  }
1051 
1052  if (usesWeight())
1053  {
1054  if (stSampled == regionType_)
1055  {
1057  << "Cannot use weighted operation '"
1058  << operationTypeNames_[operation_]
1059  << "' for sampledSurface"
1060  << exit(FatalIOError);
1061  }
1062 
1063  // Can have "weightFields" or "weightField"
1064 
1065  bool missing = true;
1066  if (dict.readIfPresent("weightFields", weightFieldNames_))
1067  {
1068  missing = false;
1069  }
1070  else
1071  {
1072  weightFieldNames_.resize(1);
1073 
1074  if (dict.readIfPresent("weightField", weightFieldNames_.first()))
1075  {
1076  missing = false;
1077  if ("none" == weightFieldNames_.first())
1078  {
1079  // "none" == no weighting
1080  weightFieldNames_.clear();
1081  }
1082  }
1083  }
1084 
1085  if (missing)
1086  {
1087  // Suggest possible alternative unweighted operation?
1089  << "The '" << operationTypeNames_[operation_]
1090  << "' operation is missing a weightField." << nl
1091  << "Either provide the weightField, "
1092  << "use weightField 'none' to suppress weighting," << nl
1093  << "or use a different operation."
1094  << exit(FatalIOError);
1095  }
1096 
1097  Info<< " weight field = ";
1098  if (weightFieldNames_.empty())
1099  {
1100  Info<< "none" << nl;
1101  }
1102  else
1103  {
1104  Info<< flatOutput(weightFieldNames_) << nl;
1105  }
1106  }
1107 
1108  // Backwards compatibility for v1612 and older
1109  List<word> orientedFields;
1110  if (dict.readIfPresent("orientedFields", orientedFields))
1111  {
1112  fields_.append(orientedFields);
1113 
1115  << "The 'orientedFields' option is deprecated. These fields can "
1116  << "and have been added to the standard 'fields' list."
1117  << endl;
1118  }
1119 
1120  if (writeFields_)
1121  {
1122  const word formatName(dict.get<word>("surfaceFormat"));
1123 
1124  surfaceWriterPtr_.reset
1125  (
1127  (
1128  formatName,
1129  dict.subOrEmptyDict("formatOptions").subOrEmptyDict(formatName)
1130  )
1131  );
1132 
1133  if (debug)
1134  {
1135  surfaceWriterPtr_->verbose(true);
1136  }
1137 
1138  if (surfaceWriterPtr_->enabled())
1139  {
1140  Info<< " surfaceFormat = " << formatName << nl;
1141  }
1142  else
1143  {
1144  surfaceWriterPtr_->clear();
1145  }
1146  }
1147 
1148  Info<< nl << endl;
1149 
1150  return true;
1151 }
1152 
1153 
1155 {
1156  if (needsUpdate_ || operation_ != opNone)
1157  {
1159  }
1160 
1161  update();
1162 
1163  if (operation_ != opNone)
1164  {
1165  writeCurrentTime(file());
1166  }
1167 
1168  if (writeArea_)
1169  {
1170  totalArea_ = totalArea();
1171  Log << " total area = " << totalArea_ << endl;
1172 
1173  if (operation_ != opNone && Pstream::master())
1174  {
1175  file() << tab << totalArea_;
1176  }
1177  }
1178 
1179  // Many operations use the Sf field
1180  vectorField Sf;
1181  if (usesSf())
1182  {
1183  if (stObject == regionType_)
1184  {
1185  const polySurface& s = dynamicCast<const polySurface>(obr());
1186  Sf = s.Sf();
1187  }
1188  else if (sampledPtr_)
1189  {
1190  Sf = sampledPtr_->Sf();
1191  }
1192  else
1193  {
1194  Sf = filterField(mesh_.Sf());
1195  }
1196  }
1197 
1198  // Faces and points for surface format (if specified)
1199  faceList faces;
1201 
1202  if (surfaceWriterPtr_)
1203  {
1204  if (withTopologicalMerge())
1205  {
1206  combineMeshGeometry(faces, points);
1207  }
1208  else
1209  {
1210  combineSurfaceGeometry(faces, points);
1211  }
1212  }
1213 
1214 
1215  // Check availability and type of weight field
1216  // Only support a few weight types:
1217  // scalar: 0-N fields
1218  // vector: 0-1 fields
1219 
1220  // Default is a zero-size scalar weight field (ie, weight = 1)
1221  scalarField scalarWeights;
1222  vectorField vectorWeights;
1223 
1224  for (const word& weightName : weightFieldNames_)
1225  {
1226  if (validField<scalar>(weightName))
1227  {
1228  tmp<scalarField> tfld = getFieldValues<scalar>(weightName, true);
1229 
1230  if (scalarWeights.empty())
1231  {
1232  scalarWeights = tfld;
1233  }
1234  else
1235  {
1236  scalarWeights *= tfld;
1237  }
1238  }
1239  else if (validField<vector>(weightName))
1240  {
1241  tmp<vectorField> tfld = getFieldValues<vector>(weightName, true);
1242 
1243  if (vectorWeights.empty())
1244  {
1245  vectorWeights = tfld;
1246  }
1247  else
1248  {
1250  << "weightField " << weightName
1251  << " - only one vector weight field allowed. " << nl
1252  << "weights: " << flatOutput(weightFieldNames_) << nl
1253  << abort(FatalError);
1254  }
1255  }
1256  else if (weightName != "none")
1257  {
1258  // Silently ignore "none", flag everything else as an error
1259 
1260  // TBD: treat missing "rho" like incompressible with rho = 1
1261  // and/or provided rhoRef value
1262 
1264  << "weightField " << weightName
1265  << " not found or an unsupported type" << nl
1266  << abort(FatalError);
1267  }
1268  }
1269 
1270 
1271  // Process the fields
1272  if (vectorWeights.size())
1273  {
1274  if (scalarWeights.size())
1275  {
1276  vectorWeights *= scalarWeights;
1277  }
1278 
1279  writeAll(Sf, vectorWeights, points, faces);
1280  }
1281  else
1282  {
1283  writeAll(Sf, scalarWeights, points, faces);
1284  }
1285 
1286 
1287  if (operation_ != opNone)
1288  {
1289  file() << endl;
1290  Log << endl;
1291  }
1292 
1293 
1294  return true;
1295 }
1296 
1297 
1300  const mapPolyMesh& mpm
1301 )
1302 {
1303  needsUpdate_ = true;
1304 }
1305 
1306 
1309  const polyMesh& mesh
1310 )
1311 {
1312  needsUpdate_ = true;
1313 }
1314 
1315 
1316 // ************************************************************************* //
Foam::expressions::patchExpr::debug
int debug
Static debugging option.
Foam::labelList
List< label > labelList
A List of labels.
Definition: List.H:71
Foam::pointField
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:44
runTime
engineTime & runTime
Definition: createEngineTime.H:13
p
volScalarField & p
Definition: createFieldRefs.H:8
Foam::sampledSurface::New
static autoPtr< sampledSurface > New(const word &name, const polyMesh &mesh, const dictionary &dict)
Return a reference to the selected surface.
Definition: sampledSurface.C:64
Foam::Enum
Enum is a wrapper around a list of names/values that represent particular enumeration (or int) values...
Definition: IOstreamOption.H:57
Log
#define Log
Definition: PDRblock.C:35
Foam::Time
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:73
Foam::functionObjects::fieldValues::addToRunTimeSelectionTable
addToRunTimeSelectionTable(functionObject, fieldValueDelta, dictionary)
Foam::word
A class for handling words, derived from Foam::string.
Definition: word.H:62
Foam::functionObjects::fieldValue::read
virtual bool read(const dictionary &dict)
Read from dictionary.
Definition: fieldValue.C:89
Foam::returnReduce
T returnReduce(const T &Value, const BinaryOp &bop, const int tag=Pstream::msgType(), const label comm=UPstream::worldComm)
Definition: PstreamReduceOps.H:94
s
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))
Definition: gmvOutputSpray.H:25
uindirectPrimitivePatch.H
update
mesh update()
Foam::tmp
A class for managing temporary objects.
Definition: PtrList.H:61
Foam::functionObjects::fieldValues::surfaceFieldValue::operationType
operationType
Operation type enumeration.
Definition: surfaceFieldValue.H:483
Foam::functionObjects::fieldValues::surfaceFieldValue::read
virtual bool read(const dictionary &dict)
Read from dictionary.
Definition: surfaceFieldValue.C:971
Foam::functionObjects::fieldValues::surfaceFieldValue::postOperationType
postOperationType
Post-operation type enumeration.
Definition: surfaceFieldValue.H:555
PatchTools.H
Foam::HashTableOps::values
List< T > values(const HashTable< T, Key, Hash > &tbl, const bool doSort=false)
List of values from HashTable, optionally sorted.
Definition: HashOps.H:149
Foam::functionObjects::timeFunctionObject::storedObjects
objectRegistry & storedObjects()
Definition: timeFunctionObject.C:63
Foam::PatchTools::gatherAndMerge
static void gatherAndMerge(const scalar mergeDist, const PrimitivePatch< FaceList, PointField > &p, Field< typename PrimitivePatch< FaceList, PointField >::point_type > &mergedPoints, List< typename PrimitivePatch< FaceList, PointField >::face_type > &mergedFaces, labelList &pointMergeMap)
Gather points and faces onto master and merge into single patch.
Definition: PatchToolsGatherAndMerge.C:38
Foam::read
bool read(const char *buf, int32_t &val)
Same as readInt32.
Definition: int32.H:108
Foam::UPstream::parRun
static bool & parRun()
Test if this a parallel run, or allow modify access.
Definition: UPstream.H:434
Foam::functionObjects::fieldValues::defineTypeNameAndDebug
defineTypeNameAndDebug(fieldValueDelta, 0)
Foam::functionObjects::fieldValues::surfaceFieldValue::usesSf
bool usesSf() const
True if the operation needs a surface Sf.
Definition: surfaceFieldValue.C:552
Foam::UPstream::master
static bool master(const label communicator=worldComm)
Am I the master process.
Definition: UPstream.H:458
Foam::List::append
void append(const T &val)
Append an element at the end of the list.
Definition: ListI.H:182
Foam::FatalIOError
IOerror FatalIOError
Foam::endl
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:350
Foam::pos0
dimensionedScalar pos0(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:188
Foam::gSum
Type gSum(const FieldField< Field, Type > &f)
Definition: FieldFieldFunctions.C:594
Foam::Pout
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Foam::Vector::normalise
Vector< Cmpt > & normalise()
Normalise the vector by its magnitude.
Definition: VectorI.H:123
Foam::functionObjects::fieldValues::surfaceFieldValue::regionTypeNames_
static const Enum< regionTypes > regionTypeNames_
Region type names.
Definition: surfaceFieldValue.H:471
Foam::functionObjects::fieldValues::surfaceFieldValue::regionType_
regionTypes regionType_
Region type.
Definition: surfaceFieldValue.H:599
Foam::min
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:33
Foam::inplaceRenumber
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values (not the indices) of a list.
Definition: ListOpsTemplates.C:61
Foam::polyMesh
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:77
Foam::sumOp
Definition: ops.H:213
forAll
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:296
Foam::functionObjects::fieldValues::surfaceFieldValue::surfaceFieldValue
surfaceFieldValue(const word &name, const Time &runTime, const dictionary &dict)
Construct from name, Time and dictionary.
Definition: surfaceFieldValue.C:901
nPoints
label nPoints
Definition: gmvOutputHeader.H:2
Foam::functionObjects::fieldValues::surfaceFieldValue::stObject
Calculate with function object surface.
Definition: surfaceFieldValue.H:466
Foam::objectRegistry
Registry of regIOobjects.
Definition: objectRegistry.H:60
Foam::functionObjects::fieldValues::surfaceFieldValue::weightingFactor
tmp< scalarField > weightingFactor(const Field< WeightType > &weightField) const
Weighting factor.
n
label n
Definition: TABSMDCalcMethod2.H:31
Foam::polySurface
A surface mesh consisting of general polygon faces and capable of holding fields.
Definition: polySurface.H:67
Foam::functionObjects::fieldValues::surfaceFieldValue::update
bool update()
Update the surface and surface information as required.
Definition: surfaceFieldValue.C:574
coupledPolyPatch.H
patchIds
labelList patchIds
Definition: convertProcessorPatches.H:67
Foam::functionObjects::fieldValue::regionName_
word regionName_
Name of region (patch, zone, etc.)
Definition: fieldValue.H:133
Foam::Field< scalar >
Foam::Info
messageStream Info
Information stream (uses stdout - output is on the master only)
Foam::name
word name(const complex &c)
Return string representation of complex.
Definition: complex.C:76
faceId
label faceId(-1)
sampledSurface.H
Foam::functionObjects::fieldValues::surfaceFieldValue::updateMesh
virtual void updateMesh(const mapPolyMesh &mpm)
Update for changes of mesh.
Definition: surfaceFieldValue.C:1299
Foam::functionObjects::fieldValues::surfaceFieldValue::regionTypes
regionTypes
Region type enumeration.
Definition: surfaceFieldValue.H:462
Foam::andOp
Definition: ops.H:233
Foam::IOstream::name
virtual const fileName & name() const
Return the name of the stream.
Definition: IOstream.C:39
Foam::objectRegistry::lookupObject
const Type & lookupObject(const word &name, const bool recursive=false) const
Definition: objectRegistryTemplates.C:434
Foam::max
label max(const labelHashSet &set, label maxValue=labelMin)
Find the max value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:47
dict
dictionary dict
Definition: searchingEngine.H:14
Foam::FatalError
error FatalError
Foam::dictionary
A list of keyword definitions, which are a keyword followed by a number of values (eg,...
Definition: dictionary.H:121
mesh
dynamicFvMesh & mesh
Definition: createDynamicFvMesh.H:6
addToRunTimeSelectionTable.H
Macros for easy insertion into run-time selection tables.
fvMesh.H
Foam
Namespace for OpenFOAM.
Definition: atmBoundaryLayer.C:33
Foam::flatOutput
FlatOutput::OutputAdaptor< Container, Delimiters > flatOutput(const Container &obj, Delimiters delim)
Global flatOutput() function with specified output delimiters.
Definition: FlatOutput.H:217
Foam::abort
errorManip< error > abort(error &err)
Definition: errorManip.H:144
Foam::functionObjects::fieldValues::surfaceFieldValue::obr
const objectRegistry & obr() const
The volume mesh or surface registry being used.
Definition: surfaceFieldValue.C:120
Foam::vector
Vector< scalar > vector
A scalar version of the templated Vector.
Definition: vector.H:51
emptyPolyPatch.H
Foam::functionObjects::fieldValues::surfaceFieldValue::movePoints
virtual void movePoints(const polyMesh &mesh)
Update for changes of mesh.
Definition: surfaceFieldValue.C:1308
Foam::mergePoints
label mergePoints(const PointList &points, const scalar mergeTol, const bool verbose, labelList &pointMap, typename PointList::const_reference origin=PointList::value_type::zero)
Sorts and merges points. All points closer than/equal mergeTol get merged.
Foam::exit
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:130
FatalErrorInFunction
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:381
Foam::Pstream::gatherList
static void gatherList(const List< commsStruct > &comms, List< T > &Values, const int tag, const label comm)
Gather data but keep individual values separate.
Definition: gatherScatterList.C:52
Foam::tab
constexpr char tab
Definition: Ostream.H:384
Foam::UPstream::myProcNo
static int myProcNo(const label communicator=worldComm)
Number of this process (starting from masterNo() = 0)
Definition: UPstream.H:464
Foam::nl
constexpr char nl
Definition: Ostream.H:385
Foam::functionObjects::fieldValues::surfaceFieldValue::operationTypeNames_
static const Enum< operationType > operationTypeNames_
Operation type names.
Definition: surfaceFieldValue.H:551
f
labelList f(nPoints)
Foam::faceList
List< face > faceList
A List of faces.
Definition: faceListFwd.H:47
Foam::surfaceWriter::New
static autoPtr< surfaceWriter > New(const word &writeType)
Return a reference to the selected surfaceWriter.
Definition: surfaceWriter.C:64
Foam::FlatOutput::BareComma
Surround with '\0' and '\0' separate with ','.
Definition: FlatOutput.H:84
Foam::Vector< scalar >
Foam::List< label >
Foam::functionObjects::fieldValue
Intermediate class for handling field value-based function objects.
Definition: fieldValue.H:119
Foam::mag
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
Foam::type
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: MSwindows.C:590
Foam::functionObjects::fvMeshFunctionObject::mesh_
const fvMesh & mesh_
Reference to the fvMesh.
Definition: fvMeshFunctionObject.H:73
points
const pointField & points
Definition: gmvOutputHeader.H:1
Foam::constant::electromagnetic::e
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
Foam::functionObjects::fieldValues::surfaceFieldValue::writeFileHeader
virtual void writeFileHeader(Ostream &os)
Output file header information.
Definition: surfaceFieldValue.C:635
Foam::identity
labelList identity(const label len, label start=0)
Create identity map of the given length with (map[i] == i)
Definition: labelList.C:38
mergePoints.H
Merge points. See below.
Foam::mapPolyMesh
Class containing mesh-to-mesh mapping information after a change in polyMesh topology.
Definition: mapPolyMesh.H:161
Foam::primitivePatch
PrimitivePatch< SubList< face >, const pointField & > primitivePatch
A PrimitivePatch with a SubList addressing for the faces, const reference for the point field.
Definition: primitivePatch.H:51
surfaceFieldValue.H
FatalIOErrorInFunction
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:401
Foam::DelaunayMeshTools::allPoints
tmp< pointField > allPoints(const Triangulation &t)
Extract all points in vertex-index order.
Foam::uindirectPrimitivePatch
PrimitivePatch< UIndirectList< face >, const pointField & > uindirectPrimitivePatch
A PrimitivePatch with UIndirectList for the faces, const reference for the point field.
Definition: uindirectPrimitivePatch.H:49
Foam::keyType::LITERAL
String literal.
Definition: keyType.H:73
Foam::Ostream
An Ostream is an abstract base class for all output systems (streams, files, token lists,...
Definition: Ostream.H:56
Foam::functionObjects::fieldValue::write
virtual bool write()
Write.
Definition: fieldValue.C:113
Foam::point
vector point
Point is a vector.
Definition: point.H:43
Foam::functionObjects::fieldValues::surfaceFieldValue::write
virtual bool write()
Calculate and write.
Definition: surfaceFieldValue.C:1154
Foam::orOp
Definition: ops.H:234
Foam::functionObjects::fieldValues::surfaceFieldValue::processValues
Type processValues(const Field< Type > &values, const vectorField &Sf, const Field< WeightType > &weightField) const
Apply the 'operation' to the values. Wrapper around.
Definition: surfaceFieldValueTemplates.C:293
Foam::neg
dimensionedScalar neg(const dimensionedScalar &ds)
Definition: dimensionedScalar.C:199
WarningInFunction
#define WarningInFunction
Report a warning using Foam::Warning.
Definition: messageStream.H:303
Foam::functionObjects::fieldValues::surfaceFieldValue::postOperationTypeNames_
static const Enum< postOperationType > postOperationTypeNames_
Operation type names.
Definition: surfaceFieldValue.H:563
Foam::UPstream::nProcs
static label nProcs(const label communicator=worldComm)
Number of processes in parallel run, and 1 for serial run.
Definition: UPstream.H:446