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