reconstructParMesh.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration |
5  \\ / A nd | www.openfoam.com
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8  Copyright (C) 2011-2016 OpenFOAM Foundation
9  Copyright (C) 2016-2022 OpenCFD Ltd.
10 -------------------------------------------------------------------------------
11 License
12  This file is part of OpenFOAM.
13 
14  OpenFOAM is free software: you can redistribute it and/or modify it
15  under the terms of the GNU General Public License as published by
16  the Free Software Foundation, either version 3 of the License, or
17  (at your option) any later version.
18 
19  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
20  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
21  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
22  for more details.
23 
24  You should have received a copy of the GNU General Public License
25  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
26 
27 Application
28  reconstructParMesh
29 
30 Group
31  grpParallelUtilities
32 
33 Description
34  Reconstructs a mesh using geometric information only.
35 
36  Writes point/face/cell procAddressing so afterwards reconstructPar can be
37  used to reconstruct fields.
38 
39 Usage
40  \b reconstructParMesh [OPTION]
41 
42  Options:
43  - \par -fullMatch
44  Does geometric matching on all boundary faces. Assumes no point
45  ordering
46 
47  - \par -procMatch
48  Assumes processor patches already in face order but not point order.
49  This is the pre v2106 default behaviour but might be removed if the new
50  topological method works well
51 
52  - \par -mergeTol <tol>
53  Specifies non-default merge tolerance (fraction of mesh bounding box)
54  for above options
55 
56  The default is to assume all processor boundaries are correctly ordered
57  (both faces and points) in which case no merge tolerance is needed.
58 
59 \*---------------------------------------------------------------------------*/
60 
61 #include "argList.H"
62 #include "timeSelector.H"
63 
64 #include "IOobjectList.H"
65 #include "labelIOList.H"
66 #include "processorPolyPatch.H"
67 #include "mapAddedPolyMesh.H"
68 #include "polyMeshAdder.H"
69 #include "faceCoupleInfo.H"
70 #include "fvMeshAdder.H"
71 #include "polyTopoChange.H"
73 #include "topoSet.H"
74 #include "regionProperties.H"
75 #include "fvMeshTools.H"
76 
77 using namespace Foam;
78 
79 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
80 
81 // Tolerance (as fraction of the bounding box). Needs to be fairly lax since
82 // usually meshes get written with limited precision (6 digits)
83 static const scalar defaultMergeTol = 1e-7;
84 
85 
86 // Determine which faces are coupled. Uses geometric merge distance.
87 // Looks either at all boundaryFaces (fullMatch) or only at the
88 // procBoundaries for proci. Assumes that masterMesh contains already merged
89 // all the processors < proci.
90 autoPtr<faceCoupleInfo> determineCoupledFaces
91 (
92  const bool fullMatch,
93  const label masterMeshProcStart,
94  const label masterMeshProcEnd,
95  const polyMesh& masterMesh,
96  const label meshToAddProcStart,
97  const label meshToAddProcEnd,
98  const polyMesh& meshToAdd,
99  const scalar mergeDist
100 )
101 {
102  if (fullMatch || masterMesh.nCells() == 0)
103  {
105  (
106  masterMesh,
107  meshToAdd,
108  mergeDist, // Absolute merging distance
109  true // Matching faces identical
110  );
111  }
112  else
113  {
114  // Pick up all patches on masterMesh ending in "toDDD" where DDD is
115  // the processor number proci.
116 
117  const polyBoundaryMesh& masterPatches = masterMesh.boundaryMesh();
118 
119 
120  DynamicList<label> masterFaces
121  (
122  masterMesh.nFaces()
123  - masterMesh.nInternalFaces()
124  );
125 
126 
127  forAll(masterPatches, patchi)
128  {
129  const polyPatch& pp = masterPatches[patchi];
130 
131  if (isA<processorPolyPatch>(pp))
132  {
133  for
134  (
135  label proci=meshToAddProcStart;
136  proci<meshToAddProcEnd;
137  proci++
138  )
139  {
140  const string toProcString("to" + name(proci));
141  if (
142  pp.name().rfind(toProcString)
143  == (pp.name().size()-toProcString.size())
144  )
145  {
146  label meshFacei = pp.start();
147  forAll(pp, i)
148  {
149  masterFaces.append(meshFacei++);
150  }
151  break;
152  }
153  }
154 
155  }
156  }
157  masterFaces.shrink();
158 
159 
160  // Pick up all patches on meshToAdd ending in "procBoundaryDDDtoYYY"
161  // where DDD is the processor number proci and YYY is < proci.
162 
163  const polyBoundaryMesh& addPatches = meshToAdd.boundaryMesh();
164 
165  DynamicList<label> addFaces
166  (
167  meshToAdd.nFaces()
168  - meshToAdd.nInternalFaces()
169  );
170 
171  forAll(addPatches, patchi)
172  {
173  const polyPatch& pp = addPatches[patchi];
174 
175  if (isA<processorPolyPatch>(pp))
176  {
177  bool isConnected = false;
178 
179  for
180  (
181  label mergedProci=masterMeshProcStart;
182  !isConnected && (mergedProci < masterMeshProcEnd);
183  mergedProci++
184  )
185  {
186  for
187  (
188  label proci = meshToAddProcStart;
189  proci < meshToAddProcEnd;
190  proci++
191  )
192  {
193  const word fromProcString
194  (
195  processorPolyPatch::newName(proci, mergedProci)
196  );
197 
198  if (pp.name() == fromProcString)
199  {
200  isConnected = true;
201  break;
202  }
203  }
204  }
205 
206  if (isConnected)
207  {
208  label meshFacei = pp.start();
209  forAll(pp, i)
210  {
211  addFaces.append(meshFacei++);
212  }
213  }
214  }
215  }
216  addFaces.shrink();
217 
219  (
220  masterMesh,
221  masterFaces,
222  meshToAdd,
223  addFaces,
224  mergeDist, // Absolute merging distance
225  true, // Matching faces identical?
226  false, // If perfect match are faces already ordered
227  // (e.g. processor patches)
228  false // are faces each on separate patch?
229  );
230  }
231 }
232 
233 
234 autoPtr<mapPolyMesh> mergeSharedPoints
235 (
236  const scalar mergeDist,
237  polyMesh& mesh,
238  labelListList& pointProcAddressing
239 )
240 {
241  // Find out which sets of points get merged and create a map from
242  // mesh point to unique point.
243  Map<label> pointToMaster
244  (
246  (
247  mesh,
248  mergeDist
249  )
250  );
251 
252  Info<< "mergeSharedPoints : detected " << pointToMaster.size()
253  << " points that are to be merged." << endl;
254 
255  if (returnReduceAnd(pointToMaster.empty()))
256  {
257  return nullptr;
258  }
259 
260  polyTopoChange meshMod(mesh);
261 
262  fvMeshAdder::mergePoints(mesh, pointToMaster, meshMod);
263 
264  // Change the mesh (no inflation). Note: parallel comms allowed.
265  autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false, true);
266 
267  // Update fields. No inflation, parallel sync.
268  mesh.updateMesh(map());
269 
270  // pointProcAddressing give indices into the master mesh so adapt them
271  // for changed point numbering.
272 
273  // Adapt constructMaps for merged points.
274  forAll(pointProcAddressing, proci)
275  {
276  labelList& constructMap = pointProcAddressing[proci];
277 
278  forAll(constructMap, i)
279  {
280  label oldPointi = constructMap[i];
281 
282  // New label of point after changeMesh.
283  label newPointi = map().reversePointMap()[oldPointi];
284 
285  if (newPointi < -1)
286  {
287  constructMap[i] = -newPointi-2;
288  }
289  else if (newPointi >= 0)
290  {
291  constructMap[i] = newPointi;
292  }
293  else
294  {
296  << "Problem. oldPointi:" << oldPointi
297  << " newPointi:" << newPointi << abort(FatalError);
298  }
299  }
300  }
301 
302  return map;
303 }
304 
305 
306 boundBox procBounds
307 (
308  const PtrList<Time>& databases,
309  const word& regionDir
310 )
311 {
312  boundBox bb;
313 
314  for (const Time& procDb : databases)
315  {
316  fileName pointsInstance
317  (
318  procDb.findInstance
319  (
321  "points"
322  )
323  );
324 
325  if (pointsInstance != procDb.timeName())
326  {
328  << "Your time was specified as " << procDb.timeName()
329  << " but there is no polyMesh/points in that time." << nl
330  << "(points file at " << pointsInstance << ')' << nl
331  << "Please rerun with the correct time specified"
332  << " (through the -constant, -time or -latestTime "
333  << "(at your option)."
334  << endl << exit(FatalError);
335  }
336 
337  Info<< "Reading points from "
338  << procDb.caseName()
339  << " for time = " << procDb.timeName()
340  << nl << endl;
341 
343  (
344  IOobject
345  (
346  "points",
347  procDb.findInstance
348  (
350  "points"
351  ),
353  procDb,
356  false
357  )
358  );
359 
360  bb.add(points);
361  }
362 
363  return bb;
364 }
365 
366 
367 void writeDistribution
368 (
369  Time& runTime,
370  const fvMesh& masterMesh,
371  const labelListList& cellProcAddressing
372 )
373 {
374  // Write the decomposition as labelList for use with 'manual'
375  // decomposition method.
376  labelIOList cellDecomposition
377  (
378  IOobject
379  (
380  "cellDecomposition",
381  masterMesh.facesInstance(),
382  masterMesh,
385  false
386  ),
387  masterMesh.nCells()
388  );
389 
390  forAll(cellProcAddressing, proci)
391  {
392  const labelList& pCells = cellProcAddressing[proci];
393  labelUIndList(cellDecomposition, pCells) = proci;
394  }
395 
396  cellDecomposition.write();
397 
398  Info<< nl << "Wrote decomposition to "
399  << cellDecomposition.objectRelPath()
400  << " for use in manual decomposition." << endl;
401 
402  // Write as volScalarField for postprocessing.
403  // Change time to 0 if was 'constant'
404  {
405  const scalar oldTime = runTime.value();
406  const label oldIndex = runTime.timeIndex();
407  if (runTime.timeName() == runTime.constant() && oldIndex == 0)
408  {
409  runTime.setTime(0, oldIndex+1);
410  }
411 
412  volScalarField cellDist
413  (
414  IOobject
415  (
416  "cellDist",
417  runTime.timeName(),
418  masterMesh,
421  false
422  ),
423  masterMesh,
424  dimensionedScalar("cellDist", dimless, -1),
425  zeroGradientFvPatchScalarField::typeName
426  );
427 
428  forAll(cellDecomposition, celli)
429  {
430  cellDist[celli] = cellDecomposition[celli];
431  }
432 
433  cellDist.correctBoundaryConditions();
434  cellDist.write();
435 
436  Info<< nl << "Wrote decomposition to "
437  << cellDist.objectRelPath()
438  << " (volScalarField) for visualization."
439  << endl;
440 
441  // Restore time
442  runTime.setTime(oldTime, oldIndex);
443  }
444 }
445 
446 
447 void writeMesh
448 (
449  const fvMesh& mesh,
450  const labelListList& cellProcAddressing
451 )
452 {
453  const fileName outputDir
454  (
455  mesh.time().path()
456  / mesh.time().timeName()
457  / mesh.regionName()
459  );
460 
461  Info<< nl << "Writing merged mesh to "
462  << mesh.time().relativePath(outputDir) << nl << endl;
463 
464  if (!mesh.write())
465  {
467  << "Failed writing polyMesh."
468  << exit(FatalError);
469  }
471 }
472 
473 
474 void writeMaps
475 (
476  const label masterInternalFaces,
477  const labelUList& masterOwner,
478  const polyMesh& procMesh,
479  const labelUList& cellProcAddressing,
480  const labelUList& faceProcAddressing,
481  const labelUList& pointProcAddressing,
482  const labelUList& boundProcAddressing
483 )
484 {
485  const fileName outputDir
486  (
487  procMesh.time().caseName()
488  / procMesh.facesInstance()
489  / procMesh.regionName()
491  );
492 
493  IOobject ioAddr
494  (
495  "procAddressing",
496  procMesh.facesInstance(),
498  procMesh,
501  false // Do not register
502  );
503 
504 
505  Info<< "Writing addressing : " << outputDir << nl;
506 
507  // From processor point to reconstructed mesh point
508 
509  Info<< " pointProcAddressing" << endl;
510  ioAddr.rename("pointProcAddressing");
511  IOListRef<label>(ioAddr, pointProcAddressing).write();
512 
513  // From processor face to reconstructed mesh face
514  Info<< " faceProcAddressing" << endl;
515  ioAddr.rename("faceProcAddressing");
516  labelIOList faceProcAddr(ioAddr, faceProcAddressing);
517 
518  // Now add turning index to faceProcAddressing.
519  // See reconstructPar for meaning of turning index.
520  forAll(faceProcAddr, procFacei)
521  {
522  const label masterFacei = faceProcAddr[procFacei];
523 
524  if
525  (
526  !procMesh.isInternalFace(procFacei)
527  && masterFacei < masterInternalFaces
528  )
529  {
530  // proc face is now external but used to be internal face.
531  // Check if we have owner or neighbour.
532 
533  label procOwn = procMesh.faceOwner()[procFacei];
534  label masterOwn = masterOwner[masterFacei];
535 
536  if (cellProcAddressing[procOwn] == masterOwn)
537  {
538  // No turning. Offset by 1.
539  faceProcAddr[procFacei]++;
540  }
541  else
542  {
543  // Turned face.
544  faceProcAddr[procFacei] = -1 - faceProcAddr[procFacei];
545  }
546  }
547  else
548  {
549  // No turning. Offset by 1.
550  faceProcAddr[procFacei]++;
551  }
552  }
553 
554  faceProcAddr.write();
555 
556 
557  // From processor cell to reconstructed mesh cell
558  Info<< " cellProcAddressing" << endl;
559  ioAddr.rename("cellProcAddressing");
560  IOListRef<label>(ioAddr, cellProcAddressing).write();
561 
562 
563  // From processor patch to reconstructed mesh patch
564  Info<< " boundaryProcAddressing" << endl;
565  ioAddr.rename("boundaryProcAddressing");
566  IOListRef<label>(ioAddr, boundProcAddressing).write();
567 
568  Info<< endl;
569 }
570 
571 
572 void printWarning()
573 {
574  Info<<
575 "Merge individual processor meshes back into one master mesh.\n"
576 "Use if the original master mesh has been deleted or the processor meshes\n"
577 "have been modified (topology change).\n"
578 "This tool will write the resulting mesh to a new time step and construct\n"
579 "xxxxProcAddressing files in the processor meshes so reconstructPar can be\n"
580 "used to regenerate the fields on the master mesh.\n\n"
581 "Not well tested & use at your own risk!\n\n";
582 }
583 
584 
585 int main(int argc, char *argv[])
586 {
588  (
589  "Reconstruct a mesh using geometric/topological information only"
590  );
591 
592  // Enable -constant ... if someone really wants it
593  // Enable -withZero to prevent accidentally trashing the initial fields
594  timeSelector::addOptions(true, true); // constant(true), zero(true)
595 
597 
598  argList::addVerboseOption("Additional verbosity");
600  (
601  "addressing-only",
602  "Create procAddressing only without overwriting the mesh"
603  );
605  (
606  "mergeTol",
607  "scalar",
608  "The merge distance relative to the bounding box size (default 1e-7)"
609  );
611  (
612  "fullMatch",
613  "Do (slower) geometric matching on all boundary faces"
614  );
616  (
617  "procMatch",
618  "Do matching on processor faces only"
619  );
621  (
622  "cellDist",
623  "Write cell distribution as a labelList - for use with 'manual' "
624  "decomposition method or as a volScalarField for post-processing."
625  );
626 
627  #include "addAllRegionOptions.H"
628 
629  #include "setRootCase.H"
630  #include "createTime.H"
631 
632  printWarning();
633 
634  const bool fullMatch = args.found("fullMatch");
635  const bool procMatch = args.found("procMatch");
636  const bool writeCellDist = args.found("cellDist");
637 
638  const bool writeAddrOnly = args.found("addressing-only");
639 
640  const scalar mergeTol =
641  args.getOrDefault<scalar>("mergeTol", defaultMergeTol);
642 
643  if (fullMatch)
644  {
645  Info<< "Use geometric matching on all boundary faces." << nl << endl;
646  }
647  else if (procMatch)
648  {
649  Info<< "Use geometric matching on correct procBoundaries only." << nl
650  << "This assumes a correct decomposition." << endl;
651  }
652  else
653  {
654  Info<< "Merge assuming correct, fully matched procBoundaries." << nl
655  << endl;
656  }
657 
658  if (fullMatch || procMatch)
659  {
660  const scalar writeTol =
661  Foam::pow(10.0, -scalar(IOstream::defaultPrecision()));
662 
663  Info<< "Merge tolerance : " << mergeTol << nl
664  << "Write tolerance : " << writeTol << endl;
665 
666  if
667  (
669  && mergeTol < writeTol
670  )
671  {
673  << "Your current settings specify ASCII writing with "
674  << IOstream::defaultPrecision() << " digits precision." << endl
675  << "Your merging tolerance (" << mergeTol << ")"
676  << " is finer than this." << endl
677  << "Please change your writeFormat to binary"
678  << " or increase the writePrecision" << endl
679  << "or adjust the merge tolerance (-mergeTol)."
680  << exit(FatalError);
681  }
682  }
683 
684  // Get region names
685  #include "getAllRegionOptions.H"
686 
687  // Determine the processor count
688  label nProcs{0};
689 
690  if (regionNames.empty())
691  {
693  << "No regions specified or detected."
694  << exit(FatalError);
695  }
696  else if (regionNames[0] == polyMesh::defaultRegion)
697  {
698  nProcs = fileHandler().nProcs(args.path());
699  }
700  else
701  {
702  nProcs = fileHandler().nProcs(args.path(), regionNames[0]);
703 
704  if (regionNames.size() == 1)
705  {
706  Info<< "Using region: " << regionNames[0] << nl << endl;
707  }
708  }
709 
710  if (!nProcs)
711  {
713  << "No processor* directories found"
714  << exit(FatalError);
715  }
716 
717  // Read all time databases
718  PtrList<Time> databases(nProcs);
719 
720  Info<< "Found " << nProcs << " processor directories" << endl;
721  forAll(databases, proci)
722  {
723  Info<< " Reading database "
724  << args.caseName()/("processor" + Foam::name(proci))
725  << endl;
726 
727  databases.set
728  (
729  proci,
730  new Time
731  (
733  args.rootPath(),
734  args.caseName()/("processor" + Foam::name(proci)),
736  args.allowLibs()
737  )
738  );
739  }
740  Info<< endl;
741 
742  // Use the times list from the master processor
743  // and select a subset based on the command-line options
745  (
746  databases[0].times(),
747  args
748  );
749 
750  // Loop over all times
751  forAll(timeDirs, timei)
752  {
753  // Set time for global database
754  runTime.setTime(timeDirs[timei], timei);
755 
756  // Set time for all databases
757  forAll(databases, proci)
758  {
759  databases[proci].setTime(timeDirs[timei], timei);
760  }
761 
762  Info<< "Time = " << runTime.timeName() << endl;
763 
764  // Check for any mesh changes
765  label nMeshChanged = 0;
766  boolList hasRegionMesh(regionNames.size(), false);
767  forAll(regionNames, regioni)
768  {
769  const word& regionName = regionNames[regioni];
770 
771  IOobject facesIO
772  (
773  "faces",
774  databases[0].timeName(),
776  databases[0],
779  );
780 
781  // Problem: faceCompactIOList recognises both 'faceList' and
782  // 'faceCompactList' so we should be lenient when doing
783  // typeHeaderOk
784 
785  const bool ok = facesIO.typeHeaderOk<faceCompactIOList>(false);
786 
787  if (ok)
788  {
789  hasRegionMesh[regioni] = true;
790  ++nMeshChanged;
791  }
792  }
793 
794  // Check for any mesh changes
795  if (!nMeshChanged)
796  {
797  Info<< "No meshes" << nl << endl;
798  continue;
799  }
800 
801  Info<< endl;
802 
803  forAll(regionNames, regioni)
804  {
805  const word& regionName = regionNames[regioni];
807 
808  if (!hasRegionMesh[regioni])
809  {
810  Info<< "region=" << regionName << " (no mesh)" << nl << endl;
811  continue;
812  }
813 
814  if (regionNames.size() > 1)
815  {
816  Info<< "region=" << regionName << nl;
817  }
818 
819 
820  // Addressing from processor to reconstructed case
821  labelListList cellProcAddressing(nProcs);
822  labelListList faceProcAddressing(nProcs);
823  labelListList pointProcAddressing(nProcs);
824  labelListList boundProcAddressing(nProcs);
825 
826 
827  // Internal faces on the final reconstructed mesh
828  label masterInternalFaces;
829 
830  // Owner addressing on the final reconstructed mesh
831  labelList masterOwner;
832 
833  if (procMatch)
834  {
835  // Read point on individual processors to determine
836  // merge tolerance
837  // (otherwise single cell domains might give problems)
838 
839  const boundBox bb = procBounds(databases, regionDir);
840  const scalar mergeDist = mergeTol*bb.mag();
841 
842  Info<< "Overall mesh bounding box : " << bb << nl
843  << "Relative tolerance : " << mergeTol << nl
844  << "Absolute matching distance : " << mergeDist << nl
845  << endl;
846 
847 
848  // Construct empty mesh.
849  PtrList<fvMesh> masterMesh(nProcs);
850 
851  for (label proci=0; proci<nProcs; proci++)
852  {
853  masterMesh.set
854  (
855  proci,
856  new fvMesh
857  (
858  IOobject
859  (
860  regionName,
861  runTime.timeName(),
862  runTime,
864  ),
865  Zero
866  )
867  );
868 
869  fvMesh meshToAdd
870  (
871  IOobject
872  (
873  regionName,
874  databases[proci].timeName(),
875  databases[proci]
876  )
877  );
878 
879  // Initialize its addressing
880  cellProcAddressing[proci] = identity(meshToAdd.nCells());
881  faceProcAddressing[proci] = identity(meshToAdd.nFaces());
882  pointProcAddressing[proci] = identity(meshToAdd.nPoints());
883  boundProcAddressing[proci] =
884  identity(meshToAdd.boundaryMesh().size());
885 
886  // Find geometrically shared points/faces.
887  autoPtr<faceCoupleInfo> couples = determineCoupledFaces
888  (
889  fullMatch,
890  proci,
891  proci,
892  masterMesh[proci],
893  proci,
894  proci,
895  meshToAdd,
896  mergeDist
897  );
898 
899  // Add elements to mesh
901  (
902  masterMesh[proci],
903  meshToAdd,
904  couples()
905  );
906 
907  // Added processor
908  renumber(map().addedCellMap(), cellProcAddressing[proci]);
909  renumber(map().addedFaceMap(), faceProcAddressing[proci]);
910  renumber(map().addedPointMap(), pointProcAddressing[proci]);
911  renumber(map().addedPatchMap(), boundProcAddressing[proci]);
912  }
913  for (label step=2; step<nProcs*2; step*=2)
914  {
915  for (label proci=0; proci<nProcs; proci+=step)
916  {
917  label next = proci + step/2;
918  if(next >= nProcs)
919  {
920  continue;
921  }
922 
923  Info<< "Merging mesh " << proci << " with "
924  << next << endl;
925 
926  // Find geometrically shared points/faces.
927  autoPtr<faceCoupleInfo> couples = determineCoupledFaces
928  (
929  fullMatch,
930  proci,
931  next,
932  masterMesh[proci],
933  next,
934  proci+step,
935  masterMesh[next],
936  mergeDist
937  );
938 
939  // Add elements to mesh
941  (
942  masterMesh[proci],
943  masterMesh[next],
944  couples()
945  );
946 
947  // Processors that were already in masterMesh
948  for (label mergedI=proci; mergedI<next; mergedI++)
949  {
950  renumber
951  (
952  map().oldCellMap(),
953  cellProcAddressing[mergedI]
954  );
955 
956  renumber
957  (
958  map().oldFaceMap(),
959  faceProcAddressing[mergedI]
960  );
961 
962  renumber
963  (
964  map().oldPointMap(),
965  pointProcAddressing[mergedI]
966  );
967 
968  // Note: boundary is special since can contain -1.
969  renumber
970  (
971  map().oldPatchMap(),
972  boundProcAddressing[mergedI]
973  );
974  }
975 
976  // Added processor
977  for
978  (
979  label addedI=next;
980  addedI<min(proci+step, nProcs);
981  addedI++
982  )
983  {
984  renumber
985  (
986  map().addedCellMap(),
987  cellProcAddressing[addedI]
988  );
989 
990  renumber
991  (
992  map().addedFaceMap(),
993  faceProcAddressing[addedI]
994  );
995 
996  renumber
997  (
998  map().addedPointMap(),
999  pointProcAddressing[addedI]
1000  );
1001 
1002  renumber
1003  (
1004  map().addedPatchMap(),
1005  boundProcAddressing[addedI]
1006  );
1007  }
1008 
1009  masterMesh.set(next, nullptr);
1010  }
1011  }
1012 
1013  for (label proci=0; proci<nProcs; proci++)
1014  {
1015  Info<< "Reading mesh to add from "
1016  << databases[proci].caseName()
1017  << " for time = " << databases[proci].timeName()
1018  << nl << nl << endl;
1019  }
1020 
1021  // See if any points on the mastermesh have become connected
1022  // because of connections through processor meshes.
1023  mergeSharedPoints(mergeDist,masterMesh[0],pointProcAddressing);
1024 
1025  // Save some properties on the reconstructed mesh
1026  masterInternalFaces = masterMesh[0].nInternalFaces();
1027  masterOwner = masterMesh[0].faceOwner();
1028 
1029 
1030  if (writeAddrOnly)
1031  {
1032  Info<< nl
1033  << "Disabled writing of merged mesh (-addressing-only)"
1034  << nl << nl;
1035  }
1036  else
1037  {
1038  // Write reconstructed mesh
1039  writeMesh(masterMesh[0], cellProcAddressing);
1040  if (writeCellDist)
1041  {
1042  writeDistribution
1043  (
1044  runTime,
1045  masterMesh[0],
1046  cellProcAddressing
1047  );
1048  }
1049  }
1050  }
1051  else
1052  {
1053  // Load all meshes
1054  PtrList<fvMesh> fvMeshes(nProcs);
1055  for (label proci=0; proci<nProcs; proci++)
1056  {
1057  fvMeshes.set
1058  (
1059  proci,
1060  new fvMesh
1061  (
1062  IOobject
1063  (
1064  regionName,
1065  databases[proci].timeName(),
1066  databases[proci]
1067  )
1068  )
1069  );
1070  }
1071 
1072  // Construct pointers to meshes
1073  UPtrList<polyMesh> meshes(fvMeshes.size());
1074  forAll(fvMeshes, proci)
1075  {
1076  meshes.set(proci, &fvMeshes[proci]);
1077  }
1078 
1079  // Get pairs of patches to stitch. These pairs have to
1080  // - have ordered, opposite faces (so one to one correspondence)
1081  List<DynamicList<label>> localPatch;
1082  List<DynamicList<label>> remoteProc;
1083  List<DynamicList<label>> remotePatch;
1084  const label nGlobalPatches = polyMeshAdder::procPatchPairs
1085  (
1086  meshes,
1087  localPatch,
1088  remoteProc,
1089  remotePatch
1090  );
1091 
1092  // Collect matching boundary faces on patches-to-stitch
1093  labelListList localBoundaryFace;
1094  labelListList remoteFaceProc;
1095  labelListList remoteBoundaryFace;
1097  (
1098  meshes,
1099  localPatch,
1100  remoteProc,
1101  remotePatch,
1102  localBoundaryFace,
1103  remoteFaceProc,
1104  remoteBoundaryFace
1105  );
1106 
1107  // All matched faces assumed to have vertex0 matched
1108  labelListList remoteFaceStart(meshes.size());
1109  forAll(meshes, proci)
1110  {
1111  const labelList& procFaces = localBoundaryFace[proci];
1112  remoteFaceStart[proci].setSize(procFaces.size(), 0);
1113  }
1114 
1115 
1116 
1117  labelListList patchMap(meshes.size());
1118  labelListList pointZoneMap(meshes.size());
1119  labelListList faceZoneMap(meshes.size());
1120  labelListList cellZoneMap(meshes.size());
1121  forAll(meshes, proci)
1122  {
1123  const polyMesh& mesh = meshes[proci];
1124  patchMap[proci] = identity(mesh.boundaryMesh().size());
1125 
1126  // Remove excess patches
1127  patchMap[proci].setSize(nGlobalPatches);
1128 
1129  pointZoneMap[proci] = identity(mesh.pointZones().size());
1130  faceZoneMap[proci] = identity(mesh.faceZones().size());
1131  cellZoneMap[proci] = identity(mesh.cellZones().size());
1132  }
1133 
1134 
1135  refPtr<fvMesh> masterMeshPtr;
1136  {
1137  // Do in-place addition on proc0.
1138 
1139  const labelList oldFaceOwner(fvMeshes[0].faceOwner());
1140 
1142  (
1143  0, // index of mesh to modify (== mesh_)
1144  fvMeshes,
1145  oldFaceOwner,
1146 
1147  // Coupling info
1148  localBoundaryFace,
1149  remoteFaceProc,
1150  remoteBoundaryFace,
1151 
1152  boundProcAddressing,
1153  cellProcAddressing,
1154  faceProcAddressing,
1155  pointProcAddressing
1156  );
1157 
1158  // Remove zero-faces processor patches
1159  const polyBoundaryMesh& pbm = fvMeshes[0].boundaryMesh();
1160  labelList oldToNew(pbm.size(), -1);
1161  label newi = 0;
1162  // Non processor patches first
1163  forAll(pbm, patchi)
1164  {
1165  const auto& pp = pbm[patchi];
1166  if (!isA<processorPolyPatch>(pp) || pp.size())
1167  {
1168  oldToNew[patchi] = newi++;
1169  }
1170  }
1171  const label nNonProcPatches = newi;
1172 
1173  // Move all deletable patches to the end
1174  forAll(oldToNew, patchi)
1175  {
1176  if (oldToNew[patchi] == -1)
1177  {
1178  oldToNew[patchi] = newi++;
1179  }
1180  }
1182  (
1183  fvMeshes[0],
1184  oldToNew,
1185  nNonProcPatches,
1186  false
1187  );
1188 
1189  masterMeshPtr.cref(fvMeshes[0]);
1190  }
1191 
1192 
1193  const fvMesh& masterMesh = masterMeshPtr();
1194 
1195  // Number of internal faces on the final reconstructed mesh
1196  masterInternalFaces = masterMesh.nInternalFaces();
1197 
1198  // Owner addressing on the final reconstructed mesh
1199  masterOwner = masterMesh.faceOwner();
1200 
1201 
1202  // Write reconstructed mesh
1203  // Override:
1204  // - caseName
1205  // - processorCase flag
1206  // so the resulting mesh goes to the correct location (even with
1207  // collated). The better way of solving this is to construct
1208  // (zero) mesh on the undecomposed runTime.
1209 
1210  if (writeAddrOnly)
1211  {
1212  Info<< nl
1213  << "Disabled writing of merged mesh (-addressing-only)"
1214  << nl << nl;
1215  }
1216  else
1217  {
1218  Time& masterTime = const_cast<Time&>(masterMesh.time());
1219 
1220  const word oldCaseName = masterTime.caseName();
1221  masterTime.caseName() = runTime.caseName();
1222  const bool oldProcCase(masterTime.processorCase(false));
1223 
1224  // Write reconstructed mesh
1225  writeMesh(masterMesh, cellProcAddressing);
1226  if (writeCellDist)
1227  {
1228  writeDistribution
1229  (
1230  runTime,
1231  masterMesh,
1232  cellProcAddressing
1233  );
1234  }
1235  masterTime.caseName() = oldCaseName;
1236  masterTime.processorCase(oldProcCase);
1237  }
1238  }
1239 
1240 
1241  // Write the addressing
1242 
1243  Info<< "Reconstructing addressing from processor meshes"
1244  << " to the newly reconstructed mesh" << nl << endl;
1245 
1246  forAll(databases, proci)
1247  {
1248  Info<< "Processor " << proci << nl
1249  << "Read processor mesh: "
1250  << (databases[proci].caseName()/regionDir) << endl;
1251 
1252  polyMesh procMesh
1253  (
1254  IOobject
1255  (
1256  regionName,
1257  databases[proci].timeName(),
1258  databases[proci]
1259  )
1260  );
1261 
1262  writeMaps
1263  (
1264  masterInternalFaces,
1265  masterOwner,
1266  procMesh,
1267  cellProcAddressing[proci],
1268  faceProcAddressing[proci],
1269  pointProcAddressing[proci],
1270  boundProcAddressing[proci]
1271  );
1272  }
1273  }
1274  }
1275 
1276  Info<< "End\n" << endl;
1277 
1278  return 0;
1279 }
1280 
1281 
1282 // ************************************************************************* //
const polyBoundaryMesh & boundaryMesh() const
Return boundary mesh.
Definition: polyMesh.H:584
const Type & value() const noexcept
Return const reference to value.
static void reorderPatches(fvMesh &, const labelList &oldToNew, const label nPatches, const bool validBoundary)
Reorder and remove trailing patches.
Definition: fvMeshTools.C:315
static void addNote(const string &note)
Add extra notes for the usage information.
Definition: argList.C:453
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:118
fileName path() const
Return path.
Definition: Time.H:449
Info<< "Creating field kinetic energy K\"<< endl;volScalarField K("K", 0.5 *magSqr(U));if(U.nOldTimes()){ volVectorField *Uold=&U.oldTime();volScalarField *Kold=&K.oldTime();*Kold==0.5 *magSqr(*Uold);while(Uold->nOldTimes()) { Uold=&Uold-> oldTime()
Definition: createK.H:12
A class for handling file names.
Definition: fileName.H:71
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
const fileName & facesInstance() const
Return the current instance directory for faces.
Definition: polyMesh.C:853
label nPoints() const noexcept
Number of mesh points.
error FatalError
Error stream (stdout output on all processes), with additional &#39;FOAM FATAL ERROR&#39; header text and sta...
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:578
const word & regionName() const
The mesh region name or word::null if polyMesh::defaultRegion.
Definition: polyMesh.C:841
virtual bool write(const bool valid=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1072
IntListType renumber(const labelUList &oldToNew, const IntListType &input)
Renumber the values (not the indices) of a list.
const fileName & caseName() const noexcept
Return case name.
Definition: TimePathsI.H:55
constexpr char nl
The newline &#39;\n&#39; character (0x0a)
Definition: Ostream.H:49
"ascii" (normal default)
bool empty() const noexcept
True if the UList is empty (ie, size() is zero)
Definition: UListI.H:420
static word meshSubDir
Return the mesh sub-directory name (usually "polyMesh")
Definition: polyMesh.H:402
engineTime & runTime
static word newName(const label myProcNo, const label neighbProcNo)
Return the name of a processorPolyPatch.
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:487
fileName relativePath(const fileName &input, const bool caseTag=false) const
Return the input relative to the globalPath by stripping off a leading value of the globalPath...
Definition: TimePathsI.H:80
static unsigned int defaultPrecision() noexcept
Return the default precision.
Definition: IOstream.H:416
static void addBoolOption(const word &optName, const string &usage="", bool advanced=false)
Add a bool option to validOptions with usage information.
Definition: argList.C:365
static void noParallel()
Remove the parallel options.
Definition: argList.C:551
autoPtr< fileOperation > fileHandler(std::nullptr_t)
Delete current file handler.
A bounding box defined in terms of min/max extrema points.
Definition: boundBox.H:63
wordList regionNames
Ignore writing from objectRegistry::writeObject()
const dimensionSet dimless
Dimensionless.
T getOrDefault(const word &optName, const T &deflt) const
Get a value from the named option if present, or return default.
Definition: argListI.H:300
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:361
const T & cref() const
Return const reference to the object or to the contents of a (non-null) managed pointer.
Definition: refPtrI.H:194
label nFaces() const noexcept
Number of mesh faces.
Class to control time during OpenFOAM simulations that is also the top-level objectRegistry.
Definition: Time.H:69
A class for managing references or pointers (no reference counting)
Definition: HashPtrTable.H:49
instantList select(const instantList &times) const
Select a list of Time values that are within the ranges.
Definition: timeSelector.C:88
bool processorCase() const noexcept
Return true if this is a processor case.
Definition: TimePathsI.H:29
bool allowFunctionObjects() const
The controlDict &#39;functions&#39; entry is allowed to be used.
Definition: argList.C:1817
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:413
word timeName
Definition: getTimeIndex.H:3
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
Foam::word regionName(Foam::polyMesh::defaultRegion)
bool allowLibs() const
The controlDict &#39;libs&#39; entry is allowed to be used. (eg, has not been disabled by the -no-libs option...
Definition: argList.C:1835
void add(const boundBox &bb)
Extend to include the second box.
Definition: boundBoxI.H:309
static void removeFiles(const polyMesh &)
Helper: remove all sets files from mesh instance.
Definition: topoSet.C:618
bool returnReduceAnd(const bool value, const label comm=UPstream::worldComm)
Perform logical (and) MPI Allreduce on a copy. Uses UPstream::reduceAnd.
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:289
virtual void updateMesh(const mapPolyMesh &mpm)
Update mesh corresponding to the given map.
Definition: fvMesh.C:964
dynamicFvMesh & mesh
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for INVALID.
Definition: exprTraits.C:52
const pointField & points
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i)
Definition: labelList.C:31
static void mergePoints(const polyMesh &, const Map< label > &pointToMaster, polyTopoChange &meshMod)
Helper: Merge points.
A class for handling words, derived from Foam::string.
Definition: word.H:63
const Time & time() const noexcept
Return time registry.
static word defaultRegion
Return the default region name.
Definition: polyMesh.H:397
scalar mag() const
The magnitude/length of the bounding box diagonal.
Definition: boundBoxI.H:191
label size() const noexcept
The number of elements in the list.
Definition: UPtrListI.H:99
Foam::PtrList< Foam::fvMesh > meshes(regionNames.size())
static autoPtr< mapAddedPolyMesh > add(fvMesh &mesh0, const fvMesh &mesh1, const faceCoupleInfo &coupleInfo, const bool validBoundary=true, const bool fullyMapped=false)
Inplace add mesh to fvMesh. Maps all stored fields. Returns map.
Definition: fvMeshAdder.C:67
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1104
static void addVerboseOption(const string &usage, bool advanced=false)
Enable a &#39;verbose&#39; bool option, with usage information.
Definition: argList.C:505
static void addOption(const word &optName, const string &param="", const string &usage="", bool advanced=false)
Add an option to validOptions with usage information.
Definition: argList.C:376
virtual void setTime(const Time &t)
Reset the time and time-index to those of the given time.
Definition: Time.C:977
label nInternalFaces() const noexcept
Number of internal faces.
label timeIndex() const noexcept
Return current time index.
Definition: TimeStateI.H:30
static word controlDictName
The default control dictionary name (normally "controlDict")
Definition: Time.H:275
label min(const labelHashSet &set, label minValue=labelMax)
Find the min value in labelHashSet, optionally limited by second argument.
Definition: hashSets.C:26
A list of pointers to objects of type <T>, without allocation/deallocation management of the pointers...
Definition: HashTable.H:100
errorManip< error > abort(error &err)
Definition: errorManip.H:139
const T * set(const label i) const
Return const pointer to element (can be nullptr), or nullptr for out-of-range access (ie...
Definition: PtrList.H:163
const fileName & caseName() const noexcept
Return case name (parallel run) or global case (serial run)
Definition: argListI.H:62
static label procPatchPairs(const UPtrList< polyMesh > &meshes, List< DynamicList< label >> &localPatch, List< DynamicList< label >> &remoteMesh, List< DynamicList< label >> &remotePatch)
Helper: find pairs of processor patches. Return number of non-processor patches.
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO...
const word & name() const noexcept
The patch name.
static word timeName(const scalar t, const int precision=precision_)
Return time name of given scalar time formatted with the given precision.
Definition: Time.C:760
const word & constant() const noexcept
Return constant name.
Definition: TimePathsI.H:89
const fileName & rootPath() const noexcept
Return root path.
Definition: argListI.H:56
const faceZoneMesh & faceZones() const noexcept
Return face zone mesh.
Definition: polyMesh.H:646
virtual void rename(const word &newName)
Rename.
Definition: regIOobject.C:413
IOstreamOption::streamFormat writeFormat() const noexcept
The write stream format.
Definition: Time.H:486
fileName path() const
Return the full path to the (processor local) case.
Definition: argListI.H:74
dimensionedScalar pow(const dimensionedScalar &ds, const dimensionedScalar &expt)
const pointZoneMesh & pointZones() const noexcept
Return point zone mesh.
Definition: polyMesh.H:638
UIndirectList< label > labelUIndList
UIndirectList of labels.
Definition: IndirectList.H:65
static void patchFacePairs(const UPtrList< polyMesh > &meshes, const List< DynamicList< label >> &localPatch, const List< DynamicList< label >> &remoteMesh, const List< DynamicList< label >> &remotePatch, labelListList &localBoundaryFace, labelListList &remoteFaceMesh, labelListList &remoteBoundaryFace)
Helper: expand list of coupled patches into pairs of coupled faces.
label newPointi
Definition: readKivaGrid.H:496
dimensioned< scalar > dimensionedScalar
Dimensioned scalar obtained from generic dimensioned type.
label nCells() const noexcept
Number of mesh cells.
const word & regionDir
A list of pointers to objects of type <T>, with allocation/deallocation management of the pointers...
Definition: List.H:55
Mesh data needed to do the Finite Volume discretisation.
Definition: fvMesh.H:79
Direct mesh changes based on v1.3 polyTopoChange syntax.
label start() const
Return start label of this patch in the polyMesh face list.
Definition: polyPatch.H:441
Nothing to be read.
const cellZoneMesh & cellZones() const noexcept
Return cell zone mesh.
Definition: polyMesh.H:654
messageStream Info
Information stream (stdout output on master, null elsewhere)
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:73
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition: autoPtr.H:178
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
Foam::argList args(argc, argv)
A IOList wrapper for writing external data.
Definition: IOList.H:144
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:166
A primitive field of type <T> with automated input and output.
static void writeMaps(Ostream &os, const word &key, const labelListList &maps)
static const word & regionName(const word &region)
The mesh region name or word::null if polyMesh::defaultRegion.
Definition: polyMesh.C:816
static Map< label > findSharedPoints(const polyMesh &, const scalar mergeTol)
Find topologically and geometrically shared points.
bool found(const word &optName) const
Return true if the named option is found.
Definition: argListI.H:171
static void addOptions(const bool constant=true, const bool withZero=false)
Add timeSelector options to argList::validOptions.
Definition: timeSelector.C:101
bool set(const Key &key, const T &obj)
Copy assign a new entry, overwriting existing entries.
Definition: HashTableI.H:195
Namespace for OpenFOAM.
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:157