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-2025 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 
30 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
31 
33 Foam::refinementRegions::refineModeNames_
34 {
35  "inside",
36  "outside",
37  "distance",
38  "insideSpan",
39  "outsideSpan"
40 };
41 
42 
43 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
44 
45 void Foam::refinementRegions::setAndCheckLevels
46 (
47  const label shelli,
48  const dictionary& dict
49 )
50 {
51  const searchableSurface& shell = allGeometry_[shells_[shelli]];
52 
53  if
54  (
55  modes_[shelli] == refineMode::inside
56  || modes_[shelli] == refineMode::outside
57  )
58  {
59  if (!allGeometry_[shells_[shelli]].hasVolumeType())
60  {
62  << "Shell " << shell.name()
63  << " is not closed so testing for '"
64  << refineModeNames_[modes_[shelli]]
65  << "' may fail." << endl;
66  }
67 
68  distances_[shelli].setSize(0);
69  levels_[shelli].setSize(1);
70 
71  if (dict.found("levels") && !(dict.found("level")))
72  {
73  // Support 'levels' for backward compatibility
74  const List<Tuple2<scalar, label>> distLevels(dict.lookup("levels"));
75 
76  if (distLevels.size() != 1)
77  {
79  << "For refinement mode "
80  << refineModeNames_[modes_[shelli]]
81  << " specify only one distance+level."
82  << " (its distance gets discarded)"
83  << exit(FatalError);
84  }
85 
86  levels_[shelli][0] = distLevels[0].second();
87  }
88  else
89  {
90  if (dict.found("levels"))
91  {
93  << "Found both 'level' and 'levels' entries, using 'level'."
94  << endl;
95  }
96 
97  levels_[shelli][0] = readLabel(dict.lookup("level"));
98  }
99 
100  if (modes_[shelli] == refineMode::inside)
101  {
102  Info<< "Refinement level " << levels_[shelli][0]
103  << " for all cells inside " << shell.name() << endl;
104  }
105  else
106  {
107  Info<< "Refinement level " << levels_[shelli][0]
108  << " for all cells outside " << shell.name() << endl;
109  }
110  }
111  else if
112  (
113  modes_[shelli] == refineMode::insideSpan
114  || modes_[shelli] == refineMode::outsideSpan
115  )
116  {
117  if (!allGeometry_[shells_[shelli]].hasVolumeType())
118  {
120  << "Shell " << shell.name()
121  << " is not closed so testing for '"
122  << refineModeNames_[modes_[shelli]]
123  << "' may fail." << endl;
124  }
125 
126  distances_[shelli].setSize(1);
127  levels_[shelli].setSize(1);
128  const Tuple2<scalar, label> distLevel(dict.lookup("level"));
129  distances_[shelli][0] = distLevel.first();
130  levels_[shelli][0] = distLevel.second();
131 
132  if (modes_[shelli] == refineMode::insideSpan)
133  {
134  Info<< "Refinement level " << levels_[shelli][0]
135  << " for all cells inside " << shell.name()
136  << " within distance " << distances_[shelli][0] << endl;
137  }
138  else
139  {
140  Info<< "Refinement level " << levels_[shelli][0]
141  << " for all cells outside " << shell.name()
142  << " within distance " << distances_[shelli][0] << endl;
143  }
144  }
145  else
146  {
147  const List<Tuple2<scalar, label>> distLevels(dict.lookup("levels"));
148 
149  // Extract information into separate distance and level
150  distances_[shelli].setSize(distLevels.size());
151  levels_[shelli].setSize(distLevels.size());
152 
153  forAll(distLevels, j)
154  {
155  distances_[shelli][j] = distLevels[j].first();
156  levels_[shelli][j] = distLevels[j].second();
157 
158  // Check in incremental order
159  if (j > 0)
160  {
161  if
162  (
163  (distances_[shelli][j] <= distances_[shelli][j-1])
164  || (levels_[shelli][j] > levels_[shelli][j-1])
165  )
166  {
168  << "For refinement mode "
169  << refineModeNames_[modes_[shelli]]
170  << " : Refinement should be specified in order"
171  << " of increasing distance"
172  << " (and decreasing refinement level)." << endl
173  << "Distance:" << distances_[shelli][j]
174  << " refinementLevel:" << levels_[shelli][j]
175  << exit(FatalError);
176  }
177  }
178  }
179 
180  if (modes_[shelli] == refineMode::distance)
181  {
182  Info<< "Refinement level according to distance to "
183  << shell.name() << endl;
184 
185  forAll(levels_[shelli], j)
186  {
187  Info<< " level " << levels_[shelli][j]
188  << " for all cells within " << distances_[shelli][j]
189  << " metre." << endl;
190  }
191  }
192  }
193 }
194 
195 
196 void Foam::refinementRegions::orient()
197 {
198  // Determine outside point.
199  boundBox overallBb = boundBox::invertedBox;
200 
201  bool hasSurface = false;
202 
203  forAll(shells_, shelli)
204  {
205  const searchableSurface& s = allGeometry_[shells_[shelli]];
206 
207  if
208  (
209  modes_[shelli] != refineMode::distance
210  && isA<searchableSurfaces::triSurface>(s)
211  )
212  {
213  const searchableSurfaces::triSurface& shell =
214  refCast<const searchableSurfaces::triSurface>(s);
215 
216  if (shell.triSurface::size())
217  {
218  tmp<pointField> tpoints(shell.points());
219  const pointField& points = tpoints();
220 
221  hasSurface = true;
222  boundBox shellBb(points[0], points[0]);
223 
224  // Assume surface is compact!
225  forAll(points, i)
226  {
227  const point& pt = points[i];
228  shellBb.min() = min(shellBb.min(), pt);
229  shellBb.max() = max(shellBb.max(), pt);
230  }
231 
232  overallBb.min() = min(overallBb.min(), shellBb.min());
233  overallBb.max() = max(overallBb.max(), shellBb.max());
234  }
235  }
236  }
237 
238  if (hasSurface)
239  {
240  const point outsidePt = overallBb.max() + overallBb.span();
241 
242  // Info<< "Using point " << outsidePt << " to orient shells" << endl;
243 
244  forAll(shells_, shelli)
245  {
246  const searchableSurface& s = allGeometry_[shells_[shelli]];
247 
248  if
249  (
250  modes_[shelli] != refineMode::distance
251  && isA<searchableSurfaces::triSurface>(s)
252  )
253  {
254  searchableSurfaces::triSurface& shell =
255  const_cast<searchableSurfaces::triSurface&>
256  (
257  refCast<const searchableSurfaces::triSurface>(s)
258  );
259 
260  // Flip surface so outsidePt is outside.
261  bool anyFlipped = orientedSurface::orient
262  (
263  shell,
264  outsidePt,
265  true
266  );
267 
268  if (anyFlipped)
269  {
270  // orientedSurface will have done a clearOut of the surface.
271  // we could do a clearout of the triSurfacees::trees()
272  // but these aren't affected by orientation
273  // (except for cached
274  // sideness which should not be set at this point.
275  // !!Should check!)
276 
277  Info<< "refinementRegions : Flipped orientation of surface "
278  << s.name()
279  << " so point " << outsidePt << " is outside." << endl;
280  }
281  }
282  }
283  }
284 }
285 
286 
287 Foam::scalar Foam::refinementRegions::interpolate
288 (
289  const searchableSurfaces::triSurface& tsm,
290  const triSurfacePointScalarField& closeness,
291  const point& pt,
292  const label index
293 ) const
294 {
295  const barycentric2D bary
296  (
298  (
299  tsm.points(),
300  tsm.Foam::triSurface::operator[](index)
301  ).pointToBarycentric(pt)
302  );
303 
304  const labelledTri& lf = tsm.localFaces()[index];
305 
306  return closeness[lf[0]]*bary[0]
307  + closeness[lf[1]]*bary[1]
308  + closeness[lf[2]]*bary[2];
309 }
310 
311 
312 void Foam::refinementRegions::findHigherLevel
313 (
314  const pointField& pt,
315  const label shelli,
316  const scalar level0EdgeLength,
317  labelList& maxLevel
318 ) const
319 {
320  const labelList& levels = levels_[shelli];
321 
322  if (modes_[shelli] == refineMode::distance)
323  {
324  // Distance mode.
325 
326  const scalarField& distances = distances_[shelli];
327 
328  // Collect all those points that have a current maxLevel less than
329  // (any of) the shell. Also collect the furthest distance allowable
330  // to any shell with a higher level.
331 
332  pointField candidates(pt.size());
333  labelList candidateMap(pt.size());
334  scalarField candidateDistSqr(pt.size());
335  label candidatei = 0;
336 
337  forAll(maxLevel, pointi)
338  {
339  forAllReverse(levels, leveli)
340  {
341  if (levels[leveli] > maxLevel[pointi])
342  {
343  candidates[candidatei] = pt[pointi];
344  candidateMap[candidatei] = pointi;
345  candidateDistSqr[candidatei] = sqr(distances[leveli]);
346  candidatei++;
347  break;
348  }
349  }
350  }
351  candidates.setSize(candidatei);
352  candidateMap.setSize(candidatei);
353  candidateDistSqr.setSize(candidatei);
354 
355  // Do the expensive nearest test only for the candidate points.
356  List<pointIndexHit> nearInfo;
357  allGeometry_[shells_[shelli]].findNearest
358  (
359  candidates,
360  candidateDistSqr,
361  nearInfo
362  );
363 
364  // Update maxLevel
365  forAll(nearInfo, candidatei)
366  {
367  if (nearInfo[candidatei].hit())
368  {
369  // Check which level it actually is in.
370  label minDistI = findLower
371  (
372  distances,
373  mag(nearInfo[candidatei].hitPoint()-candidates[candidatei])
374  );
375 
376  label pointi = candidateMap[candidatei];
377 
378  // pt is in between shell[minDistI] and shell[minDistI+1]
379  maxLevel[pointi] = levels[minDistI+1];
380  }
381  }
382  }
383  else if
384  (
385  modes_[shelli] == refineMode::insideSpan
386  || modes_[shelli] == refineMode::outsideSpan
387  )
388  {
389  const searchableSurfaces::triSurface& tsm =
390  refCast<const searchableSurfaces::triSurface>
391  (allGeometry_[shells_[shelli]]);
392 
393  // Collect all those points that have a current maxLevel less than
394  // the maximum and furthest distance allowable for the shell.
395 
396  pointField candidates(pt.size());
397  labelList candidateMap(pt.size());
398  scalarField candidateDistSqr(pt.size());
399  label candidatei = 0;
400 
401  forAll(pt, pointi)
402  {
403  if (levels[0] > maxLevel[pointi])
404  {
405  candidates[candidatei] = pt[pointi];
406  candidateMap[candidatei] = pointi;
407  candidateDistSqr[candidatei] = sqr(distances_[shelli][0]);
408  candidatei++;
409  }
410  }
411  candidates.setSize(candidatei);
412  candidateMap.setSize(candidatei);
413  candidateDistSqr.setSize(candidatei);
414 
415  // Do the expensive nearest test only for the candidate points.
416  List<pointIndexHit> nearInfo;
417  tsm.findNearest
418  (
419  candidates,
420  candidateDistSqr,
421  nearInfo
422  );
423 
424  // Minimum span for the maximum level specified
425  const scalar minSpan
426  (
427  cellsAcrossSpan_[shelli]*level0EdgeLength/(1 << (levels[0] - 1))
428  );
429 
430  // Update maxLevel
431  forAll(nearInfo, candidatei)
432  {
433  if (nearInfo[candidatei].hit())
434  {
435  const scalar span
436  (
438  (
439  tsm,
440  closeness_[shelli],
441  nearInfo[candidatei].rawPoint(),
442  nearInfo[candidatei].index()
443  )
444  );
445 
446  if (span > minSpan)
447  {
448  const label level
449  (
450  log2(cellsAcrossSpan_[shelli]*level0EdgeLength/span) + 1
451  );
452 
453  maxLevel[candidateMap[candidatei]] = min(levels[0], level);
454  }
455  else
456  {
457  maxLevel[candidateMap[candidatei]] = levels[0];
458  }
459  }
460  }
461  }
462  else
463  {
464  // Inside/outside mode
465 
466  // Collect all those points that have a current maxLevel less than the
467  // shell.
468 
469  pointField candidates(pt.size());
470  labelList candidateMap(pt.size());
471  label candidatei = 0;
472 
473  forAll(maxLevel, pointi)
474  {
475  if (levels[0] > maxLevel[pointi])
476  {
477  candidates[candidatei] = pt[pointi];
478  candidateMap[candidatei] = pointi;
479  candidatei++;
480  }
481  }
482  candidates.setSize(candidatei);
483  candidateMap.setSize(candidatei);
484 
485  // Do the expensive nearest test only for the candidate points.
486  List<volumeType> volType;
487  allGeometry_[shells_[shelli]].getVolumeType(candidates, volType);
488 
489  forAll(volType, i)
490  {
491  label pointi = candidateMap[i];
492 
493  if
494  (
495  (
496  modes_[shelli] == refineMode::inside
497  && volType[i] == volumeType::inside
498  )
499  || (
500  modes_[shelli] == refineMode::outside
501  && volType[i] == volumeType::outside
502  )
503  )
504  {
505  maxLevel[pointi] = levels[0];
506  }
507  }
508  }
509 }
510 
511 
512 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
513 
515 (
516  const searchableSurfaceList& allGeometry,
517  const dictionary& shellsDict
518 )
519 :
520  allGeometry_(allGeometry)
521 {
522  // Wildcard specification : loop over all surfaces and try to find a match.
523 
524  // Count number of shells.
525  label shelli = 0;
526  forAll(allGeometry.names(), geomi)
527  {
528  const word& geomName = allGeometry_.names()[geomi];
529 
530  if (shellsDict.found(geomName))
531  {
532  shelli++;
533  }
534  }
535 
536  // Size lists
537  shells_.setSize(shelli);
538  modes_.setSize(shelli);
539  distances_.setSize(shelli);
540  levels_.setSize(shelli);
541  cellsAcrossSpan_.setSize(shelli);
542  closeness_.setSize(shelli);
543 
544  HashSet<word> unmatchedKeys(shellsDict.toc());
545  shelli = 0;
546 
547  forAll(allGeometry_.names(), geomi)
548  {
549  const word& geomName = allGeometry_.names()[geomi];
550 
551  const entry* ePtr = shellsDict.lookupEntryPtr(geomName, false, true);
552 
553  if (ePtr)
554  {
555  const dictionary& dict = ePtr->dict();
556  unmatchedKeys.erase(ePtr->keyword());
557 
558  shells_[shelli] = geomi;
559  modes_[shelli] = refineModeNames_.read(dict.lookup("mode"));
560 
561  // Read pairs of distance+level
562  setAndCheckLevels(shelli, dict);
563 
564  if
565  (
566  modes_[shelli] == refineMode::insideSpan
567  || modes_[shelli] == refineMode::outsideSpan
568  )
569  {
570  const searchableSurface& surface = allGeometry_[geomi];
571 
572  if (isA<searchableSurfaces::triSurface>(surface))
573  {
574  dict.lookup("cellsAcrossSpan") >> cellsAcrossSpan_[shelli];
575 
576  const searchableSurfaces::triSurface& tsm =
577  refCast<const searchableSurfaces::triSurface>
578  (surface);
579 
580  closeness_.set
581  (
582  shelli,
584  (
585  IOobject
586  (
587  surface.name()
588  + ".closeness."
589  + (
590  modes_[shelli] == refineMode::insideSpan
591  ? "internal"
592  : "external"
593  )
594  + "PointCloseness",
595  surface.searchableSurface::time().constant(),
597  (
598  surface.searchableSurface::time()
599  ),
600  surface.searchableSurface::time(),
602  ),
603  tsm
604  )
605  );
606  }
607  else
608  {
609  FatalIOErrorInFunction(shellsDict)
610  << "Surface " << surface.name()
611  << " is not a triSurface as required by"
612  " refinement modes "
613  << refineModeNames_[refineMode::insideSpan]
614  << " and " << refineModeNames_[refineMode::outsideSpan]
615  << exit(FatalIOError);
616  }
617  }
618 
619  shelli++;
620  }
621  }
622 
623  if (unmatchedKeys.size() > 0)
624  {
626  (
627  shellsDict
628  ) << "Not all entries in refinementRegions dictionary were used."
629  << " The following entries were not used : "
630  << unmatchedKeys.sortedToc()
631  << endl;
632  }
633 
634  // Orient shell surfaces before any searching is done. Note that this
635  // only needs to be done for inside or outside. Orienting surfaces
636  // constructs lots of addressing which we want to avoid.
637  orient();
638 }
639 
640 
641 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
642 
644 {
645  label overallMax = 0;
646 
647  forAll(levels_, shelli)
648  {
649  overallMax = max(overallMax, max(levels_[shelli]));
650  }
651 
652  return overallMax;
653 }
654 
655 
656 void Foam::refinementRegions::findHigherLevel
657 (
658  const pointField& pt,
659  const labelList& ptLevel,
660  const scalar level0EdgeLength,
661  labelList& maxLevel
662 ) const
663 {
664  // Maximum level of any shell. Start off with level of point.
665  maxLevel = ptLevel;
666 
667  forAll(shells_, shelli)
668  {
669  findHigherLevel(pt, shelli, level0EdgeLength, maxLevel);
670  }
671 }
672 
673 
674 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:433
#define forAllReverse(list, i)
Reverse loop across all elements in list.
Definition: UList.H:445
Field with dimensions and associated with geometry type GeoMesh which is used to size the field and a...
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
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:54
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:82
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:578
wordList toc() const
Return the table of contents.
Definition: dictionary.C:982
bool found(const word &, bool recursive=false, bool patternMatch=true) const
Search dictionary for given keyword.
Definition: dictionary.C:539
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:
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 surface geometry formed of discrete facets, e.g. triangles and/or quadrilaterals,...
A class for handling words, derived from string.
Definition: word.H:62
#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:258
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
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
void mag(LagrangianPatchField< scalar > &f, const LagrangianPatchField< Type > &f1)
Field< scalar > scalarField
Specialisation of Field<T> for scalar.
layerAndWeight min(const layerAndWeight &a, const layerAndWeight &b)
DimensionedField< scalar, triSurfacePointGeoMesh, Field > triSurfacePointScalarField
layerAndWeight max(const layerAndWeight &a, const layerAndWeight &b)
IOerror FatalIOError
void sqr(LagrangianPatchField< typename outerProduct< Type, Type >::type > &f, const LagrangianPatchField< Type > &f1)
triangle< point, const point & > triPointRef
Definition: triPointRef.H:44
label readLabel(Istream &is)
Definition: label.H:64
error FatalError
dictionary dict