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