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