parLagrangianDistributor.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) 2015 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 "ListOps.H"
31 #include "passivePositionParticleCloud.H"
32 #include "fileOperation.H"
33 
34 // * * * * * * * * * * * * * * Static Data Members * * * * * * * * * * * * * //
35 
37 
38 
39 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
40 
42 (
43  const fvMesh& srcMesh,
44  const fvMesh& tgtMesh,
45  const label nSrcCells,
46  const mapDistributePolyMesh& distMap
47 )
48 :
49  srcMesh_(srcMesh),
50  tgtMesh_(tgtMesh),
51  distMap_(distMap)
52 {
53  const mapDistribute& cellMap = distMap_.cellMap();
54 
55  // Get destination processors and cells
56  destinationProcID_ = labelList(tgtMesh_.nCells(), UPstream::myProcNo());
57  cellMap.reverseDistribute(nSrcCells, destinationProcID_);
58 
59  destinationCell_ = identity(tgtMesh_.nCells());
60  cellMap.reverseDistribute(nSrcCells, destinationCell_);
61 }
62 
63 
64 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
65 
66 // Find all clouds (on all processors) and for each cloud all the objects.
67 // Result will be synchronised on all processors
69 (
70  const fvMesh& mesh,
71  wordList& cloudNames, // All cloud names on any processor
72  boolList& haveClouds, // Per cloud name, whether my processor has it
73  List<wordList>& objectNames // Per cloud name, the field name
74 )
75 {
76  const IOobject io
77  (
79  mesh.time().timeName(),
80  mesh.thisDb(),
84  );
85 
86  // Using the fileHandler:
87  // - use fileHandler to synthesise correct processor directory
88  // - cannot use readObjects since assumes all processors have same
89  // files (i.e. it only checks processor0)
90  const fileNameList localCloudDirs
91  (
93  (
94  fileHandler().objectPath
95  (
96  io,
97  word::null // typeName: not used currently
98  ),
100  )
101  );
102 
103 
104  // Copy out the local cloud names (and fileName -> word)
105  cloudNames.resize_nocopy(localCloudDirs.size());
106  forAll(localCloudDirs, i)
107  {
108  cloudNames[i] = localCloudDirs[i];
109  }
110 
111  // Synchronise cloud names
112  Pstream::combineReduce(cloudNames, ListOps::uniqueEqOp<word>());
113  Foam::sort(cloudNames); // Consistent order
114 
115  const label nClouds = cloudNames.size();
116 
117  // See which of the global cloudNames I have
118  haveClouds.resize_nocopy(nClouds);
119  haveClouds = false;
120 
121  for (const fileName& localCloudName : localCloudDirs)
122  {
123  const label cloudi = cloudNames.find(localCloudName);
124  if (cloudi >= 0)
125  {
126  haveClouds[cloudi] = true;
127  }
128  }
129 
130  // Collect fields per cloud
131  objectNames.resize_nocopy(nClouds);
132 
133  for (label cloudi = 0; cloudi < nClouds; ++cloudi)
134  {
135  objectNames[cloudi].clear();
136 
137  if (!haveClouds[cloudi]) continue;
138 
139  // Do local scan for valid cloud objects
140  const bool oldParRun = UPstream::parRun(false);
141  IOobjectList localObjs
142  (
143  mesh,
144  mesh.time().timeName(),
145  cloud::prefix/cloudNames[cloudi]
146  );
147  UPstream::parRun(oldParRun);
148 
149  bool isCloud = false;
150  if (localObjs.erase("coordinates"))
151  {
152  isCloud = true;
153  }
154  if (localObjs.erase("positions"))
155  {
156  isCloud = true;
157  }
158 
159  if (isCloud)
160  {
161  // Has coordinates/positions - so must be a valid cloud
162  objectNames[cloudi] = localObjs.sortedNames();
163  }
164  }
165 
166  // Synchronise objectNames (per cloud)
167  for (wordList& objNames : objectNames)
168  {
169  Pstream::combineReduce(objNames, ListOps::uniqueEqOp<word>());
170  Foam::sort(objNames); // Consistent order
171  }
172 }
173 
174 
177 (
178  passivePositionParticleCloud& lpi
179 ) const
180 {
181  //Debug(lpi.size());
182  //const label oldLpi = lpi.size();
183 
184  labelListList sendMap;
185 
186  // Transfer buffers
187  PstreamBuffers pBufs(UPstream::commsTypes::nonBlocking);
188 
189  {
190  // List of lists of particles to be transferred for all of the
191  // neighbour processors
192  List<IDLList<passivePositionParticle>> particleTransferLists
193  (
195  );
196 
197  // Per particle the destination processor
198  labelList destProc(lpi.size());
199 
200  label particleI = 0;
201  for (passivePositionParticle& ppi : lpi)
202  {
203  const label destProcI = destinationProcID_[ppi.cell()];
204  const label destCellI = destinationCell_[ppi.cell()];
205 
206  ppi.cell() = destCellI;
207  destProc[particleI++] = destProcI;
208  particleTransferLists[destProcI].append(lpi.remove(&ppi));
209  }
210 
211 
212  // Per processor the indices of the particles to send
213  sendMap = invertOneToMany(UPstream::nProcs(), destProc);
214 
215 
216  // Stream into send buffers
217  forAll(particleTransferLists, procI)
218  {
219  //Pout<< "To proc " << procI << " sending "
220  // << particleTransferLists[procI] << endl;
221  if (particleTransferLists[procI].size())
222  {
223  UOPstream particleStream(procI, pBufs);
224  particleStream << particleTransferLists[procI];
225  }
226  }
227  }
228 
229 
230  // Start sending
231  pBufs.finishedSends();
232 
233 
234  // The cloud name
235  const word cloudName = lpi.name();
236 
237  {
238  // Temporarily rename original cloud so we can construct a new one
239  // (to distribute the positions) without getting a duplicate
240  // registration warning
241  lpi.rename(cloudName + "_old");
242 
243  // New empty cloud on tgtMesh
244  passivePositionParticleCloud lagrangianPositions
245  (
246  tgtMesh_,
247  Foam::zero{},
248  cloudName
249  );
250 
251  // Retrieve from receive buffers
252  for (const int proci : pBufs.allProcs())
253  {
254  //Pout<< "Receive from processor" << proci << " : "
255  // << pBufs.recvDataCount(proci) << endl;
256 
257  if (pBufs.recvDataCount(proci))
258  {
259  UIPstream particleStream(proci, pBufs);
260 
261  // Receive particles and locate them
262  IDLList<passivePositionParticle> newParticles
263  (
264  particleStream,
265  passivePositionParticle::iNew(tgtMesh_)
266  );
267 
268  for (passivePositionParticle& newp : newParticles)
269  {
270  lagrangianPositions.addParticle(newParticles.remove(&newp));
271  }
272  }
273  }
274 
275  const bool writeOnProc = lagrangianPositions.size();
276 
277  //if (writeOnProc)
278  {
279  // Write coordinates file
280  IOPosition<passivePositionParticleCloud>
281  (
282  lagrangianPositions
283  ).write(writeOnProc);
284 
285  // Optionally write positions file in v1706 format and earlier
287  {
288  IOPosition<passivePositionParticleCloud>
289  (
290  lagrangianPositions,
292  ).write(writeOnProc);
293  }
294  }
295  //else if (!writeOnProc && oldLpi)
296  //{
297  // // When running with -overwrite it should also delete the old
298  // // files. Below works but is not optimal.
299  //
300  // // Remove any existing coordinates
301  // Foam::rm
302  // (
303  // IOPosition<passivePositionParticleCloud>
304  // (
305  // lagrangianPositions
306  // ).objectPath()
307  // );
308  //
309  // // Remove any existing positions
310  // Foam::rm
311  // (
312  // IOPosition<passivePositionParticleCloud>
313  // (
314  // lagrangianPositions,
315  // cloud::geometryType::POSITIONS
316  // ).objectPath()
317  // );
318  //}
319  }
320 
321  // Restore cloud name
322  lpi.rename(cloudName);
323 
324 
325  // The constructMap is in linear (processor) order
327  (
329  std::move(sendMap)
330  );
331 }
332 
333 
336 (
337  const word& cloudName
338 ) const
339 {
340  // Mixed exists/missing on various ranks?
341  // Avoid masterRead+broadcast (can cause blocking)
342 
343  auto& handler = Foam::fileHandler();
344  const bool oldDistributed =
345  handler.distributed
346  (
347  !fileOperation::cacheLevel() || handler.distributed()
348  );
349 
350 
351  // Load cloud
352  passivePositionParticleCloud lpi(srcMesh_, cloudName, false);
353 
354  // Restore distributed flag
355  handler.distributed(oldDistributed);
356 
357  // Distribute particles to other ranks
358  return distributeLagrangianPositions(lpi);
359 }
360 
361 
362 // ************************************************************************* //
static int verbose_
Output verbosity when writing.
void append(const T &val)
Append an element at the end of the list.
Definition: List.H:517
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()
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
const Time & time() const
Return the top-level database.
Definition: fvMesh.H:360
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
Various functions to operate on Lists.
virtual const objectRegistry & thisDb() const
Return the object registry - resolve conflict polyMesh/lduMesh.
Definition: fvMesh.H:376
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
void write(vtk::formatter &fmt, const Type &val, const label n=1)
Component-wise write of a value (N times)
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
dynamicFvMesh & mesh
static void findClouds(const fvMesh &, wordList &cloudNames, boolList &haveClouds, List< wordList > &objectNames)
Find all clouds (on all processors) and for each cloud all the objects. Result will be synchronised o...
labelListList invertOneToMany(const label len, const labelUList &map)
Invert one-to-many map. Unmapped elements will be size 0.
Definition: ListOps.C:107
labelList identity(const label len, label start=0)
Return an identity map of the given length with (map[i] == i), works like std::iota() but returning a...
Definition: labelLists.C:44
const word cloudName(propsDict.get< word >("cloud"))
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:296
static const word null
An empty word.
Definition: word.H:84
const mapDistribute & cellMap() const noexcept
Cell distribute map.
static void combineReduce(const List< commsStruct > &comms, T &value, const CombineOp &cop, const int tag=UPstream::msgType(), const label comm=UPstream::worldComm)
Reduce inplace (cf. MPI Allreduce) applying cop to inplace combine value from different processors...
static int cacheLevel() noexcept
Return cache level.
static word timeName(const scalar t, const int precision=precision_)
Return a time name for the given scalar time value formatted with the given precision.
Definition: Time.C:714
parLagrangianDistributor(const parLagrangianDistributor &)=delete
No copy construct.
List< word > wordList
List of word.
Definition: fileName.H:59
static bool writeLagrangianPositions
Write particle positions file (v1706 format and earlier) Default is true (disable in etc/controlDict)...
Definition: particle.H:472
label nCells() const noexcept
Number of mesh cells.
"nonBlocking" : (MPI_Isend, MPI_Irecv)
A class representing the concept of 0 (zero) that can be used to avoid manipulating objects known to ...
Definition: zero.H:57
Pointer management similar to std::unique_ptr, with some additional methods and type checking...
Definition: HashPtrTable.H:48
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)
static autoPtr< T > New(Args &&... args)
Construct autoPtr with forwarding arguments.
Definition: autoPtr.H:178
List< fileName > fileNameList
List of fileName.
Definition: fileNameList.H:32
fileNameList readDir(const fileName &directory, const fileName::Type type=fileName::Type::FILE, const bool filtergz=true, const bool followLink=true)
Read a directory and return the entries as a fileName List.
Definition: POSIX.C:963
List< bool > boolList
A List of bools.
Definition: List.H:60
Do not request registration (bool: false)
autoPtr< mapDistributeBase > distributeLagrangianPositions(passivePositionParticleCloud &cloud) const
Redistribute and write lagrangian positions.
static const word prefix
The prefix to local: lagrangian.
Definition: cloud.H:79