NEMoSys  0.63.0
A modular, extensible resource with robust automated mesh generation, mesh quality analysis, adaptive mesh refinement, and data transfer between arbitrary meshes.
AMRFoam.C
Go to the documentation of this file.
1 /*******************************************************************************
2 * Promesh *
3 * Copyright (C) 2022, IllinoisRocstar LLC. All rights reserved. *
4 * *
5 * Promesh is the property of IllinoisRocstar LLC. *
6 * *
7 * IllinoisRocstar LLC *
8 * Champaign, IL *
9 * www.illinoisrocstar.com *
10 * promesh@illinoisrocstar.com *
11 *******************************************************************************/
12 /*******************************************************************************
13 * This file is part of Promesh *
14 * *
15 * This version of Promesh is free software: you can redistribute it and/or *
16 * modify it under the terms of the GNU Lesser General Public License as *
17 * published by the Free Software Foundation, either version 3 of the License, *
18 * or (at your option) any later version. *
19 * *
20 * Promesh is distributed in the hope that it will be useful, but WITHOUT ANY *
21 * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS *
22 * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more *
23 * details. *
24 * *
25 * You should have received a copy of the GNU Lesser General Public License *
26 * along with this program. If not, see <https://www.gnu.org/licenses/>. *
27 * *
28 *******************************************************************************/
29 #include <cstdlib>
30 
31 #include <iostream>
32 #include <string>
33 #include <vector>
34 
35 #include "AuxiliaryFunctions.H"
36 #include "Refinement/AMRFoam.H"
37 
38 #include <IFstream.H>
39 #include <IOdictionary.H>
40 #include <OFstream.H>
41 #include <ReadFields.H>
42 #include <Time.H>
43 #include <argList.H>
44 #include <cellSet.H>
45 #include <fvMesh.H>
46 #include <fvcGrad.H>
47 #include <hexRef8.H>
48 #include <mapPolyMesh.H>
49 #include <motionSolver.H>
50 #include <pointFields.H>
51 #include <pointMesh.H>
52 #include <polyMesh.H>
53 #include <polyTopoChange.H>
54 #include <sigFpe.H>
55 #include <surfaceFields.H>
56 #include <surfaceInterpolate.H>
57 #include <syncTools.H>
58 
59 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
60 namespace Foam {
61 
62 AMRFoam::AMRFoam(const Foam::IOobject &iomesh)
63  : dynamicFvMesh(iomesh),
64  meshCutter_(*this),
65  dumpLevel_(true),
66  nRefinementIterations_(0),
67  protectedCell_(nCells()) {
70 }
71 
73  // Check if motion needs to be enabled
74  dictionary refineDict(IOdictionary(
75  IOobject("dynamicMeshDict", time().constant(), *this,
76  IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE, false)));
77 
78  word subType = word(refineDict.lookup("dynamicFvMesh"));
79  if (subType == "dynamicMotionSolverFvMesh") { enableMotion = true; }
80 
81  if (enableMotion) { motionPtr_ = motionSolver::New(*this); }
82 }
83 
85 
87  const labelList &cellLevel = meshCutter_.cellLevel();
88  const labelList &pointLevel = meshCutter_.pointLevel();
89 
90  // Set cells that should not be refined.
91  // This is currently any cell which does not have 8 anchor points or
92  // uses any face which does not have 4 anchor points.
93  // Note: do not use cellPoint addressing
94 
95  // Count number of points <= cellLevel
96  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
97 
98  labelList nAnchors(nCells(), Zero);
99 
100  label nProtected = 0;
101 
102  forAll (pointCells(), pointi) {
103  const labelList &pCells = pointCells()[pointi];
104 
105  for (const label celli : pCells) {
106  if (!protectedCell_.test(celli)) {
107  if (pointLevel[pointi] <= cellLevel[celli]) {
108  ++nAnchors[celli];
109 
110  if (nAnchors[celli] > 8) {
111  protectedCell_.set(celli);
112  nProtected++;
113  }
114  }
115  }
116  }
117  }
118 
119  // Count number of points <= faceLevel
120  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
121  // Bit tricky since proc face might be one more refined than the owner since
122  // the coupled one is refined.
123 
124  {
125  labelList neiLevel(nFaces());
126 
127  for (label facei = 0; facei < nInternalFaces(); ++facei) {
128  neiLevel[facei] = cellLevel[faceNeighbour()[facei]];
129  }
130  for (label facei = nInternalFaces(); facei < nFaces(); ++facei) {
131  neiLevel[facei] = cellLevel[faceOwner()[facei]];
132  }
133  syncTools::swapFaceList(*this, neiLevel);
134 
135  bitSet protectedFace(nFaces());
136 
137  forAll (faceOwner(), facei) {
138  const label faceLevel =
139  max(cellLevel[faceOwner()[facei]], neiLevel[facei]);
140 
141  const face &f = faces()[facei];
142 
143  label nnAnchors = 0;
144 
145  for (const label pointi : f) {
146  if (pointLevel[pointi] <= faceLevel) {
147  ++nnAnchors;
148 
149  if (nnAnchors > 4) {
150  protectedFace.set(facei);
151  break;
152  }
153  }
154  }
155  }
156 
157  syncTools::syncFaceList(*this, protectedFace, orEqOp<unsigned int>());
158 
159  for (label facei = 0; facei < nInternalFaces(); ++facei) {
160  if (protectedFace.test(facei)) {
161  protectedCell_.set(faceOwner()[facei]);
162  nProtected++;
163  protectedCell_.set(faceNeighbour()[facei]);
164  nProtected++;
165  }
166  }
167  for (label facei = nInternalFaces(); facei < nFaces(); ++facei) {
168  if (protectedFace.test(facei)) {
169  protectedCell_.set(faceOwner()[facei]);
170  nProtected++;
171  }
172  }
173 
174  // Also protect any cells that are less than hex
175  forAll (cells(), celli) {
176  const cell &cFaces = cells()[celli];
177 
178  if (cFaces.size() < 6) {
179  if (protectedCell_.set(celli)) { nProtected++; }
180 
181  } else {
182  for (const label cfacei : cFaces) {
183  if (faces()[cfacei].size() < 4) {
184  if (protectedCell_.set(celli)) { nProtected++; }
185  break;
186  }
187  }
188  }
189  }
190 
191  // Check cells for 8 corner points
193 
194  if (enableMotion) {
195  // Block boundary cells from refinement
196  const polyBoundaryMesh &patches = boundaryMesh();
197  wordList ptchTypes = patches.types();
198 
199  labelList assignedScore(nCells(), 0);
200 
201  forAll (patches, patchI) {
202  if (ptchTypes[patchI] != "empty") {
203  auto allCells = patches[patchI].faceCells();
204  forAll (allCells, cellI) {
205  assignedScore[allCells[cellI]] += 1;
206  // Find all neighbours and assign the score of one
207  auto neiCells = cellCells()[allCells[cellI]];
208  forAll (neiCells, nCellI) { assignedScore[neiCells[nCellI]] += 1; }
209  }
210  }
211  }
212 
213  forAll (assignedScore, scoreI) {
214  if (assignedScore[scoreI] >= 2) { protectedCell_.set(scoreI); }
215  }
216  }
217  }
218 
219  if (!returnReduce(protectedCell_.any(), orOp<bool>())) {
220  protectedCell_.clear();
221  } else {
222  cellSet protectedCells(*this, "protectedCells",
223  HashSetOps::used(protectedCell_));
224  Info << "Detected " << returnReduce(protectedCells.size(), sumOp<label>())
225  << " cells that are protected from refinement."
226  << " Writing these to cellSet " << protectedCells.name() << "."
227  << endl;
228 
229  protectedCells.write();
230  }
231 }
232 
234  // Re-read dictionary. Chosen since usually -small so trivial amount
235  // of time compared to actual refinement. Also very useful to be able
236  // to modify on-the-fly.
237  dictionary refineDict(
238  IOdictionary(IOobject("dynamicMeshDict", time().constant(), *this,
239  IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE,
240  false))
241  .optionalSubDict("dynamicRefineMotionFvMeshCoeffs"));
242 
243  auto fluxVelocities = refineDict.get<List<Pair<word>>>("correctFluxes");
244 
245  // Rework into hashtable.
246  correctFluxes_.resize(fluxVelocities.size());
247  for (const auto &pr : fluxVelocities) {
248  correctFluxes_.insert(pr.first(), pr.second());
249  }
250 
251  refineDict.readEntry("dumpLevel", dumpLevel_);
252  const label refineInterval = refineDict.get<label>("refineInterval");
253 
254  bool hasChanged = false;
255 
256  if (refineInterval == 0) {
257  polyMesh::topoChanging(hasChanged);
258  return hasChanged;
259  } else if (refineInterval < 0) {
260  FatalErrorInFunction << "Illegal refineInterval " << refineInterval << nl
261  << "The refineInterval value should"
262  << " be >= 1." << nl << exit(FatalError);
263  }
264 
265  if (time().timeIndex() > 0 && time().timeIndex() % refineInterval == 0) {
266  label maxCells = refineDict.get<label>("maxCells");
267 
268  if (maxCells < 0) maxCells = 500000;
269 
270  if (maxCells <= 0) {
271  FatalErrorInFunction
272  << "Illegal maximum number of cells " << maxCells << nl
273  << "The maxCells setting in the dynamicMeshDict should"
274  << " be > 0." << nl << exit(FatalError);
275  }
276 
277  const label maxRefinement = refineDict.get<label>("maxRefinement");
278 
279  if (maxRefinement <= 0) {
280  FatalErrorInFunction
281  << "Illegal maximum refinement level " << maxRefinement << nl
282  << "The maxCells setting in the dynamicMeshDict should"
283  << " be > 0." << nl << exit(FatalError);
284  }
285 
286  const word fieldName(refineDict.get<word>("field"));
287 
288  const volScalarField &vFld = lookupObject<volScalarField>(fieldName);
289 
290  const scalar lowerRefineLevel = refineDict.get<scalar>("lowerRefineLevel");
291  const scalar upperRefineLevel = refineDict.get<scalar>("upperRefineLevel");
292  const scalar unrefineBelow =
293  refineDict.getOrDefault<scalar>("unrefineLevel", GREAT);
294  const scalar unrefineAbove =
295  refineDict.getOrDefault<scalar>("unrefineAbove", 50000);
296  const label nBufferLayers = refineDict.get<label>("nBufferLayers");
297 
298  // Cells marked for refinement or otherwise protected from unrefinement.
299  bitSet refineCell(nCells());
300 
301  // Determine candidates for refinement (looking at field only)
302  selectRefineCandidates(lowerRefineLevel, upperRefineLevel, vFld,
303  refineCell);
304 
305  if (globalData().nTotalCells() < maxCells) {
306  // Select subset of candidates. Take into account max allowable
307  // cells, refinement level, protected cells.
308  labelList cellsToRefine(
309  selectRefineCells(maxCells, maxRefinement, refineCell));
310 
311  const label nCellsToRefine =
312  returnReduce(cellsToRefine.size(), sumOp<label>());
313 
314  if (nCellsToRefine > 0) {
315  // Refine/update mesh and map fields
316  autoPtr<mapPolyMesh> map = refine(cellsToRefine);
317 
318  // Update refineCell. Note that some of the marked ones have
319  // not been refined due to constraints.
320  {
321  const labelList &cellMap = map().cellMap();
322  const labelList &reverseCellMap = map().reverseCellMap();
323 
324  bitSet newRefineCell(cellMap.size());
325 
326  forAll (cellMap, celli) {
327  const label oldCelli = cellMap[celli];
328  if ((oldCelli < 0) || (reverseCellMap[oldCelli] != celli) ||
329  (refineCell.test(oldCelli))) {
330  newRefineCell.set(celli);
331  }
332  }
333  refineCell.transfer(newRefineCell);
334  }
335 
336  // Extend with a buffer layer to prevent neighbouring points
337  // being unrefined.
338  for (label i = 0; i < nBufferLayers; ++i) {
339  extendMarkedCells(refineCell);
340  }
341 
342  hasChanged = true;
343  }
344  }
345 
346  {
347  // Select unrefineable points that are not marked in refineCell
348  labelList pointsToUnrefine(selectUnrefinePoints(
349  unrefineAbove, unrefineBelow, refineCell, maxCellField(vFld)));
350 
351  const label nSplitPoints =
352  returnReduce(pointsToUnrefine.size(), sumOp<label>());
353 
354  if (nSplitPoints > 0) {
355  // Refine/update mesh
356  unrefine(pointsToUnrefine);
357 
358  hasChanged = true;
359  }
360  }
361 
362  if ((nRefinementIterations_ % 1) == 0) {
363  // Compact refinement history occasionally (how often?).
364  // Unrefinement causes holes in the refinementHistory.
365  const_cast<refinementHistory &>(meshCutter().history()).compact();
366  }
368  }
369 
370  writeHistory();
371 
372  writeMesh();
373 
374  topoChanging(hasChanged);
375 
376  if (hasChanged) {
377  // Reset moving flag (if any). If not using inflation we'll not move,
378  // if are using inflation any follow on movePoints will set it.
379  moving(false);
380  } else {
381  if (enableMotion) solveMotion();
382  }
383  return hasChanged;
384 }
385 
386 bool AMRFoam::updateAMR(const int &refineInterval, const int &maxRefinement,
387  volScalarField &vFld, const double &lowerRefineLevel,
388  const double &upperRefineLevel,
389  const double &unrefineAbove,
390  const double &unrefineBelow, const int &nBufferLayers,
391  const int &maxCells) {
392  bool hasChanged = false;
393 
394  if (refineInterval == 0) {
395  polyMesh::topoChanging(hasChanged);
396  return hasChanged;
397  } else if (refineInterval < 0) {
398  FatalErrorInFunction << "Illegal refineInterval " << refineInterval << nl
399  << "The refineInterval value should"
400  << " be >= 1." << nl << exit(FatalError);
401  }
402 
403  if (time().timeIndex() > 0 && time().timeIndex() % refineInterval == 0) {
404  if (maxCells <= 0) {
405  FatalErrorInFunction
406  << "Illegal maximum number of cells " << maxCells << nl
407  << "The maxCells setting in the dynamicMeshDict should"
408  << " be > 0." << nl << exit(FatalError);
409  }
410 
411  if (maxRefinement <= 0) {
412  FatalErrorInFunction
413  << "Illegal maximum refinement level " << maxRefinement << nl
414  << "The maxCells setting in the dynamicMeshDict should"
415  << " be > 0." << nl << exit(FatalError);
416  }
417 
418  // Cells marked for refinement or otherwise protected from unrefinement.
419  bitSet refineCell(nCells());
420 
421  // Determine candidates for refinement (looking at field only)
422  selectRefineCandidates(lowerRefineLevel, upperRefineLevel, vFld,
423  refineCell);
424 
425  if (globalData().nTotalCells() < maxCells) {
426  // Select subset of candidates. Take into account max allowable
427  // cells, refinement level, protected cells.
428  labelList cellsToRefine(
429  selectRefineCells(maxCells, maxRefinement, refineCell));
430 
431  const label nCellsToRefine =
432  returnReduce(cellsToRefine.size(), sumOp<label>());
433 
434  if (nCellsToRefine > 0) {
435  // Refine/update mesh and map fields
436  autoPtr<mapPolyMesh> map = refine(cellsToRefine);
437 
438  // Update refineCell. Note that some of the marked ones have
439  // not been refined due to constraints.
440  {
441  const labelList &cellMap = map().cellMap();
442  const labelList &reverseCellMap = map().reverseCellMap();
443 
444  bitSet newRefineCell(cellMap.size());
445 
446  forAll (cellMap, celli) {
447  const label oldCelli = cellMap[celli];
448  if ((oldCelli < 0) || (reverseCellMap[oldCelli] != celli) ||
449  (refineCell.test(oldCelli))) {
450  newRefineCell.set(celli);
451  }
452  }
453  refineCell.transfer(newRefineCell);
454  }
455 
456  // Extend with a buffer layer to prevent neighbouring points
457  // being unrefined.
458  for (label i = 0; i < nBufferLayers; ++i) {
459  extendMarkedCells(refineCell);
460  }
461 
462  hasChanged = true;
463  }
464  }
465 
466  {
467  // Select unrefineable points that are not marked in refineCell
468  labelList pointsToUnrefine(selectUnrefinePoints(
469  unrefineAbove, unrefineBelow, refineCell, maxCellField(vFld)));
470 
471  const label nSplitPoints =
472  returnReduce(pointsToUnrefine.size(), sumOp<label>());
473 
474  if (nSplitPoints > 0) {
475  // Refine/update mesh
476  unrefine(pointsToUnrefine);
477 
478  hasChanged = true;
479  }
480  }
481 
482  if ((nRefinementIterations_ % 10) == 0) {
483  // Compact refinement history occasionally (how often?).
484  // Unrefinement causes holes in the refinementHistory.
485  const_cast<refinementHistory &>(meshCutter().history()).compact();
486  }
488  }
489 
490  topoChanging(hasChanged);
491 
492  if (hasChanged) {
493  // Reset moving flag (if any). If not using inflation we'll not move,
494  // if are using inflation any follow on movePoints will set it.
495  moving(false);
496  } else {
497  if (enableMotion) solveMotion();
498  }
499 
500  if (writeField) vFld.write();
501 
503 
504  if (writeMeshData) writeMesh();
505 
506  return hasChanged;
507 }
508 
509 // New method for machine learning model
510 bool AMRFoam::updateAMRML(const int &refineInterval, const int &maxRefinement,
511  const int &nBufferLayers, const int &maxCells,
512  volScalarField &vFld) {
513  bool hasChanged = false;
514  if (refineInterval == 0) {
515  polyMesh::topoChanging(hasChanged);
516  return hasChanged;
517  } else if (refineInterval < 0) {
518  FatalErrorInFunction << "Illegal refineInterval " << refineInterval << nl
519  << "The refineInterval value should"
520  << " be >= 1." << nl << exit(FatalError);
521  }
522 
523  // if (maxCells < 0)
524  // maxCells = 500000;
525 
526  // Cannot refine at time = 0;
527  if (fvMesh::time().timeIndex() > 0 &&
528  fvMesh::time().timeIndex() % refineInterval == 0) {
529  if (maxCells <= 0) {
530  FatalErrorInFunction << "Illegal maximum number of cells " << maxCells
531  << nl << "The maxCells value should"
532  << " be > 0." << nl << exit(FatalError);
533  }
534 
535  if (maxRefinement <= 0) {
536  FatalErrorInFunction << "Illegal maximum refinement level "
537  << maxRefinement << nl << "The maxCells value should"
538  << " be > 0." << nl << exit(FatalError);
539  }
540 
541  // Cells marked for refinement or otherwise protected from unrefinement.
542  bitSet refineCell(primitiveMesh::nCells());
543 
544  // Selecting refinement candidates based on lower and upper refinement
545  // levels in scalar field
546  selectRefineCandidates(0.5, 1.5, vFld, refineCell);
547 
548  // Refinement Procedure Loop
549  if (polyMesh::globalData().nTotalCells() < maxCells) {
550  // Select subset of candidates. Take into account max allowable
551  // cells, refinement level, protected cells.
552  labelList cellsToRefine(
553  selectRefineCells(maxCells, maxRefinement, refineCell));
554 
555  label nCellsToRefine = returnReduce(cellsToRefine.size(), sumOp<label>());
556 
557  if (nCellsToRefine > 0) {
558  // Refine/update mesh and map fields
559  autoPtr<mapPolyMesh> map = refine(cellsToRefine);
560 
561  // Update refineCell. Note that some of the marked ones have not been
562  // refined due to constraints.
563  const labelList &cellMap = map().cellMap();
564  const labelList &reverseCellMap = map().reverseCellMap();
565 
566  bitSet newRefineCell(cellMap.size());
567 
568  forAll (cellMap, celli) {
569  label oldCelli = cellMap[celli];
570 
571  if (oldCelli < 0)
572  newRefineCell.set(celli, 1);
573  else if (reverseCellMap[oldCelli] != celli)
574  newRefineCell.set(celli, 1);
575  else
576  newRefineCell.set(celli, refineCell.get(oldCelli));
577  }
578  refineCell.transfer(newRefineCell);
579 
580  // Extended with a buffer layer to prevent neighbouring points
581  // being unrefined
582  for (label i = 0; i < nBufferLayers; i++) {
583  extendMarkedCells(refineCell);
584  }
585 
586  hasChanged = true;
587  }
588  }
589 
590  // Select unrefinable points that are not marked in refineCell
591  labelList pointsToUnrefine(
592  selectUnrefinePoints(-1, 0.5, refineCell, maxCellField(vFld)));
593 
594  label nSplitPoints = returnReduce(pointsToUnrefine.size(), sumOp<label>());
595  autoPtr<mapPolyMesh> mapUnrefine;
596 
597  if (nSplitPoints > 0) {
598  // Refine/update mesh
599  unrefine(pointsToUnrefine);
600  hasChanged = true;
601  }
602 
603  if ((nRefinementIterations_ % 10) == 0) {
604  // Unrefinement causes holes in the refinementHistory
605  const_cast<refinementHistory &>(meshCutter().history()).compact();
606  }
608  }
609 
610  polyMesh::topoChanging(hasChanged);
611  if (hasChanged) { polyMesh::moving(false); }
612 
613  return hasChanged;
614 }
615 
616 labelList AMRFoam::selectUnrefinePoints(const scalar unrefineAbove,
617  const scalar unrefineBelow,
618  const bitSet &markedCell,
619  const scalarField &pFld) const {
620  // All points that can be unrefined
621  const labelList splitPoints(meshCutter_.getSplitPoints());
622 
623  const labelListList &pointCells = this->pointCells();
624 
625  // If we have any protected cells make sure they also are not being
626  // unrefined
627 
628  bitSet protectedPoint(nPoints());
629 
630  if (!protectedCell_.empty()) {
631  // Get all points on a protected cell
632  forAll (pointCells, pointi) {
633  for (const label celli : pointCells[pointi]) {
634  if (protectedCell_.test(celli)) {
635  protectedPoint.set(pointi);
636  break;
637  }
638  }
639  }
640 
641  syncTools::syncPointList(*this, protectedPoint, orEqOp<unsigned int>(), 0u);
642 
643  DebugInfo << "From " << returnReduce(protectedCell_.count(), sumOp<label>())
644  << " protected cells found "
645  << returnReduce(protectedPoint.count(), sumOp<label>())
646  << " protected points." << endl;
647  }
648 
649  DynamicList<label> newSplitPoints(splitPoints.size());
650 
651  for (const label pointi : splitPoints) {
652  if ((!protectedPoint[pointi] && pFld[pointi] < unrefineBelow) ||
653  (!protectedPoint[pointi] && pFld[pointi] > unrefineAbove)) {
654  // Check that all cells are not marked
655  bool hasMarked = false;
656 
657  for (const label celli : pointCells[pointi]) {
658  if (markedCell.test(celli)) {
659  hasMarked = true;
660  break;
661  }
662  }
663 
664  if (!hasMarked) { newSplitPoints.append(pointi); }
665  }
666  }
667 
668  newSplitPoints.shrink();
669 
670  // Guarantee 2:1 refinement after unrefinement
671  labelList consistentSet(
672  meshCutter_.consistentUnrefinement(newSplitPoints, false));
673  Info << "Selected " << returnReduce(consistentSet.size(), sumOp<label>())
674  << " split points out of a possible "
675  << returnReduce(splitPoints.size(), sumOp<label>()) << "." << endl;
676 
677  return consistentSet;
678 }
679 
680 scalarField AMRFoam::cell2Pt(const scalarField &vFld) {
681  scalarField pFld(nPoints());
682 
683  forAll (pointCells(), pointi) {
684  const labelList &pCells = pointCells()[pointi];
685 
686  scalar sum = 0.0;
687  forAll (pCells, i)
688  sum += vFld[pCells[i]];
689 
690  pFld[pointi] = sum / pCells.size();
691  }
692 
693  return pFld;
694 }
695 
697  // Just write refiment history in current time
698  const_cast<hexRef8 &>(meshCutter_).setInstance(time().timeName());
699  meshCutter_.write(true);
700 }
701 
702 const polyMesh &AMRFoam::getMesh() { return meshCutter_.mesh(); }
703 
705  writeObjectAMR(true);
706 }
707 
708 void AMRFoam::transformField(volScalarField &inField) {
709  double maxNum = 0;
710  for (int i = 0; i < inField.size(); i++) {
711  if (inField[i] < 0) {
712  // Do Nothing
713  } else if (inField[i] > maxNum) {
714  maxNum = inField[i];
715  } else {
716  // Do Nothing
717  }
718  }
719 
720  for (int i = 0; i < inField.size(); i++) inField[i] = inField[i] / maxNum;
721 }
722 
723 volScalarField AMRFoam::readIncomingCellField(const std::string &inName,
724  const std::string &outName) {
725  std::cout << " - Parsing Input file ... " << std::endl;
726  std::ifstream inFld(inName);
727  if (inFld.is_open()) {
728  std::string linesOut; // Temporary string for multiple uses
729  std::vector<std::string> myLines; // Stores whole file line by line
730  while (std::getline(inFld, linesOut)) myLines.push_back(linesOut);
731 
732  incomingField.clear();
733  incomingField.resize(myLines.size());
734 
735  for (int i = 0; i < (int)myLines.size(); i++)
736  incomingField[i] =
737  std::strtod(nemAux::strToChar(myLines[i]).get(), nullptr);
738 
739  volScalarField returnFld = initScalarField(outName);
740 
741  for (int i = 0; i < returnFld.size(); i++) returnFld[i] = incomingField[i];
742 
743  return returnFld;
744  } else {
745  std::cerr << "Cannot open/find " << inName << " file!" << std::endl;
746  throw;
747  }
748 }
749 
750 volScalarField AMRFoam::assignToVolScalarField(const std::vector<int> &vec) {
751  volScalarField vFld = initScalarField("vFld");
752  for (int i = 0; i < (int)vec.size(); i++) vFld[i] = vec[i];
753  return vFld;
754 }
755 
756 pointScalarField AMRFoam::readIncomingPtField(const std::string &inName,
757  const std::string &outName) {
758  std::cout << " - Parsing Input file ... " << std::endl;
759  std::ifstream inFld(inName);
760  if (inFld.is_open()) {
761  std::string linesOut; // Temporary string for multiple uses
762  std::vector<std::string> myLines; // Stores whole file line by line
763  while (std::getline(inFld, linesOut)) myLines.push_back(linesOut);
764 
765  incomingField.clear();
766  incomingField.resize(myLines.size());
767 
768  for (int i = 0; i < (int)myLines.size(); i++) {
769  incomingField[i] =
770  std::strtod(nemAux::strToChar(myLines[i]).get(), nullptr);
771  }
772 
773  pointScalarField returnFld = initPntSField(outName);
774 
775  for (int i = 0; i < returnFld.size(); i++) returnFld[i] = incomingField[i];
776 
777  return returnFld;
778  } else {
779  std::cerr << "Cannot open/find " << inName << " file!" << std::endl;
780  throw;
781  }
782 }
783 
784 volScalarField AMRFoam::readInitialField(const std::string &fldName) {
785  volScalarField meshFieldXY(IOobject(fldName, time().timeName(), *this,
786  IOobject::NO_READ, IOobject::AUTO_WRITE),
787  *this);
788 
789  return meshFieldXY;
790 }
791 
792 volScalarField AMRFoam::pF2vF(const pointScalarField &pntF,
793  const std::string &outName) {
794  volScalarField returnFld = initScalarField(outName);
795  return returnFld;
796 }
797 
798 volScalarField AMRFoam::initScalarField(const std::string &fldName) {
799  volScalarField emptyScalar(IOobject(fldName, time().timeName(), *this,
800  IOobject::NO_READ, IOobject::AUTO_WRITE),
801  *this, dimensionedScalar("scalar", dimLength, 0));
802 
803  return emptyScalar;
804 }
805 
806 volScalarField AMRFoam::initGradientField(const std::string &fldName) {
807  volScalarField emptyScalar(IOobject(fldName, time().timeName(), *this,
808  IOobject::NO_READ, IOobject::AUTO_WRITE),
809  *this, dimensionedScalar("scalar", dimless, 0));
810 
811  return emptyScalar;
812 }
813 
814 pointScalarField AMRFoam::initPntSField(const std::string &fldName) {
815  // Initialize a pointMesh object
816  pointMesh pMesh(*this);
817 
818  // Zero pointScalarField declaration. InternalField contains all mesh
819  // points. Can be overwritten with incoming stream.
820  pointScalarField initFieldZero(
821  IOobject(fldName, time().timeName(), *this, IOobject::NO_READ,
822  IOobject::AUTO_WRITE),
823  pMesh, dimensionedScalar("scalar", dimLength, 0));
824 
825  return initFieldZero;
826 }
827 
829 
831 
833 
834 volScalarField AMRFoam::getGradient(const volScalarField &fldGrd) {
835  volVectorField returnGrad(IOobject("fldName", time().timeName(), *this,
836  IOobject::NO_READ, IOobject::NO_WRITE),
837  *this, dimensionedVector("vector", dimless, Zero));
838 
839  returnGrad = Foam::fvc::grad(fldGrd);
840  returnGrad.write();
841  volScalarField returnScalar = initGradientField("gradient");
842  returnScalar = mag(returnGrad);
843 
844  return returnScalar;
845 }
846 
847 void AMRFoam::calculateProtectedCells(bitSet &unrefineableCell) const {
848  if (protectedCell_.empty()) {
849  unrefineableCell.clear();
850  return;
851  }
852 
853  const labelList &cellLevel = meshCutter_.cellLevel();
854 
855  unrefineableCell = protectedCell_;
856 
857  // Get neighbouring cell level
858  labelList neiLevel(nFaces() - nInternalFaces());
859 
860  for (label facei = nInternalFaces(); facei < nFaces(); ++facei) {
861  neiLevel[facei - nInternalFaces()] = cellLevel[faceOwner()[facei]];
862  }
863  syncTools::swapBoundaryFaceList(*this, neiLevel);
864 
865  bitSet seedFace;
866 
867  while (true) {
868  // Pick up faces on border of protected cells
869  seedFace.reset();
870  seedFace.resize(nFaces());
871 
872  for (label facei = 0; facei < nInternalFaces(); ++facei) {
873  const label own = faceOwner()[facei];
874  const label nei = faceNeighbour()[facei];
875 
876  if (
877  // Protected owner
878  (unrefineableCell.test(own) && (cellLevel[nei] > cellLevel[own])) ||
879  // Protected neighbour
880  (unrefineableCell.test(nei) && (cellLevel[own] > cellLevel[nei]))) {
881  seedFace.set(facei);
882  }
883  }
884  for (label facei = nInternalFaces(); facei < nFaces(); facei++) {
885  const label own = faceOwner()[facei];
886 
887  if (
888  // Protected owner
889  (unrefineableCell.test(own) &&
890  (neiLevel[facei - nInternalFaces()] > cellLevel[own]))) {
891  seedFace.set(facei);
892  }
893  }
894 
895  syncTools::syncFaceList(*this, seedFace, orEqOp<unsigned int>());
896 
897  // Extend unrefineableCell
898  bool hasExtended = false;
899 
900  for (label facei = 0; facei < nInternalFaces(); ++facei) {
901  if (seedFace.test(facei)) {
902  if (unrefineableCell.set(faceOwner()[facei])) { hasExtended = true; }
903  if (unrefineableCell.set(faceNeighbour()[facei])) {
904  hasExtended = true;
905  }
906  }
907  }
908  for (label facei = nInternalFaces(); facei < nFaces(); ++facei) {
909  if (seedFace.test(facei)) {
910  const label own = faceOwner()[facei];
911 
912  if (unrefineableCell.set(own)) { hasExtended = true; }
913  }
914  }
915 
916  if (!returnReduce(hasExtended, orOp<bool>())) { break; }
917  }
918 }
919 
921  dictionary refineDict(
922  IOdictionary(IOobject("dynamicMeshDict", time().constant(), *this,
923  IOobject::MUST_READ_IF_MODIFIED, IOobject::NO_WRITE,
924  false))
925  .optionalSubDict("dynamicRefineMotionFvMeshCoeffs"));
926 
927  List<Pair<word>> fluxVelocities =
928  List<Pair<word>>(refineDict.lookup("correctFluxes"));
929  // Rework into hashtable.
930  correctFluxes_.resize(fluxVelocities.size());
931  forAll (fluxVelocities, i)
932  correctFluxes_.insert(fluxVelocities[i][0], fluxVelocities[i][1]);
933 
934  dumpLevel_ = Switch(refineDict.lookup("dumpLevel"));
935 }
936 
937 void AMRFoam::mapFields(const mapPolyMesh &mpm) {
938  dynamicFvMesh::mapFields(mpm);
939 
940  // Correct the flux for modified/added faces. All the faces which only
941  // have been renumbered will already have been handled by the mapping.
942  {
943  const labelList &faceMap = mpm.faceMap();
944  const labelList &reverseFaceMap = mpm.reverseFaceMap();
945 
946  // Storage for any master faces. These will be the original faces
947  // on the coarse cell that get split into four (or rather the
948  // master face gets modified and three faces get added from the master)
949  // Estimate number of faces created
950 
951  bitSet masterFaces(nFaces());
952 
953  forAll (faceMap, facei) {
954  const label oldFacei = faceMap[facei];
955 
956  if (oldFacei >= 0) {
957  const label masterFacei = reverseFaceMap[oldFacei];
958 
959  if (masterFacei < 0) {
960  FatalErrorInFunction << "Problem: should not have removed faces"
961  << " when refining." << nl << "face:" << facei
962  << endl
963  << abort(FatalError);
964  } else if (masterFacei != facei) {
965  masterFaces.set(masterFacei);
966  }
967  }
968  }
969 
970  if (debug) {
971  Pout << "Found " << masterFaces.count() << " split faces " << endl;
972  }
973 
974  HashTable<surfaceScalarField *> fluxes(lookupClass<surfaceScalarField>());
975  forAllIters(fluxes, iter) {
976  if (!correctFluxes_.found(iter.key())) {
977  WarningInFunction
978  << "Cannot find surfaceScalarField " << iter.key()
979  << " in user-provided flux mapping table " << correctFluxes_ << endl
980  << " The flux mapping table is used to recreate the"
981  << " flux on newly created faces." << endl
982  << " Either add the entry if it is a flux or use (" << iter.key()
983  << " none) to suppress this warning." << endl;
984  continue;
985  }
986 
987  const word &UName = correctFluxes_[iter.key()];
988 
989  if (UName == "none") { continue; }
990 
991  surfaceScalarField &phi = *iter();
992 
993  if (UName == "NaN") {
994  Pout << "Setting surfaceScalarField " << iter.key() << " to NaN"
995  << endl;
996 
997  sigFpe::fillNan(phi.primitiveFieldRef());
998 
999  continue;
1000  }
1001 
1002  if (debug) {
1003  Pout << "Mapping flux " << iter.key() << " using interpolated flux "
1004  << UName << endl;
1005  }
1006 
1007  const surfaceScalarField phiU(
1008  fvc::interpolate(lookupObject<volVectorField>(UName)) & Sf());
1009 
1010  // Recalculate new internal faces.
1011  for (label facei = 0; facei < nInternalFaces(); ++facei) {
1012  const label oldFacei = faceMap[facei];
1013 
1014  if (oldFacei == -1) {
1015  // Inflated/appended
1016  phi[facei] = phiU[facei];
1017  } else if (reverseFaceMap[oldFacei] != facei) {
1018  // face-from-masterface
1019  phi[facei] = phiU[facei];
1020  }
1021  }
1022 
1023  // Recalculate new boundary faces.
1024  surfaceScalarField::Boundary &phiBf = phi.boundaryFieldRef();
1025 
1026  forAll (phiBf, patchi) {
1027  fvsPatchScalarField &patchPhi = phiBf[patchi];
1028  const fvsPatchScalarField &patchPhiU = phiU.boundaryField()[patchi];
1029 
1030  label facei = patchPhi.patch().start();
1031 
1032  forAll (patchPhi, i) {
1033  const label oldFacei = faceMap[facei];
1034 
1035  if (oldFacei == -1) {
1036  // Inflated/appended
1037  patchPhi[i] = patchPhiU[i];
1038  } else if (reverseFaceMap[oldFacei] != facei) {
1039  // face-from-masterface
1040  patchPhi[i] = patchPhiU[i];
1041  }
1042 
1043  ++facei;
1044  }
1045  }
1046 
1047  // Update master faces
1048  for (const label facei : masterFaces) {
1049  if (isInternalFace(facei)) {
1050  phi[facei] = phiU[facei];
1051  } else {
1052  const label patchi = boundaryMesh().whichPatch(facei);
1053  const label i = facei - boundaryMesh()[patchi].start();
1054 
1055  const fvsPatchScalarField &patchPhiU = phiU.boundaryField()[patchi];
1056 
1057  fvsPatchScalarField &patchPhi = phiBf[patchi];
1058 
1059  patchPhi[i] = patchPhiU[i];
1060  }
1061  }
1062  }
1063  }
1064 
1065  // Correct the flux for injected faces - these are the faces which have
1066  // no correspondence to the old mesh (i.e. added without a masterFace, edge
1067  // or point). An example is the internal faces from hexRef8.
1068  {
1069  const labelList &faceMap = mpm.faceMap();
1070 
1071  mapNewInternalFaces<scalar>(this->Sf(), this->magSf(), faceMap);
1072  mapNewInternalFaces<vector>(this->Sf(), this->magSf(), faceMap);
1073 
1074  // No oriented fields of more complex type
1075  mapNewInternalFaces<sphericalTensor>(faceMap);
1076  mapNewInternalFaces<symmTensor>(faceMap);
1077  mapNewInternalFaces<tensor>(faceMap);
1078  }
1079 }
1080 
1081 // Refines cells, maps fields and recalculates (an approximate) flux
1082 autoPtr<mapPolyMesh> Foam::AMRFoam::refine(const labelList &cellsToRefine) {
1083  // Mesh changing engine.
1084  polyTopoChange meshMod(*this);
1085 
1086  // Play refinement commands into mesh changer.
1087  meshCutter_.setRefinement(cellsToRefine, meshMod);
1088 
1089  // Create mesh (with inflation), return map from old to new mesh.
1090  // autoPtr<mapPolyMesh> map = meshMod.changeMesh(*this, true);
1091  autoPtr<mapPolyMesh> map = meshMod.changeMesh(*this, false);
1092 
1093  Info << "Refined from " << returnReduce(map().nOldCells(), sumOp<label>())
1094  << " to " << globalData().nTotalCells() << " cells." << endl;
1095 
1096  if (debug) {
1097  // Check map.
1098  for (label facei = 0; facei < nInternalFaces(); ++facei) {
1099  const label oldFacei = map().faceMap()[facei];
1100 
1101  if (oldFacei >= nInternalFaces()) {
1102  FatalErrorInFunction << "New internal face:" << facei
1103  << " fc:" << faceCentres()[facei]
1104  << " originates from boundary oldFace:" << oldFacei
1105  << abort(FatalError);
1106  }
1107  }
1108  }
1109 
1110  // Update fields
1111  updateMesh(*map);
1112 
1113  if (enableMotion) {
1114  // Solve for mesh motion and move points
1115  motionPtr_->updateMesh(*map);
1116  movePoints(motionPtr_->newPoints());
1117  }
1118 
1119  // Update numbering of cells/vertices.
1120  meshCutter_.updateMesh(*map);
1121 
1122  // Update numbering of protectedCell_
1123  if (!protectedCell_.empty()) {
1124  bitSet newProtectedCell(nCells());
1125  forAll (newProtectedCell, celli) {
1126  const label oldCelli = map().cellMap()[celli];
1127  if (protectedCell_.test(oldCelli)) { newProtectedCell.set(celli); }
1128  }
1129  protectedCell_.transfer(newProtectedCell);
1130  }
1131 
1132  // Debug: Check refinement levels (across faces only)
1133  meshCutter_.checkRefinementLevels(-1, labelList());
1134 
1135  return map;
1136 }
1137 
1138 autoPtr<mapPolyMesh> Foam::AMRFoam::unrefine(const labelList &splitPoints) {
1139  polyTopoChange meshMod(*this);
1140 
1141  // Play refinement commands into mesh changer.
1142  meshCutter_.setUnrefinement(splitPoints, meshMod);
1143 
1144  // Save information on faces that will be combined
1145  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
1146 
1147  // Find the faceMidPoints on cells to be combined.
1148  // for each face resulting of split of face into four store the
1149  // midpoint
1150  Map<label> faceToSplitPoint(3 * splitPoints.size());
1151 
1152  {
1153  for (const label pointi : splitPoints) {
1154  const labelList &pEdges = pointEdges()[pointi];
1155 
1156  for (const label edgei : pEdges) {
1157  const label otherPointi = edges()[edgei].otherVertex(pointi);
1158 
1159  const labelList &pFaces = pointFaces()[otherPointi];
1160 
1161  for (const label facei : pFaces) {
1162  faceToSplitPoint.insert(facei, otherPointi);
1163  }
1164  }
1165  }
1166  }
1167 
1168  // Change mesh and generate map.
1169  // autoPtr<mapPolyMesh> map = meshMod.changeMesh(*this, true);
1170  autoPtr<mapPolyMesh> map = meshMod.changeMesh(*this, false);
1171 
1172  Info << "Unrefined from " << returnReduce(map().nOldCells(), sumOp<label>())
1173  << " to " << globalData().nTotalCells() << " cells." << endl;
1174 
1175  // Update fields
1176  updateMesh(*map);
1177 
1178  if (enableMotion) {
1179  // Solve for mesh motion and move points
1180  motionPtr_->updateMesh(*map);
1181  movePoints(motionPtr_->newPoints());
1182  }
1183 
1184  // Correct the flux for modified faces.
1185  {
1186  const labelList &reversePointMap = map().reversePointMap();
1187  const labelList &reverseFaceMap = map().reverseFaceMap();
1188 
1189  HashTable<surfaceScalarField *> fluxes(lookupClass<surfaceScalarField>());
1190  forAllIters(fluxes, iter) {
1191  if (!correctFluxes_.found(iter.key())) {
1192  WarningInFunction
1193  << "Cannot find surfaceScalarField " << iter.key()
1194  << " in user-provided flux mapping table " << correctFluxes_ << endl
1195  << " The flux mapping table is used to recreate the"
1196  << " flux on newly created faces." << endl
1197  << " Either add the entry if it is a flux or use (" << iter.key()
1198  << " none) to suppress this warning." << endl;
1199  continue;
1200  }
1201 
1202  const word &UName = correctFluxes_[iter.key()];
1203 
1204  if (UName == "none") { continue; }
1205 
1206  DebugInfo << "Mapping flux " << iter.key() << " using interpolated flux "
1207  << UName << endl;
1208 
1209  surfaceScalarField &phi = *iter();
1210  surfaceScalarField::Boundary &phiBf = phi.boundaryFieldRef();
1211 
1212  const surfaceScalarField phiU(
1213  fvc::interpolate(lookupObject<volVectorField>(UName)) & Sf());
1214 
1215  forAllConstIters(faceToSplitPoint, iter2) {
1216  const label oldFacei = iter2.key();
1217  const label oldPointi = iter2.val();
1218  if (reversePointMap[oldPointi] < 0) {
1219  // midpoint was removed. See if face still exists.
1220  const label facei = reverseFaceMap[oldFacei];
1221 
1222  if (facei >= 0) {
1223  if (isInternalFace(facei)) {
1224  phi[facei] = phiU[facei];
1225  } else {
1226  label patchi = boundaryMesh().whichPatch(facei);
1227  label i = facei - boundaryMesh()[patchi].start();
1228 
1229  const fvsPatchScalarField &patchPhiU =
1230  phiU.boundaryField()[patchi];
1231  fvsPatchScalarField &patchPhi = phiBf[patchi];
1232  patchPhi[i] = patchPhiU[i];
1233  }
1234  }
1235  }
1236  }
1237  }
1238  }
1239 
1240  // Update numbering of cells/vertices.
1241  meshCutter_.updateMesh(*map);
1242 
1243  // Update numbering of protectedCell_
1244  if (!protectedCell_.empty()) {
1245  bitSet newProtectedCell(nCells());
1246 
1247  forAll (newProtectedCell, celli) {
1248  const label oldCelli = map().cellMap()[celli];
1249  if (protectedCell_.test(oldCelli)) { newProtectedCell.set(celli); }
1250  }
1251  protectedCell_.transfer(newProtectedCell);
1252  }
1253 
1254  // Debug: Check refinement levels (across faces only)
1255  meshCutter_.checkRefinementLevels(-1, labelList());
1256 
1257  return map;
1258 }
1259 
1260 scalarField AMRFoam::maxPointField(const scalarField &pFld) const {
1261  scalarField vFld(nCells(), -GREAT);
1262 
1263  forAll (pointCells(), pointi) {
1264  const labelList &pCells = pointCells()[pointi];
1265 
1266  for (const label celli : pCells) {
1267  vFld[celli] = max(vFld[celli], pFld[pointi]);
1268  }
1269  }
1270  return vFld;
1271 }
1272 
1273 scalarField AMRFoam::maxCellField(const volScalarField &vFld) const {
1274  scalarField pFld(nPoints(), -GREAT);
1275 
1276  forAll (pointCells(), pointi) {
1277  const labelList &pCells = pointCells()[pointi];
1278 
1279  for (const label celli : pCells) {
1280  pFld[pointi] = max(pFld[pointi], vFld[celli]);
1281  }
1282  }
1283  return pFld;
1284 }
1285 
1286 scalarField AMRFoam::cellToPoint(const scalarField &vFld) const {
1287  scalarField pFld(nPoints());
1288 
1289  forAll (pointCells(), pointi) {
1290  const labelList &pCells = pointCells()[pointi];
1291 
1292  scalar sum = 0.0;
1293  for (const label celli : pCells) { sum += vFld[celli]; }
1294  pFld[pointi] = sum / pCells.size();
1295  }
1296  return pFld;
1297 }
1298 
1299 scalarField AMRFoam::error(const scalarField &fld, const scalar minLevel,
1300  const scalar maxLevel) const {
1301  scalarField c(fld.size(), scalar(-1));
1302  forAll (fld, i) {
1303  scalar err = min(fld[i] - minLevel, maxLevel - fld[i]);
1304 
1305  if (err >= 0) { c[i] = err; }
1306  }
1307  return c;
1308 }
1309 
1310 void AMRFoam::selectRefineCandidates(const scalar lowerRefineLevel,
1311  const scalar upperRefineLevel,
1312  const scalarField &vFld,
1313  bitSet &candidateCell) const {
1314  // Get error per cell. Is -1 (not to be refined) to >0 (to be refined,
1315  // higher more desirable to be refined).
1316  scalarField cellError(maxPointField(
1317  error(cellToPoint(vFld), lowerRefineLevel, upperRefineLevel)));
1318 
1319  // Mark cells that are candidates for refinement.
1320  forAll (cellError, celli) {
1321  if (cellError[celli] > 0) { candidateCell.set(celli); }
1322  }
1323 }
1324 
1325 labelList AMRFoam::selectRefineCells(const label maxCells,
1326  const label maxRefinement,
1327  const bitSet &candidateCell) const {
1328  // Every refined cell causes 7 extra cells
1329  label nTotToRefine = (maxCells - globalData().nTotalCells()) / 7;
1330 
1331  const labelList &cellLevel = meshCutter_.cellLevel();
1332 
1333  // Mark cells that cannot be refined since they would trigger refinement
1334  // of protected cells (since 2:1 cascade)
1335  bitSet unrefineableCell;
1336  calculateProtectedCells(unrefineableCell);
1337 
1338  // Count current selection
1339  label nLocalCandidates = candidateCell.count();
1340  label nCandidates = returnReduce(nLocalCandidates, sumOp<label>());
1341 
1342  // Collect all cells
1343  DynamicList<label> candidates(nLocalCandidates);
1344 
1345  if (nCandidates < nTotToRefine) {
1346  for (const label celli : candidateCell) {
1347  if ((!unrefineableCell.test(celli)) && cellLevel[celli] < maxRefinement) {
1348  candidates.append(celli);
1349  }
1350  }
1351  } else {
1352  // Sort by error? For now just truncate.
1353  for (label level = 0; level < maxRefinement; ++level) {
1354  for (const label celli : candidateCell) {
1355  if ((!unrefineableCell.test(celli)) && cellLevel[celli] == level) {
1356  candidates.append(celli);
1357  }
1358  }
1359 
1360  if (returnReduce(candidates.size(), sumOp<label>()) > nTotToRefine) {
1361  break;
1362  }
1363  }
1364  }
1365 
1366  // Guarantee 2:1 refinement after refinement
1367  labelList consistentSet(meshCutter_.consistentRefinement(
1368  candidates.shrink(),
1369  true // Add to set to guarantee 2:1
1370  ));
1371 
1372  Info << "Selected " << returnReduce(consistentSet.size(), sumOp<label>())
1373  << " cells for refinement out of " << globalData().nTotalCells() << "."
1374  << endl;
1375 
1376  return consistentSet;
1377 }
1378 
1379 void AMRFoam::extendMarkedCells(bitSet &markedCell) const {
1380  // Mark faces using any marked cell
1381  bitSet markedFace(nFaces());
1382 
1383  for (const label celli : markedCell) {
1384  markedFace.set(cells()[celli]); // set multiple faces
1385  }
1386 
1387  syncTools::syncFaceList(*this, markedFace, orEqOp<unsigned int>());
1388 
1389  // Update cells using any markedFace
1390  for (label facei = 0; facei < nInternalFaces(); ++facei) {
1391  if (markedFace.test(facei)) {
1392  markedCell.set(faceOwner()[facei]);
1393  markedCell.set(faceNeighbour()[facei]);
1394  }
1395  }
1396  for (label facei = nInternalFaces(); facei < nFaces(); ++facei) {
1397  if (markedFace.test(facei)) { markedCell.set(faceOwner()[facei]); }
1398  }
1399 }
1400 
1402  const labelList &cellLevel = meshCutter_.cellLevel();
1403  const labelList &pointLevel = meshCutter_.pointLevel();
1404 
1405  labelList nAnchorPoints(nCells(), Zero);
1406 
1407  forAll (pointLevel, pointi) {
1408  const labelList &pCells = pointCells(pointi);
1409 
1410  for (const label celli : pCells) {
1411  if (pointLevel[pointi] <= cellLevel[celli]) {
1412  // Check if cell has already 8 anchor points -> protect cell
1413  if (nAnchorPoints[celli] == 8) { protectedCell.set(celli); }
1414 
1415  if (!protectedCell.test(celli)) { ++nAnchorPoints[celli]; }
1416  }
1417  }
1418  }
1419 
1420  forAll (protectedCell, celli) {
1421  if (nAnchorPoints[celli] != 8) { protectedCell.set(celli); }
1422  }
1423 }
1424 
1425 bool AMRFoam::writeObjectAMR(const bool valid) const {
1426  // Force refinement data to go to the current time directory.
1427  const_cast<hexRef8 &>(meshCutter_).setInstance(time().timeName());
1428  auto strmOpt =
1429  IOstreamOption(time().writeFormat(), time().writeCompression());
1430  bool writeOk =
1431  (dynamicFvMesh::writeObject(strmOpt, true) && meshCutter_.write(valid));
1432 
1433  if (dumpLevel_) {
1434  volScalarField scalarCellLevel(
1435  IOobject("cellLevel", time().timeName(), *this, IOobject::NO_READ,
1436  IOobject::AUTO_WRITE, false),
1437  *this, dimensionedScalar("level", dimless, 0));
1438 
1439  const labelList &cellLevel = meshCutter_.cellLevel();
1440 
1441  forAll (cellLevel, celli) { scalarCellLevel[celli] = cellLevel[celli]; }
1442 
1443  writeOk = writeOk && scalarCellLevel.write();
1444  }
1445 
1446  return writeOk;
1447 }
1448 
1450  movePoints(motionPtr_->newPoints());
1451 
1452  if (foundObject<volVectorField>("U")) {
1453  lookupObjectRef<volVectorField>("U").correctBoundaryConditions();
1454  }
1455 }
1456 
1457 template <class T>
1459  const labelList &faceMap,
1460  GeometricField<T, fvsPatchField, surfaceMesh> &sFld) {
1461  using GeoField = GeometricField<T, fvsPatchField, surfaceMesh>;
1462 
1463  //- Make flat field for ease of looping
1464  Field<T> tsFld(this->nFaces(), Zero);
1465  SubField<T>(tsFld, this->nInternalFaces()) = sFld.internalField();
1466 
1467  const typename GeoField::Boundary &bFld = sFld.boundaryField();
1468  forAll (bFld, patchi) {
1469  label facei = this->boundaryMesh()[patchi].start();
1470  for (const T &val : bFld[patchi]) { tsFld[facei++] = val; }
1471  }
1472 
1473  const labelUList &owner = this->faceOwner();
1474  const labelUList &neighbour = this->faceNeighbour();
1475  const cellList &cells = this->cells();
1476 
1477  for (label facei = 0; facei < nInternalFaces(); facei++) {
1478  label oldFacei = faceMap[facei];
1479 
1480  // Map surface field on newly generated faces by obtaining the
1481  // hull of the outside faces
1482  if (oldFacei == -1) {
1483  // Loop over all owner/neighbour cell faces
1484  // and find already mapped ones (master-faces):
1485  T tmpValue(pTraits<T>::zero);
1486  label counter = 0;
1487 
1488  const cell &ownFaces = cells[owner[facei]];
1489  for (auto ownFacei : ownFaces) {
1490  if (faceMap[ownFacei] != -1) {
1491  tmpValue += tsFld[ownFacei];
1492  counter++;
1493  }
1494  }
1495 
1496  const cell &neiFaces = cells[neighbour[facei]];
1497  for (auto neiFacei : neiFaces) {
1498  if (faceMap[neiFacei] != -1) {
1499  tmpValue += tsFld[neiFacei];
1500  counter++;
1501  }
1502  }
1503 
1504  if (counter > 0) { sFld[facei] = tmpValue / counter; }
1505  }
1506  }
1507 }
1508 
1509 template <class T>
1510 void AMRFoam::mapNewInternalFaces(const labelList &faceMap) {
1511  using GeoField = GeometricField<T, fvsPatchField, surfaceMesh>;
1512  HashTable<GeoField *> sFlds(this->objectRegistry::lookupClass<GeoField>());
1513 
1514  forAllIters(sFlds, iter) {
1515  // if (mapSurfaceFields_.found(iter.key()))
1516  {
1517  DebugInfo << "dynamicRefineMotionFvMesh::mapNewInternalFaces():"
1518  << " Mapping new internal faces by interpolation on "
1519  << iter.key() << endl;
1520 
1521  GeoField &sFld = *iter();
1522 
1523  if (sFld.oriented()()) {
1524  WarningInFunction << "Ignoring mapping oriented field " << sFld.name()
1525  << " since of type " << sFld.type() << endl;
1526  } else {
1527  mapNewInternalFaces(faceMap, sFld);
1528  }
1529  }
1530  }
1531 }
1532 
1533 template <class T>
1534 void AMRFoam::mapNewInternalFaces(const surfaceVectorField &Sf,
1535  const surfaceScalarField &magSf,
1536  const labelList &faceMap) {
1537  using GeoField = GeometricField<T, fvsPatchField, surfaceMesh>;
1538  HashTable<GeoField *> sFlds(this->objectRegistry::lookupClass<GeoField>());
1539 
1540  forAllIters(sFlds, iter) {
1541  // if (mapSurfaceFields_.found(iter.key()))
1542  {
1543  DebugInfo << "dynamicRefineMotionFvMesh::mapNewInternalFaces():"
1544  << " Mapping new internal faces by interpolation on "
1545  << iter.key() << endl;
1546 
1547  GeoField &sFld = *iter();
1548 
1549  if (sFld.oriented()()) {
1550  DebugInfo << "dynamicRefineMotionFvMesh::mapNewInternalFaces(): "
1551  << "Converting oriented field " << iter.key()
1552  << " to intensive field and mapping" << endl;
1553 
1554  // Assume any oriented field is face area weighted (i.e. a flux)
1555  // Convert to intensive (& oriented) before mapping. Untested.
1556 
1557  using NormalGeoField =
1558  GeometricField<typename outerProduct<vector, T>::type,
1559  fvsPatchField, surfaceMesh>;
1560 
1561  // Convert to intensive and non oriented
1562  NormalGeoField fFld(sFld * Sf / Foam::sqr(magSf));
1563 
1564  // Interpolate
1565  mapNewInternalFaces(faceMap, fFld);
1566 
1567  // Convert back to extensive and oriented
1568  sFld = (fFld & Sf);
1569  } else {
1570  mapNewInternalFaces(faceMap, sFld);
1571  }
1572  }
1573  }
1574 }
1575 
1576 } // namespace Foam
1577 
1578 // ************************************************************************* //
virtual Foam::autoPtr< Foam::mapPolyMesh > unrefine(const labelList &)
Refines selected cells in a mesh.
Definition: AMRFoam.C:1138
pointScalarField initPntSField(const std::string &fldName)
Creates an empty pointScalarField based on current mesh.
Definition: AMRFoam.C:814
std::vector< double > incomingField
Vector to store user defined vector.
Definition: AMRFoam.H:249
scalarField error(const scalarField &fld, const scalar minLevel, const scalar maxLevel) const
Definition: AMRFoam.C:1299
bitSet protectedCell_
Definition: AMRFoam.H:265
void enableMeshWriting()
Access to mesh data.
Definition: AMRFoam.C:832
virtual labelList selectUnrefinePoints(const scalar unrefineAbove, const scalar unrefineBelow, const bitSet &markedCell, const scalarField &pFld) const
Marked the cells to unrefine based on user defined levels.
Definition: AMRFoam.C:616
void enableUpdatedField()
Access to updated field at every time step.
Definition: AMRFoam.C:830
bool enableMotion
Boolean to check if mesh motion is needed.
Definition: AMRFoam.H:231
bool writeObjectAMR(const bool valid) const
Definition: AMRFoam.C:1425
void calculateProtectedCells(bitSet &unrefineableCell) const
Definition: AMRFoam.C:847
geoMeshBase * New(MeshType meshType)
Create a new mesh object.
virtual bool update()
Definition: AMRFoam.C:233
void checkForMotion()
Checks for availability of mesh motion keywords in dictionary.
Definition: AMRFoam.C:72
void readDict()
Definition: AMRFoam.C:920
Foam::scalarField cell2Pt(const scalarField &vFld)
Converts volume field to point field.
Definition: AMRFoam.C:680
void solveMotion()
A mesh motion solver.
Definition: AMRFoam.C:1449
volScalarField getGradient(const volScalarField &fldGrd)
Returns a gradient of scalar field over mesh param fldGrd Scalar Field for Gradient Calculation...
Definition: AMRFoam.C:834
std::shared_ptr< char > strToChar(const std::string &strng)
void mapNewInternalFaces(const labelList &faceMap, GeometricField< T, fvsPatchField, surfaceMesh > &)
Definition: AMRFoam.C:1458
label nRefinementIterations_
Definition: AMRFoam.H:262
volScalarField assignToVolScalarField(const std::vector< int > &vec)
Converts vector to volScalarField for refinement/unrefinement.
Definition: AMRFoam.C:750
void calcProtectedCells()
Calculates and stores non-hex cells in protected cells catagory to avoid their refinement.
Definition: AMRFoam.C:86
const hexRef8 & meshCutter() const
Definition: AMRFoam.H:370
virtual void mapFields(const mapPolyMesh &mpm)
Definition: AMRFoam.C:937
void writeHistory()
Writes refinement history in current time directory.
Definition: AMRFoam.C:696
AMRFoam(const Foam::IOobject &iomesh)
Standard constructor.
Definition: AMRFoam.C:62
HashTable< word > correctFluxes_
Definition: AMRFoam.H:259
bool updateAMR(const int &refineInterval, const int &maxRefinement, volScalarField &vFld, const double &lowerRefineLevel, const double &upperRefineLevel, const double &unrefineAbove, const double &unrefineBelow, const int &nBufferLayers, const int &maxCells)
updateAMR is a master method for this process.
Definition: AMRFoam.C:386
bool writeRefHistory
Boolean to enable writing of refinement history.
Definition: AMRFoam.H:241
hexRef8 meshCutter_
Definition: AMRFoam.H:253
volScalarField initScalarField(const std::string &fldName)
Creates an empty volScalarField based on current mesh.
Definition: AMRFoam.C:798
volScalarField initGradientField(const std::string &fldName)
Creates an empty volScalarField for gradient magnitude.
Definition: AMRFoam.C:806
const polyMesh & getMesh()
Access to current mesh at every time step.
Definition: AMRFoam.C:702
void enableRefHistoryData()
Access to refinement history data.
Definition: AMRFoam.C:828
scalarField maxCellField(const volScalarField &) const
Definition: AMRFoam.C:1273
const bitSet & protectedCell() const
Definition: AMRFoam.H:373
bool updateAMRML(const int &refineInterval, const int &maxRefinement, const int &nBufferLayers, const int &maxCells, volScalarField &vFld)
New method for machine learning ML.
Definition: AMRFoam.C:510
void transformField(volScalarField &inField)
Transforms incoming scalar field into refinement levels understood by OpenFOAM environement.
Definition: AMRFoam.C:708
virtual void selectRefineCandidates(const scalar lowerRefineLevel, const scalar upperRefineLevel, const scalarField &vFld, bitSet &candidateCell) const
Definition: AMRFoam.C:1310
pointScalarField readIncomingPtField(const std::string &inName, const std::string &outName)
Converts incoming size-field/solution-field into pointScalarField defined over all points of mesh...
Definition: AMRFoam.C:756
void checkEightAnchorPoints(bitSet &protectedCell) const
Definition: AMRFoam.C:1401
void disableMotion()
Ability to explicitely disable motion.
Definition: AMRFoam.C:84
void extendMarkedCells(bitSet &markedCell) const
Definition: AMRFoam.C:1379
void writeMesh()
Ability to write current mesh into current time directory.
Definition: AMRFoam.C:704
scalarField maxPointField(const scalarField &) const
Definition: AMRFoam.C:1260
volScalarField readIncomingCellField(const std::string &inName, const std::string &outName)
reads incoming field and converts to volScalarField
Definition: AMRFoam.C:723
autoPtr< motionSolver > motionPtr_
Definition: AMRFoam.H:365
bool dumpLevel_
Definition: AMRFoam.H:256
scalarField cellToPoint(const scalarField &vFld) const
Definition: AMRFoam.C:1286
volScalarField pF2vF(const pointScalarField &pntF, const std::string &outName)
Converts pointScalarField to volScalarField.
Definition: AMRFoam.C:792
volScalarField readInitialField(const std::string &fldName)
Reads the refinement field and registers it.
Definition: AMRFoam.C:784
bool writeMeshData
Boolean to enable writing of mesh.
Definition: AMRFoam.H:245
bool writeField
Boolean to enable writing of updated solution field every timestep.
Definition: AMRFoam.H:237
virtual Foam::autoPtr< Foam::mapPolyMesh > refine(const labelList &)
Unrefines selected cells in a mesh.
Definition: AMRFoam.C:1082
std::vector< bool > cellsToRefine(const std::vector< T > &values, T tol)
virtual labelList selectRefineCells(const label maxCells, const label maxRefinement, const bitSet &candidateCell) const
Definition: AMRFoam.C:1325
Definition: AMRFoam.C:60