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