refinementRegions.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2026 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 \*---------------------------------------------------------------------------*/
25 
26 #include "refinementRegions.H"
27 #include "searchableSurfaceList.H"
28 #include "orientedSurface.H"
29 #include "Time.H"
30 
31 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
32 
34 Foam::refinementRegions::refineModeNames_
35 {
36  "inside",
37  "outside",
38  "distance",
39  "insideSpan",
40  "outsideSpan"
41 };
42 
43 
44 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
45 
46 void Foam::refinementRegions::setAndCheckLevels
47 (
48  const label shelli,
49  const dictionary& dict
50 )
51 {
52  const searchableSurface& shell = allGeometry_[shells_[shelli]];
53 
54  if
55  (
56  modes_[shelli] == refineMode::inside
57  || modes_[shelli] == refineMode::outside
58  )
59  {
60  if (!allGeometry_[shells_[shelli]].hasVolumeType())
61  {
63  << "Shell " << shell.name()
64  << " is not closed so testing for '"
65  << refineModeNames_[modes_[shelli]]
66  << "' may fail." << endl;
67  }
68 
69  distances_[shelli].setSize(0);
70  levels_[shelli].setSize(1);
71 
72  if (dict.found("levels") && !(dict.found("level")))
73  {
74  // Support 'levels' for backward compatibility
75  const List<Tuple2<scalar, label>> distLevels(dict.lookup("levels"));
76 
77  if (distLevels.size() != 1)
78  {
80  << "For refinement mode "
81  << refineModeNames_[modes_[shelli]]
82  << " specify only one distance+level."
83  << " (its distance gets discarded)"
84  << exit(FatalError);
85  }
86 
87  levels_[shelli][0] = distLevels[0].second();
88  }
89  else
90  {
91  if (dict.found("levels"))
92  {
94  << "Found both 'level' and 'levels' entries, using 'level'."
95  << endl;
96  }
97 
98  levels_[shelli][0] = readLabel(dict.lookup("level"));
99  }
100 
101  if (modes_[shelli] == refineMode::inside)
102  {
103  Info<< "Refinement level " << levels_[shelli][0]
104  << " for all cells inside " << shell.name() << endl;
105  }
106  else
107  {
108  Info<< "Refinement level " << levels_[shelli][0]
109  << " for all cells outside " << shell.name() << endl;
110  }
111  }
112  else if
113  (
114  modes_[shelli] == refineMode::insideSpan
115  || modes_[shelli] == refineMode::outsideSpan
116  )
117  {
118  if (!allGeometry_[shells_[shelli]].hasVolumeType())
119  {
121  << "Shell " << shell.name()
122  << " is not closed so testing for '"
123  << refineModeNames_[modes_[shelli]]
124  << "' may fail." << endl;
125  }
126 
127  distances_[shelli].setSize(1);
128  levels_[shelli].setSize(1);
129  const Tuple2<scalar, label> distLevel(dict.lookup("level"));
130  distances_[shelli][0] = distLevel.first();
131  levels_[shelli][0] = distLevel.second();
132 
133  if (modes_[shelli] == refineMode::insideSpan)
134  {
135  Info<< "Refinement level " << levels_[shelli][0]
136  << " for all cells inside " << shell.name()
137  << " within distance " << distances_[shelli][0] << endl;
138  }
139  else
140  {
141  Info<< "Refinement level " << levels_[shelli][0]
142  << " for all cells outside " << shell.name()
143  << " within distance " << distances_[shelli][0] << endl;
144  }
145  }
146  else
147  {
148  const List<Tuple2<scalar, label>> distLevels(dict.lookup("levels"));
149 
150  // Extract information into separate distance and level
151  distances_[shelli].setSize(distLevels.size());
152  levels_[shelli].setSize(distLevels.size());
153 
154  forAll(distLevels, j)
155  {
156  distances_[shelli][j] = distLevels[j].first();
157  levels_[shelli][j] = distLevels[j].second();
158 
159  // Check in incremental order
160  if (j > 0)
161  {
162  if
163  (
164  (distances_[shelli][j] <= distances_[shelli][j-1])
165  || (levels_[shelli][j] > levels_[shelli][j-1])
166  )
167  {
169  << "For refinement mode "
170  << refineModeNames_[modes_[shelli]]
171  << " : Refinement should be specified in order"
172  << " of increasing distance"
173  << " (and decreasing refinement level)." << endl
174  << "Distance:" << distances_[shelli][j]
175  << " refinementLevel:" << levels_[shelli][j]
176  << exit(FatalError);
177  }
178  }
179  }
180 
181  if (modes_[shelli] == refineMode::distance)
182  {
183  Info<< "Refinement level according to distance to "
184  << shell.name() << endl;
185 
186  forAll(levels_[shelli], j)
187  {
188  Info<< " level " << levels_[shelli][j]
189  << " for all cells within " << distances_[shelli][j]
190  << " metre." << endl;
191  }
192  }
193  }
194 }
195 
196 
197 void Foam::refinementRegions::orient()
198 {
199  // Determine outside point.
200  boundBox overallBb = boundBox::invertedBox;
201 
202  bool hasSurface = false;
203 
204  forAll(shells_, shelli)
205  {
206  const searchableSurface& s = allGeometry_[shells_[shelli]];
207 
208  if
209  (
210  modes_[shelli] != refineMode::distance
211  && isA<searchableSurfaces::triSurface>(s)
212  )
213  {
214  const searchableSurfaces::triSurface& shell =
215  refCast<const searchableSurfaces::triSurface>(s);
216 
217  if (shell.triSurface::size())
218  {
219  tmp<pointField> tpoints(shell.points());
220  const pointField& points = tpoints();
221 
222  hasSurface = true;
223  boundBox shellBb(points[0], points[0]);
224 
225  // Assume surface is compact!
226  forAll(points, i)
227  {
228  const point& pt = points[i];
229  shellBb.min() = min(shellBb.min(), pt);
230  shellBb.max() = max(shellBb.max(), pt);
231  }
232 
233  overallBb.min() = min(overallBb.min(), shellBb.min());
234  overallBb.max() = max(overallBb.max(), shellBb.max());
235  }
236  }
237  }
238 
239  if (hasSurface)
240  {
241  const point outsidePt = overallBb.max() + overallBb.span();
242 
243  // Info<< "Using point " << outsidePt << " to orient shells" << endl;
244 
245  forAll(shells_, shelli)
246  {
247  const searchableSurface& s = allGeometry_[shells_[shelli]];
248 
249  if
250  (
251  modes_[shelli] != refineMode::distance
252  && isA<searchableSurfaces::triSurface>(s)
253  )
254  {
255  searchableSurfaces::triSurface& shell =
256  const_cast<searchableSurfaces::triSurface&>
257  (
258  refCast<const searchableSurfaces::triSurface>(s)
259  );
260 
261  // Flip surface so outsidePt is outside.
262  bool anyFlipped = orientedSurface::orient
263  (
264  shell,
265  outsidePt,
266  true
267  );
268 
269  if (anyFlipped)
270  {
271  // orientedSurface will have done a clearOut of the surface.
272  // we could do a clearout of the triSurfacees::trees()
273  // but these aren't affected by orientation
274  // (except for cached
275  // sideness which should not be set at this point.
276  // !!Should check!)
277 
278  Info<< "refinementRegions : Flipped orientation of surface "
279  << s.name()
280  << " so point " << outsidePt << " is outside." << endl;
281  }
282  }
283  }
284  }
285 }
286 
287 
288 Foam::scalar Foam::refinementRegions::interpolate
289 (
290  const searchableSurfaces::triSurface& tsm,
291  const scalarIOField& closeness,
292  const point& pt,
293  const label index
294 ) const
295 {
296  const barycentric2D bary
297  (
299  (
300  tsm.points(),
301  tsm.Foam::triSurface::operator[](index)
302  ).pointToBarycentric(pt)
303  );
304 
305  const labelledTri& lf = tsm.localFaces()[index];
306 
307  return closeness[lf[0]]*bary[0]
308  + closeness[lf[1]]*bary[1]
309  + closeness[lf[2]]*bary[2];
310 }
311 
312 
313 void Foam::refinementRegions::findHigherLevel
314 (
315  const pointField& pt,
316  const label shelli,
317  const scalar level0EdgeLength,
318  labelList& maxLevel
319 ) const
320 {
321  const labelList& levels = levels_[shelli];
322 
323  if (modes_[shelli] == refineMode::distance)
324  {
325  // Distance mode.
326 
327  const scalarField& distances = distances_[shelli];
328 
329  // Collect all those points that have a current maxLevel less than
330  // (any of) the shell. Also collect the furthest distance allowable
331  // to any shell with a higher level.
332 
333  pointField candidates(pt.size());
334  labelList candidateMap(pt.size());
335  scalarField candidateDistSqr(pt.size());
336  label candidatei = 0;
337 
338  forAll(maxLevel, pointi)
339  {
340  forAllReverse(levels, leveli)
341  {
342  if (levels[leveli] > maxLevel[pointi])
343  {
344  candidates[candidatei] = pt[pointi];
345  candidateMap[candidatei] = pointi;
346  candidateDistSqr[candidatei] = sqr(distances[leveli]);
347  candidatei++;
348  break;
349  }
350  }
351  }
352  candidates.setSize(candidatei);
353  candidateMap.setSize(candidatei);
354  candidateDistSqr.setSize(candidatei);
355 
356  // Do the expensive nearest test only for the candidate points.
358  allGeometry_[shells_[shelli]].findNearest
359  (
360  candidates,
361  candidateDistSqr,
362  nearInfo
363  );
364 
365  // Update maxLevel
366  forAll(nearInfo, candidatei)
367  {
368  if (nearInfo[candidatei].hit())
369  {
370  // Check which level it actually is in.
371  label minDistI = findLower
372  (
373  distances,
374  mag(nearInfo[candidatei].hitPoint()-candidates[candidatei])
375  );
376 
377  label pointi = candidateMap[candidatei];
378 
379  // pt is in between shell[minDistI] and shell[minDistI+1]
380  maxLevel[pointi] = levels[minDistI+1];
381  }
382  }
383  }
384  else if
385  (
386  modes_[shelli] == refineMode::insideSpan
387  || modes_[shelli] == refineMode::outsideSpan
388  )
389  {
390  const searchableSurfaces::triSurface& tsm =
391  refCast<const searchableSurfaces::triSurface>
392  (allGeometry_[shells_[shelli]]);
393 
394  // Collect all those points that have a current maxLevel less than
395  // the maximum and furthest distance allowable for the shell.
396 
397  pointField candidates(pt.size());
398  labelList candidateMap(pt.size());
399  scalarField candidateDistSqr(pt.size());
400  label candidatei = 0;
401 
402  forAll(pt, pointi)
403  {
404  if (levels[0] > maxLevel[pointi])
405  {
406  candidates[candidatei] = pt[pointi];
407  candidateMap[candidatei] = pointi;
408  candidateDistSqr[candidatei] = sqr(distances_[shelli][0]);
409  candidatei++;
410  }
411  }
412  candidates.setSize(candidatei);
413  candidateMap.setSize(candidatei);
414  candidateDistSqr.setSize(candidatei);
415 
416  // Do the expensive nearest test only for the candidate points.
418  tsm.findNearest
419  (
420  candidates,
421  candidateDistSqr,
422  nearInfo
423  );
424 
425  // Minimum span for the maximum level specified
426  const scalar minSpan
427  (
428  cellsAcrossSpan_[shelli]*level0EdgeLength/(1 << (levels[0] - 1))
429  );
430 
431  // Update maxLevel
432  forAll(nearInfo, candidatei)
433  {
434  if (nearInfo[candidatei].hit())
435  {
436  const scalar span
437  (
439  (
440  tsm,
441  closeness_[shelli],
442  nearInfo[candidatei].rawPoint(),
443  nearInfo[candidatei].index()
444  )
445  );
446 
447  if (span > minSpan)
448  {
449  const label level
450  (
451  log2(cellsAcrossSpan_[shelli]*level0EdgeLength/span) + 1
452  );
453 
454  maxLevel[candidateMap[candidatei]] = min(levels[0], level);
455  }
456  else
457  {
458  maxLevel[candidateMap[candidatei]] = levels[0];
459  }
460  }
461  }
462  }
463  else
464  {
465  // Inside/outside mode
466 
467  // Collect all those points that have a current maxLevel less than the
468  // shell.
469 
470  pointField candidates(pt.size());
471  labelList candidateMap(pt.size());
472  label candidatei = 0;
473 
474  forAll(maxLevel, pointi)
475  {
476  if (levels[0] > maxLevel[pointi])
477  {
478  candidates[candidatei] = pt[pointi];
479  candidateMap[candidatei] = pointi;
480  candidatei++;
481  }
482  }
483  candidates.setSize(candidatei);
484  candidateMap.setSize(candidatei);
485 
486  // Do the expensive nearest test only for the candidate points.
487  List<volumeType> volType;
488  allGeometry_[shells_[shelli]].getVolumeType(candidates, volType);
489 
490  forAll(volType, i)
491  {
492  label pointi = candidateMap[i];
493 
494  if
495  (
496  (
497  modes_[shelli] == refineMode::inside
498  && volType[i] == volumeType::inside
499  )
500  || (
501  modes_[shelli] == refineMode::outside
502  && volType[i] == volumeType::outside
503  )
504  )
505  {
506  maxLevel[pointi] = levels[0];
507  }
508  }
509  }
510 }
511 
512 
513 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
514 
516 (
517  const searchableSurfaceList& allGeometry,
518  const dictionary& shellsDict
519 )
520 :
521  allGeometry_(allGeometry)
522 {
523  // Wildcard specification : loop over all surfaces and try to find a match.
524 
525  // Count number of shells.
526  label shelli = 0;
527  forAll(allGeometry.names(), geomi)
528  {
529  const word& geomName = allGeometry_.names()[geomi];
530 
531  if (shellsDict.found(geomName))
532  {
533  shelli++;
534  }
535  }
536 
537  // Size lists
538  shells_.setSize(shelli);
539  modes_.setSize(shelli);
540  distances_.setSize(shelli);
541  levels_.setSize(shelli);
542  cellsAcrossSpan_.setSize(shelli);
543  closeness_.setSize(shelli);
544 
545  HashSet<word> unmatchedKeys(shellsDict.toc());
546  shelli = 0;
547 
548  forAll(allGeometry_.names(), geomi)
549  {
550  const word& geomName = allGeometry_.names()[geomi];
551 
552  const entry* ePtr = shellsDict.lookupEntryPtr(geomName, false, true);
553 
554  if (ePtr)
555  {
556  const dictionary& dict = ePtr->dict();
557  unmatchedKeys.erase(ePtr->keyword());
558 
559  shells_[shelli] = geomi;
560  modes_[shelli] = refineModeNames_.read(dict.lookup("mode"));
561 
562  // Read pairs of distance+level
563  setAndCheckLevels(shelli, dict);
564 
565  if
566  (
567  modes_[shelli] == refineMode::insideSpan
568  || modes_[shelli] == refineMode::outsideSpan
569  )
570  {
571  const searchableSurface& surface = allGeometry_[geomi];
572 
573  if (isA<searchableSurfaces::triSurface>(surface))
574  {
575  dict.lookup("cellsAcrossSpan") >> cellsAcrossSpan_[shelli];
576 
577  closeness_.set
578  (
579  shelli,
580  new scalarIOField
581  (
582  IOobject
583  (
584  surface.name()
585  + ".closeness."
586  + (
587  modes_[shelli] == refineMode::insideSpan
588  ? "internal"
589  : "external"
590  )
591  + "PointCloseness",
592  surface.searchableSurface::time().constant(),
594  (
595  surface.searchableSurface::time()
596  ),
597  surface.searchableSurface::time(),
599  )
600  )
601  );
602  }
603  else
604  {
605  FatalIOErrorInFunction(shellsDict)
606  << "Surface " << surface.name()
607  << " is not a triSurface as required by"
608  " refinement modes "
609  << refineModeNames_[refineMode::insideSpan]
610  << " and " << refineModeNames_[refineMode::outsideSpan]
611  << exit(FatalIOError);
612  }
613  }
614 
615  shelli++;
616  }
617  }
618 
619  if (unmatchedKeys.size() > 0)
620  {
622  (
623  shellsDict
624  ) << "Not all entries in refinementRegions dictionary were used."
625  << " The following entries were not used : "
626  << unmatchedKeys.sortedToc()
627  << endl;
628  }
629 
630  // Orient shell surfaces before any searching is done. Note that this
631  // only needs to be done for inside or outside. Orienting surfaces
632  // constructs lots of addressing which we want to avoid.
633  orient();
634 }
635 
636 
637 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
638 
640 {
641  label overallMax = 0;
642 
643  forAll(levels_, shelli)
644  {
645  overallMax = max(overallMax, max(levels_[shelli]));
646  }
647 
648  return overallMax;
649 }
650 
651 
652 void Foam::refinementRegions::findHigherLevel
653 (
654  const pointField& pt,
655  const labelList& ptLevel,
656  const scalar level0EdgeLength,
657  labelList& maxLevel
658 ) const
659 {
660  // Maximum level of any shell. Start off with level of point.
661  maxLevel = ptLevel;
662 
663  forAll(shells_, shelli)
664  {
665  findHigherLevel(pt, shelli, level0EdgeLength, maxLevel);
666  }
667 }
668 
669 
670 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:449
#define forAllReverse(list, i)
Reverse loop across all elements in list.
Definition: UList.H:461
A HashTable with keys but without contents.
Definition: HashSet.H:62
List< Key > sortedToc() const
Return the table of contents as a sorted list.
Definition: HashTable.C:242
bool erase(const iterator &)
Erase a hashedEntry specified by given iterator.
Definition: HashTable.C:445
label size() const
Return number of elements in table.
Definition: HashTableI.H:65
A primitive field of type <Type> with automated input and output.
Definition: IOField.H:53
IOobject defines the attributes of an object for which implicit objectRegistry management is supporte...
Definition: IOobject.H:99
const word & name() const
Return name.
Definition: IOobject.H:307
void setSize(const label)
Reset size of List.
Definition: List.C:281
Initialise the NamedEnum HashTable from the static list of names.
Definition: NamedEnum.H:55
Enum read(Istream &) const
Read a word from Istream and return the corresponding.
Definition: NamedEnum.C:55
T & first()
Return the first element of the list.
Definition: UListI.H:114
static const Form max
Definition: VectorSpace.H:120
static const boundBox invertedBox
A very large inverted boundBox: min/max == +/- vGreat.
Definition: boundBox.H:80
A list of keywords followed by any number of values (e.g. words and numbers) or sub-dictionaries.
Definition: dictionary.H:162
const entry * lookupEntryPtr(const word &, bool recursive, bool patternMatch) const
Find and return an entry data stream pointer if present.
Definition: dictionary.C:507
wordList toc() const
Return the table of contents.
Definition: dictionary.C:981
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:468
A keyword and a list of tokens is an 'entry'.
Definition: entry.H:68
const keyType & keyword() const
Return keyword.
Definition: entry.H:136
virtual const dictionary & dict() const =0
Return dictionary if this entry is a dictionary.
static bool orient(triSurface &, const point &, const bool orientOutside)
Flip faces such that normals are consistent with point:
Motion of the mesh specified as a list of pointMeshMovers.
refinementRegions(const searchableSurfaceList &allGeometry, const dictionary &shellsDict)
Construct from geometry and dictionary.
label maxLevel() const
Highest shell level.
Container for searchableSurfaces.
const wordList & names() const
Base class of (analytical or triangulated) surface. Encapsulates all the search routines....
static const word & geometryDir()
Return the geometry directory name.
A class for handling words, derived from string.
Definition: word.H:63
#define FatalIOErrorInFunction(ios)
Report an error message using Foam::FatalIOError.
Definition: error.H:346
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:334
const pointField & points
gmvFile<< "tracers "<< particles.size()<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().x()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().y()<< " ";}gmvFile<< nl;forAllConstIter(lagrangian::Cloud< passiveParticle >, particles, iter){ gmvFile<< iter().position().z()<< " ";}gmvFile<< nl;forAll(lagrangianScalarNames, i){ word name=lagrangianScalarNames[i];IOField< scalar > s(IOobject(name, runTime.name(), lagrangian::cloud::prefix, mesh, IOobject::MUST_READ, IOobject::NO_WRITE))
#define IOWarningInFunction(ios)
Report an IO warning using Foam::Warning.
#define WarningInFunction
Report a warning using Foam::Warning.
static tmp< SurfaceField< Type > > interpolate(const VolField< Type > &tvf, const surfaceScalarField &faceFlux, Istream &schemeData)
Interpolate field onto faces using scheme given by Istream.
Tuple2< scalar, label > nearInfo
Private class for finding nearest.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
List< label > labelList
A List of labels.
Definition: labelList.H:56
label log2(label i)
Return the log base 2 by successive bit-shifting of the given label.
Definition: label.H:79
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:288
label findLower(const ListType &, typename ListType::const_reference, const label stary, const BinaryOp &bop)
Find last element < given value in sorted list and return index,.
messageStream Info
IOField< scalar > scalarIOField
scalarField with IO.
Definition: scalarIOField.H:43
vectorField pointField
pointField is a vectorField.
Definition: pointFieldFwd.H:42
Barycentric2D< scalar > barycentric2D
A scalar version of the templated Barycentric2D.
Definition: barycentric2D.H:45
vector point
Point is a vector.
Definition: point.H:41
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
tmp< DimensionedField< typename outerProduct< Type, Type >::type, GeoMesh, Field >> sqr(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dimensioned< Type > min(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
IOerror FatalIOError
triangle< point, const point & > triPointRef
Definition: triPointRef.H:44
label readLabel(Istream &is)
Definition: label.H:64
tmp< DimensionedField< scalar, GeoMesh, Field > > mag(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
error FatalError
dimensioned< Type > max(const DimensionedField< Type, GeoMesh, PrimitiveField > &df)
dictionary dict