faMeshTools.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) 2012-2016 OpenFOAM Foundation
9  Copyright (C) 2015-2023 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 \*---------------------------------------------------------------------------*/
28 
29 #include "faMeshTools.H"
30 #include "faBoundaryMeshEntries.H"
31 #include "areaFields.H"
32 #include "edgeFields.H"
33 #include "fileOperation.H"
34 #include "BitOps.H"
35 #include "polyMesh.H"
36 #include "processorFaPatch.H"
37 
38 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
39 
41 {
42  auto& obr = const_cast<objectRegistry&>(mesh.thisDb());
43 
44  // Checkout by name (casting ambiguity)
45  obr.checkOut(faMesh::typeName);
46  obr.checkOut("faBoundaryMesh");
47  obr.checkOut("faSchemes");
48  obr.checkOut("faSolution");
49 }
50 
51 
53 {
54  (void)mesh.globalData();
55 
56  (void)mesh.Le();
57  (void)mesh.magLe();
58  (void)mesh.areaCentres();
59  (void)mesh.edgeCentres();
60 
61  (void)mesh.faceAreaNormals();
62  (void)mesh.edgeAreaNormals();
63  (void)mesh.pointAreaNormals();
64  (void)mesh.faceCurvatures();
65  (void)mesh.edgeTransformTensors();
66 
67  mesh.syncGeom();
68 }
69 
70 
73 (
74  const IOobject& io,
75  const polyMesh& pMesh,
76  const bool masterOnlyReading,
77  const bool verbose
78 )
79 {
80  // Region name
81  // ~~~~~~~~~~~
82 
83  const fileName meshSubDir
84  (
86  );
87 
88 
89  fileName facesInstance;
90 
91  // Patch types
92  // ~~~~~~~~~~~
93  // Read and scatter master patches (without reading master mesh!)
94 
95  PtrList<entry> patchEntries;
96  if (UPstream::master())
97  {
98  const bool oldParRun = UPstream::parRun(false);
99 
100  facesInstance = io.time().findInstance
101  (
102  meshSubDir,
103  "faceLabels",
105  );
106 
107  patchEntries = faBoundaryMeshEntries
108  (
109  IOobject
110  (
111  "faBoundary",
112  facesInstance,
113  meshSubDir,
114  io.db(),
118  )
119  );
120 
121  UPstream::parRun(oldParRun);
122  }
123 
124  // Broadcast information to all
126  (
128  patchEntries,
129  facesInstance
130  );
131 
132 
133  // Dummy meshes
134  // ~~~~~~~~~~~~
135 
136  // Set up to read-if-present. Note: does not search for mesh so set
137  // instance explicitly
138 
139  IOobject meshIO(io);
140  meshIO.instance() = facesInstance;
141  meshIO.readOpt(IOobject::READ_IF_PRESENT);
142 
143  // For mesh components (faceLabels, ...)
144  IOobject cmptIO(meshIO, "faceLabels", meshSubDir);
145  cmptIO.readOpt(IOobject::MUST_READ);
146  cmptIO.writeOpt(IOobject::NO_WRITE);
147  cmptIO.registerObject(false);
148 
149 
150  // Check who has a mesh
151 
152  const fileName meshDir = io.time().path()/facesInstance/meshSubDir;
153  bool haveMesh = isDir(meshDir);
154  if (masterOnlyReading && !UPstream::master())
155  {
156  haveMesh = false;
157  meshIO.readOpt(IOobject::NO_READ);
158  }
159 
160  if (!haveMesh)
161  {
162  cmptIO.readOpt(IOobject::NO_READ);
163  }
164 
165 
166  // Read mesh
167  // ~~~~~~~~~
168  // Now all processors use supplied points,faces etc
169  // Note: solution, schemes are also using the supplied IOobject so
170  // on slave will be NO_READ, on master READ_IF_PRESENT. This will
171  // conflict with e.g. timeStampMaster reading so switch off.
172 
173  const auto oldCheckType = IOobject::fileModificationChecking;
175 
176 
177  // faceLabels
178  cmptIO.rename("faceLabels");
179  labelIOList faceLabels(cmptIO);
180 
181 
183  (
184  pMesh,
185  std::move(faceLabels),
186  meshIO
187  );
188  auto& mesh = *meshPtr;
189 
190  IOobject::fileModificationChecking = oldCheckType;
191 
192 
193  // Some processors without patches? - add patches
194 
196  {
197  // Use patchEntries, which were read on master and broadcast
198 
199  faPatchList patches(patchEntries.size());
200  label nPatches = 0;
201 
202  const bool isEmptyMesh = (mesh.faceLabels().empty());
203 
204  forAll(patchEntries, patchi)
205  {
206  const entry& e = patchEntries[patchi];
207  const word type(e.dict().get<word>("type"));
208  const word& name = e.keyword();
209 
210  if
211  (
212  type == processorFaPatch::typeName
213  )
214  {
215  // Stop at the first processor patch.
216  // - logic will not work with inter-mixed proc-patches anyhow
217  break;
218  }
219  else
220  {
221  dictionary patchDict(e.dict());
222 
223  if (isEmptyMesh)
224  {
225  patchDict.set("edgeLabels", labelList());
226  }
227 
228  patches.set
229  (
230  patchi,
232  (
233  name,
234  patchDict,
235  nPatches++,
236  mesh.boundary()
237  )
238  );
239  }
240  }
241 
243  mesh.addFaPatches(patches, false); // No parallel comms
244  }
245 
246  // Recreate basic geometry, globalMeshData etc.
247  mesh.init(false);
248  (void)mesh.globalData();
249 
250  return meshPtr;
251 }
252 
253 
255 Foam::faMeshTools::loadOrCreateMeshImpl
256 (
257  const IOobject& io,
258  refPtr<fileOperation>* readHandlerPtr, // Can be nullptr
259  const polyMesh& pMesh,
260  const bool decompose,
261  const bool verbose
262 )
263 {
264  // Region name
265  // ~~~~~~~~~~~
266 
267  const fileName meshSubDir
268  (
269  pMesh.regionName() / faMesh::meshSubDir
270  );
271 
272 
273  // Patch types
274  // ~~~~~~~~~~~
275  // Read and scatter master patches (without reading master mesh!)
276 
277  PtrList<entry> patchEntries;
278  if (UPstream::master())
279  {
280  const bool oldParRun = UPstream::parRun(false);
281  const label oldNumProcs = fileHandler().nProcs();
282  const int oldCache = fileOperation::cacheLevel(0);
283 
284  patchEntries = faBoundaryMeshEntries
285  (
286  IOobject
287  (
288  "faBoundary",
289  io.instance(),
290  meshSubDir,
291  io.db(),
295  )
296  );
297 
298  fileOperation::cacheLevel(oldCache);
299  if (oldParRun)
300  {
301  const_cast<fileOperation&>(fileHandler()).nProcs(oldNumProcs);
302  }
303  UPstream::parRun(oldParRun);
304  }
305 
306  // Broadcast: send patches to all
308 
309 
310  // Check who has or needs a mesh.
311  bool haveLocalMesh = false;
312 
313  if (readHandlerPtr)
314  {
315  // Non-null reference when a mesh exists on given processor
316  haveLocalMesh = (*readHandlerPtr).good();
317  }
318  else
319  {
320  // No file handler.
321  // For 'decompose', only need mesh on master.
322  // Otherwise check for presence of the "faceLabels" file
323 
324  haveLocalMesh =
325  (
326  decompose
327  ? UPstream::master()
328  : fileHandler().isFile
329  (
330  fileHandler().filePath
331  (
332  io.time().path()/io.instance()/meshSubDir/"faceLabels"
333  )
334  )
335  );
336  }
337 
338 
339  // Globally consistent information about who has a mesh
340  boolList haveMesh
341  (
342  UPstream::allGatherValues<bool>(haveLocalMesh)
343  );
344 
345 
346  autoPtr<faMesh> meshPtr;
347 
348  if (!haveLocalMesh)
349  {
350  // No local mesh - need to synthesize one
351 
352  const bool oldParRun = UPstream::parRun(false);
353  const label oldNumProcs = fileHandler().nProcs();
354  const int oldCache = fileOperation::cacheLevel(0);
355 
356  // Create dummy mesh - on procs that don't already have a mesh
357  meshPtr.reset
358  (
359  new faMesh
360  (
361  pMesh,
362  labelList(),
364  )
365  );
366  faMesh& mesh = *meshPtr;
367 
368  // Add patches
369  faPatchList patches(patchEntries.size());
370  label nPatches = 0;
371 
372  forAll(patchEntries, patchi)
373  {
374  const entry& e = patchEntries[patchi];
375  const word type(e.dict().get<word>("type"));
376  const word& name = e.keyword();
377 
378  if
379  (
380  type == processorFaPatch::typeName
381  )
382  {
383  // Stop at the first processor patch.
384  // - logic will not work with inter-mixed proc-patches anyhow
385  break;
386  }
387  else
388  {
389  dictionary patchDict(e.dict());
390  patchDict.set("edgeLabels", labelList());
391 
392  patches.set
393  (
394  patchi,
396  (
397  name,
398  patchDict,
399  nPatches++,
400  mesh.boundary()
401  )
402  );
403  }
404  }
406  mesh.addFaPatches(patches, false); // No parallel comms
407 
408  if (!readHandlerPtr)
409  {
410  // The 'old' way of doing things.
411  // Write the dummy mesh to disk for subsequent re-reading.
412  //
413  // This is not particularly elegant.
414 
415  // Bad hack, but the underlying polyMesh is NO_WRITE
416  // so it does not create the faMesh subDir for us...
417  Foam::mkDir(mesh.boundary().path());
418 
419  //Pout<< "Writing dummy mesh to " << mesh.boundary().path() << nl;
420  mesh.write();
421 
422  // Discard - it will be re-read later
423  meshPtr.reset(nullptr);
424  }
425 
426  fileOperation::cacheLevel(oldCache);
427  if (oldParRun)
428  {
429  const_cast<fileOperation&>(fileHandler()).nProcs(oldNumProcs);
430  }
431  UPstream::parRun(oldParRun); // Restore parallel state
432  }
433  else if (readHandlerPtr && haveLocalMesh)
434  {
435  const labelList meshProcIds(BitOps::sortedToc(haveMesh));
436 
437  UPstream::communicator newCommunicator;
438  const label oldWorldComm = UPstream::commWorld();
439 
440  auto& readHandler = *readHandlerPtr;
441  auto oldHandler = fileOperation::fileHandler(readHandler);
442 
443  // With IO ranks the communicator of the fileOperation will
444  // only include the ranks for the current IO rank.
445  // Instead allocate a new communicator for everyone with a mesh
446 
447  const auto& handlerProcIds = UPstream::procID(fileHandler().comm());
448 
449  // Comparing global ranks in the communicator.
450  // Use std::equal for the List<label> vs List<int> comparison
451 
452  if
453  (
454  meshProcIds.size() == handlerProcIds.size()
455  && std::equal
456  (
457  meshProcIds.cbegin(),
458  meshProcIds.cend(),
459  handlerProcIds.cbegin()
460  )
461  )
462  {
463  // Can use the handler communicator as is.
465  }
466  else if
467  (
468  UPstream::nProcs(fileHandler().comm())
470  )
471  {
472  // Need a new communicator for the fileHandler.
473 
474  // Warning: MS-MPI currently uses MPI_Comm_create() instead of
475  // MPI_Comm_create_group() so it will block here!
476 
477  newCommunicator.reset(UPstream::worldComm, meshProcIds);
478  UPstream::commWorld(newCommunicator.comm());
479  }
480 
481  // Load but do not initialise
482  meshPtr = autoPtr<faMesh>::New(pMesh, false);
483 
484  readHandler = fileOperation::fileHandler(oldHandler);
485  UPstream::commWorld(oldWorldComm);
486 
487  // Reset mesh communicator to the real world comm
488  meshPtr().comm() = UPstream::commWorld();
489  }
490 
491 
492  if (!meshPtr)
493  {
494  // Using the 'old' way of doing things (writing to disk and re-reading).
495 
496  // Read mesh from disk
497  //
498  // Now all processors have a (possibly zero size) mesh so can
499  // read in parallel
500 
502  // Load but do not initialise
503  meshPtr = autoPtr<faMesh>::New(pMesh, false);
504  }
505 
506  faMesh& mesh = meshPtr();
507 
508  // Check patches
509  // ~~~~~~~~~~~~~
510 
511  #if 0
512  if (!UPstream::master() && haveLocalMesh)
513  {
514  // Check master names against mine
515 
516  const faBoundaryMesh& patches = mesh.boundary();
517 
518  forAll(patchEntries, patchi)
519  {
520  const entry& e = patchEntries[patchi];
521  const word type(e.dict().get<word>("type"));
522  const word& name = e.keyword();
523 
524  if
525  (
526  type == processorFaPatch::typeName
527  )
528  {
529  break;
530  }
531 
532  if (patchi >= patches.size())
533  {
535  << "Non-processor patches not synchronised." << endl
536  << "Processor " << UPstream::myProcNo()
537  << " has only " << patches.size()
538  << " patches, master has "
539  << patchi
540  << exit(FatalError);
541  }
542 
543  if
544  (
545  type != patches[patchi].type()
546  || name != patches[patchi].name()
547  )
548  {
550  << "Non-processor patches not synchronised." << endl
551  << "Master patch " << patchi
552  << " name:" << type
553  << " type:" << type << endl
554  << "Processor " << UPstream::myProcNo()
555  << " patch " << patchi
556  << " has name:" << patches[patchi].name()
557  << " type:" << patches[patchi].type()
558  << exit(FatalError);
559  }
560  }
561  }
562  #endif
563 
564 
565  // Recreate basic geometry, globalMeshData etc.
566  mesh.init(false);
567  (void)mesh.globalData();
568 
573 
574  // Do some checks.
575 
576  // Check if the boundary definition is unique
577  // and processor patches are correct
578  mesh.boundary().checkDefinition(verbose);
579  mesh.boundary().checkParallelSync(verbose);
581  return meshPtr;
582 }
583 
584 
587 (
588  const IOobject& io,
589  const polyMesh& pMesh,
590  const bool decompose,
591  const bool verbose
592 )
593 {
594  return faMeshTools::loadOrCreateMeshImpl
595  (
596  io,
597  nullptr, // fileOperation (ignore)
598  pMesh,
599  decompose,
600  verbose
601  );
602 }
603 
604 
607 (
608  const IOobject& io,
609  const polyMesh& pMesh,
610  refPtr<fileOperation>& readHandler,
611  const bool verbose
612 )
613 {
614  return faMeshTools::loadOrCreateMeshImpl
615  (
616  io,
617  &readHandler,
618  pMesh,
619  false, // decompose (ignored)
620  verbose
621  );
622 }
623 
624 
625 // ************************************************************************* //
label nPatches
Definition: readKivaGrid.H:396
PtrList< faPatch > faPatchList
Store lists of faPatch as a PtrList.
Definition: faPatch.H:59
Finite area mesh (used for 2-D non-Euclidian finite area method) defined using a patch of faces on a ...
Definition: faMesh.H:87
static autoPtr< faMesh > newMesh(const IOobject &io, const polyMesh &pMesh, const bool masterOnlyReading, const bool verbose=false)
Read mesh or create dummy mesh (0 faces, >0 patches).
Definition: faMeshTools.C:66
static autoPtr< faPatch > New(const word &name, const dictionary &dict, const label index, const faBoundaryMesh &bm)
Return pointer to a new patch created on freestore from dictionary.
Definition: faPatchNew.C:28
fileName path() const
Return path = rootPath/caseName. Same as TimePaths::path()
Definition: Time.H:503
A class for handling file names.
Definition: fileName.H:72
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:125
word findInstance(const fileName &dir, const word &name=word::null, IOobjectOption::readOption rOpt=IOobjectOption::MUST_READ, const word &stopInstance=word::null) const
Return time instance (location) of dir that contains the file name (eg, used in reading mesh data)...
Definition: Time.C:725
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:598
const word & name() const noexcept
Return the object name.
Definition: IOobjectI.H:195
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
static bool & parRun() noexcept
Test if this a parallel run.
Definition: UPstream.H:1049
refPtr< fileOperation > fileHandler(std::nullptr_t)
Delete current file handler - forwards to fileOperation::handler()
virtual bool init(const bool doInit)
Initialise all non-demand-driven data.
Definition: dynamicFvMesh.C:84
bool checkOut(regIOobject *io) const
Remove a regIOobject from registry and free memory if the object is ownedByRegistry. A nullptr is ignored.
Ignore writing from objectRegistry::writeObject()
static int myProcNo(const label communicator=worldComm)
Rank of this process in the communicator (starting from masterNo()). Can be negative if the process i...
Definition: UPstream.H:1074
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:409
labelList faceLabels(nFaceLabels)
A class for managing references or pointers (no reference counting)
Definition: HashPtrTable.H:49
bool equal(const Matrix< Form1, Type > &A, const Matrix< Form2, Type > &B, const bool verbose=false, const label maxDiffs=10, const scalar relTol=1e-5, const scalar absTol=1e-8)
Compare matrix elements for absolute or relative equality.
Definition: MatrixTools.C:27
static void broadcast(Type &value, const label comm=UPstream::worldComm)
Broadcast content (contiguous or non-contiguous) to all communicator ranks. Does nothing in non-paral...
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
bool isDir(const fileName &name, const bool followLink=true)
Does the name exist as a DIRECTORY in the file system?
Definition: POSIX.C:860
fileName::Type type(const fileName &name, const bool followLink=true)
Return the file type: DIRECTORY or FILE, normally following symbolic links.
Definition: POSIX.C:799
static label nProcs(const label communicator=worldComm)
Number of ranks in parallel run (for given communicator). It is 1 for serial run. ...
Definition: UPstream.H:1065
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
IOList< label > labelIOList
IO for a List of label.
Definition: labelIOList.H:32
dynamicFvMesh & mesh
bool mkDir(const fileName &pathName, mode_t mode=0777)
Make a directory and return an error if it could not be created.
Definition: POSIX.C:614
word name(const expressions::valueTypeCode typeCode)
A word representation of a valueTypeCode. Empty for expressions::valueTypeCode::INVALID.
Definition: exprTraits.C:127
Foam::autoPtr< Foam::dynamicFvMesh > meshPtr
label size() const noexcept
The number of entries in the list.
Definition: UPtrListI.H:106
Reading is optional [identical to LAZY_READ].
const objectRegistry & db() const noexcept
Return the local objectRegistry.
Definition: IOobject.C:450
const globalMeshData & globalData() const
Return parallel info (demand-driven)
Definition: polyMesh.C:1311
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:159
virtual bool write(const bool writeOnProc=true) const
Write mesh using IO settings from time.
Definition: fvMesh.C:1102
const Time & time() const noexcept
Return Time associated with the objectRegistry.
Definition: IOobject.C:456
bool empty() const noexcept
True if the hash table is empty.
Definition: HashTable.H:337
static int cacheLevel() noexcept
Return cache level.
fileName path(UMean.rootPath()/UMean.caseName()/"graphs"/UMean.instance())
static fileCheckTypes fileModificationChecking
Type of file modification checking.
Definition: IOobject.H:343
static label commWorld() noexcept
Communicator for all ranks (respecting any local worlds)
Definition: UPstream.H:429
void resize(const label newLen)
Adjust size of PtrList.
Definition: PtrList.C:95
const fileName & instance() const noexcept
Read access to instance path component.
Definition: IOobjectI.H:266
static autoPtr< faMesh > loadOrCreateMesh(const IOobject &io, const polyMesh &pMesh, const bool decompose, const bool verbose=false)
Definition: faMeshTools.C:580
static void broadcasts(const label comm, Type &arg1, Args &&... args)
Broadcast multiple items to all communicator ranks. Does nothing in non-parallel. ...
Read and store dictionary entries for finite-area boundary patches. The object is *never* registered ...
bool isFile(const fileName &name, const bool checkGzip=true, const bool followLink=true)
Does the name exist as a FILE in the file system?
Definition: POSIX.C:877
bool empty() const noexcept
True if the list is empty (ie, size() is zero)
Definition: UPtrListI.H:99
static word meshSubDir
The mesh sub-directory name (usually "faMesh")
Definition: faMesh.H:701
static void unregisterMesh(const faMesh &mesh)
Unregister the faMesh from its associated polyMesh to prevent triggering on polyMesh changes etc...
Definition: faMeshTools.C:33
static List< int > & procID(const label communicator)
The list of ranks within a given communicator.
Definition: UPstream.H:1130
static bool master(const label communicator=worldComm)
True if process corresponds to the master rank in the communicator.
Definition: UPstream.H:1082
const polyBoundaryMesh & patches
Nothing to be read.
Automatically write from objectRegistry::writeObject()
List< label > sortedToc(const UList< bool > &bools)
Return the (sorted) values corresponding to &#39;true&#39; entries.
Definition: BitOps.C:195
const fvBoundaryMesh & boundary() const noexcept
Return reference to boundary mesh.
Definition: fvMesh.H:395
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:74
static const fileOperation & fileHandler()
Return the current file handler. Will create the default file handler if necessary.
List< label > labelList
A List of labels.
Definition: List.H:62
IOobject io("surfaceFilmProperties", mesh.time().constant(), mesh, IOobject::READ_IF_PRESENT, IOobject::NO_WRITE, IOobject::NO_REGISTER)
Registry of regIOobjects.
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition: autoPtr.H:178
bool returnReduceOr(const bool value, const label comm=UPstream::worldComm)
Perform logical (or) MPI Allreduce on a copy. Uses UPstream::reduceOr.
Defines the attributes of an object for which implicit objectRegistry management is supported...
Definition: IOobject.H:172
List< bool > boolList
A List of bools.
Definition: List.H:60
static void forceDemandDriven(faMesh &mesh)
Force creation of everything that might vaguely be used by patches.
Definition: faMeshTools.C:45
static const word & regionName(const word &region)
The mesh region name or word::null if polyMesh::defaultRegion.
Definition: polyMesh.C:811
Do not request registration (bool: false)