globalMeshDataTopology.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 OpenFOAM Foundation
9  Copyright (C) 2015-2024 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 "globalMeshData.H"
30 #include "globalIndex.H"
31 #include "CompactListList.H"
32 #include "processorPolyPatch.H"
33 #include "syncTools.H"
34 
35 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39 
40 // NOTE: the AgglomerationType is anything that behaves like a List with
41 // an operator[] and provides coverage in the (0-nCells) range.
42 // - identityOp() does this
43 
44 template<class AgglomerationType>
45 static void calcCellCellsImpl
46 (
47  const polyMesh& mesh,
48  const AgglomerationType& agglom,
49  const label nLocalCoarse,
50  const bool parallel,
51  CompactListList<label>& cellCells,
52  CompactListList<scalar>* cellCellWeightsPtr = nullptr
53 )
54 {
55  const labelList& faceOwner = mesh.faceOwner();
56  const labelList& faceNeigh = mesh.faceNeighbour();
58 
59  // Global cell numbers (agglomerated numbering)
60  const label myProci = UPstream::myProcNo(UPstream::worldComm);
61  const globalIndex globalAgglom(nLocalCoarse, UPstream::worldComm, parallel);
62 
63  // The agglomerated owner per boundary faces (global numbering)
64  // from the other side (of coupled patches)
65 
66  labelList globalNeighbour;
67  {
68  const label myAgglomOffset = globalAgglom.localStart(myProci);
69 
70  const labelList::subList bndFaceOwner = pbm.faceOwner();
71 
72  const label nBoundaryFaces = bndFaceOwner.size();
73 
74  globalNeighbour.resize(nBoundaryFaces);
75 
76  for (label bfacei = 0; bfacei < nBoundaryFaces; ++bfacei)
77  {
78  label val = agglom[bndFaceOwner[bfacei]];
79  if (val >= 0)
80  {
81  // Only offset 'real' (non-negative) agglomerations
82  val += myAgglomOffset;
83  }
84  globalNeighbour[bfacei] = val;
85  }
86  }
87 
88  // Swap boundary neighbour information:
89  // - cyclics and (optionally) processor
90  syncTools::swapBoundaryFaceList(mesh, globalNeighbour, parallel);
91 
92 
93  // Count number of faces (internal + coupled)
94  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
95 
96  // Number of faces per coarse cell
97  labelList nFacesPerCell(nLocalCoarse, Foam::zero{});
98 
99  for (label facei = 0; facei < mesh.nInternalFaces(); ++facei)
100  {
101  const label own = agglom[faceOwner[facei]];
102  const label nei = agglom[faceNeigh[facei]];
103 
104  // Negative agglomeration (exclude from subset)
105  if (own < 0 || nei < 0) continue;
106 
107  ++nFacesPerCell[own];
108  ++nFacesPerCell[nei];
109  }
110 
111  for (const polyPatch& pp : pbm)
112  {
113  if (pp.coupled() && (parallel || !isA<processorPolyPatch>(pp)))
114  {
115  const label bndOffset = mesh.nInternalFaces();
116 
117  for (const label facei : pp.range())
118  {
119  const label own = agglom[faceOwner[facei]];
120  const label globalNei = globalNeighbour[facei-bndOffset];
121 
122  // Negative agglomeration (exclude from subset)
123  if (own < 0 || globalNei < 0) continue;
124 
125  if
126  (
127  !globalAgglom.isLocal(myProci, globalNei)
128  || globalAgglom.toLocal(myProci, globalNei) != own
129  )
130  {
131  ++nFacesPerCell[own];
132  }
133  }
134  }
135  }
136 
137 
138  // Fill in offset and data
139  // ~~~~~~~~~~~~~~~~~~~~~~~
140 
141  cellCells.resize_nocopy(nFacesPerCell);
142  nFacesPerCell = 0; // Restart the count
143 
144 
145  // CSR indexing
146  const labelList& offsets = cellCells.offsets();
147 
148  // CSR connections
149  labelList& connect = cellCells.values();
150 
151 
152  // CSR connection weights
153  scalarList weights;
154  if (cellCellWeightsPtr)
155  {
156  cellCellWeightsPtr->clear();
157  weights.resize(cellCells.totalSize());
158  }
159 
160 
161  // For internal faces is just offsetted owner and neighbour
162  for (label facei = 0; facei < mesh.nInternalFaces(); ++facei)
163  {
164  const label own = agglom[faceOwner[facei]];
165  const label nei = agglom[faceNeigh[facei]];
166 
167  // Negative agglomeration (exclude from subset)
168  if (own < 0 || nei < 0) continue;
169 
170  const label ownIndex = offsets[own] + nFacesPerCell[own]++;
171  const label neiIndex = offsets[nei] + nFacesPerCell[nei]++;
172 
173  connect[ownIndex] = globalAgglom.toGlobal(myProci, nei);
174  connect[neiIndex] = globalAgglom.toGlobal(myProci, own);
175 
176  if (!weights.empty())
177  {
178  weights[ownIndex] = Foam::mag(mesh.faceAreas()[facei]);
179  weights[neiIndex] = weights[ownIndex];
180  }
181  }
182 
183  // For boundary faces is offsetted coupled neighbour
184  for (const polyPatch& pp : pbm)
185  {
186  if (pp.coupled() && (parallel || !isA<processorPolyPatch>(pp)))
187  {
188  const label bndOffset = mesh.nInternalFaces();
189 
190  for (const label facei : pp.range())
191  {
192  const label own = agglom[faceOwner[facei]];
193  const label globalNei = globalNeighbour[facei-bndOffset];
194 
195  // Negative agglomeration (exclude from subset)
196  if (own < 0 || globalNei < 0) continue;
197 
198  if
199  (
200  !globalAgglom.isLocal(myProci, globalNei)
201  || globalAgglom.toLocal(myProci, globalNei) != own
202  )
203  {
204  const label ownIndex = offsets[own] + nFacesPerCell[own]++;
205 
206  connect[ownIndex] = globalNei;
207 
208  if (!weights.empty())
209  {
210  weights[ownIndex] = Foam::mag(mesh.faceAreas()[facei]);
211  }
212  }
213  }
214  }
215  }
216 
217 
218  // Check for duplicates connections between cells
219  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
220  // Done as postprocessing step since we now have cellCells.
221 
222  // NB: Because of agglomeration, self-connections will occur
223  // and must be filtered out.
224 
225  if (!cellCells.empty())
226  {
227  // Need non-const access to CSR indexing
228  labelList& offsets = cellCells.offsets();
229 
230  label startIndex = offsets[0];
231  label newIndex = 0;
232  labelHashSet nbrCells;
233 
234  const label nCellCells = cellCells.size();
235 
236  for (label celli = 0; celli < nCellCells; ++celli)
237  {
238  // Could be done as combination of std::sort, std::unique_copy
239  // except need to block out 'self' and std::copy_if/remove_if
240  // are undefined for overlapping regions
241 
242  const label self = globalAgglom.toGlobal(myProci, celli);
243 
244  nbrCells.clear();
245  nbrCells.insert(self);
246 
247  const label endIndex = offsets[celli+1];
248 
249  for (label i = startIndex; i < endIndex; ++i)
250  {
251  if (nbrCells.insert(connect[i]))
252  {
253  connect[newIndex] = connect[i];
254 
255  if (!weights.empty())
256  {
257  weights[newIndex] = weights[i];
258  }
259 
260  ++newIndex;
261  }
262  }
263  startIndex = endIndex;
264  offsets[celli+1] = newIndex;
265  }
266 
267  connect.resize(newIndex);
268  if (!weights.empty())
269  {
270  weights.resize(newIndex);
271  }
272  }
273 
274 
275  // CSR connection weights
276  // - addressing is identical to the connections
277  if (cellCellWeightsPtr)
278  {
279  cellCellWeightsPtr->offsets() = cellCells.offsets();
280  cellCellWeightsPtr->values() = std::move(weights);
281  }
282 
283 
284  //forAll(cellCells, celli)
285  //{
286  // Pout<< "Original: Coarse cell " << celli << endl;
287  // forAll(mesh.cellCells()[celli], i)
288  // {
289  // Pout<< " nbr:" << mesh.cellCells()[celli][i] << endl;
290  // }
291  // Pout<< "Compacted: Coarse cell " << celli << endl;
292  // const labelUList cCells = cellCells[celli];
293  // forAll(cCells, i)
294  // {
295  // Pout<< " nbr:" << cCells[i] << endl;
296  // }
297  //}
298 }
300 } // End namespace Foam
301 
302 
303 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
304 
306 (
307  const polyMesh& mesh,
308  const labelUList& agglom,
309  const label nLocalCoarse,
310  const bool parallel,
311  CompactListList<label>& cellCells
312 )
313 {
315  (
316  mesh,
317  agglom,
318  nLocalCoarse,
320  cellCells
321  );
322 }
323 
324 
326 (
327  const polyMesh& mesh,
328  const labelUList& agglom,
329  const label nLocalCoarse,
330  const bool parallel,
331  CompactListList<label>& cellCells,
332  CompactListList<scalar>& cellCellWeights
333 )
334 {
336  (
337  mesh,
338  agglom,
339  nLocalCoarse,
340  parallel,
341  cellCells,
342  &cellCellWeights
343  );
344 }
345 
346 
347 // Convenience forms
348 
350 (
351  const polyMesh& mesh,
352  CompactListList<label>& cellCells,
353  const bool parallel
354 )
355 {
357  (
358  mesh,
360  mesh.nCells(),
361  parallel,
362  cellCells
363  );
364 }
365 
366 
368 (
369  const polyMesh& mesh,
370  const bitSet& selectedCells,
371  CompactListList<label>& cellCells,
372  const bool parallel
373 )
374 {
375  const label nCells = mesh.nCells();
376 
377  labelList agglom(nCells, -1);
378  labelList cellMap;
379 
380  // First pass - sorted order without duplicates
381  label nCompact = 0;
382  for (const label celli : selectedCells)
383  {
384  // A bitSet has no negatives/duplicates, so just check the upper range
385  if (celli >= nCells)
386  {
387  break;
388  }
389  else
390  {
391  agglom[celli] = celli;
392  ++nCompact;
393  }
394  }
395 
396  // Second pass - finalize mappings
397  if (nCompact)
398  {
399  cellMap.resize(nCompact);
400  nCompact = 0;
401 
402  for (label& celli : agglom)
403  {
404  if (celli >= 0)
405  {
406  cellMap[nCompact] = celli;
407  celli = nCompact;
408  ++nCompact;
409 
410  if (nCompact == cellMap.size())
411  {
412  break; // Early termination
413  }
414  }
415  }
416  }
417 
419  (
420  mesh,
421  agglom,
422  nCompact, // == cellMap.size()
423  parallel,
424  cellCells
425  );
426 
427  return cellMap;
428 }
429 
430 
432 (
433  const polyMesh& mesh,
434  const labelUList& selectedCells,
435  CompactListList<label>& cellCells,
436  const bool parallel
437 )
438 {
439  const label nCells = mesh.nCells();
440 
441  labelList agglom(nCells, -1);
442  labelList cellMap;
443 
444  // First pass - creates a sorted order without duplicates
445  label nCompact = 0;
446  for (const label celli : selectedCells)
447  {
448  // Check cell is in range, and squash out duplicates
449  if (celli >= 0 && celli < nCells && agglom[celli] < 0)
450  {
451  agglom[celli] = celli;
452  ++nCompact;
453  }
454  }
455 
456  // Second pass - finalize mappings
457  if (nCompact)
458  {
459  cellMap.resize(nCompact);
460  nCompact = 0;
461 
462  for (label& celli : agglom)
463  {
464  if (celli >= 0)
465  {
466  cellMap[nCompact] = celli;
467  celli = nCompact;
468  ++nCompact;
469 
470  if (nCompact == cellMap.size())
471  {
472  break; // Early termination
473  }
474  }
475  }
476  }
477 
479  (
480  mesh,
481  agglom,
482  nCompact, // == cellMap.size()
483  parallel,
484  cellCells
485  );
486 
487  return cellMap;
488 }
489 
490 
491 // ************************************************************************* //
label toLocal(const label proci, const label i) const
From global to local on proci.
Definition: globalIndexI.H:377
const polyBoundaryMesh & pbm
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
const labelList & offsets() const noexcept
Return the offset table (= size()+1)
bool empty() const noexcept
True if the number of rows/sublists is zero.
label size() const noexcept
The primary size (the number of rows/sublists)
dimensioned< typename typeOfMag< Type >::type > mag(const dimensioned< Type > &dt)
void resize(const label len)
Adjust allocated size of list.
Definition: ListI.H:153
virtual const labelList & faceNeighbour() const
Return face neighbour.
Definition: polyMesh.C:1122
bool empty() const noexcept
True if List is empty (ie, size() is zero)
Definition: UList.H:675
static void swapBoundaryFaceList(const polyMesh &mesh, UList< T > &faceValues, const bool parRun=UPstream::parRun())
Swap coupled boundary face values. Uses eqOp.
Definition: syncTools.H:524
bool parallel() const noexcept
Does the mesh contain processor patches? (also valid when not running parallel)
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:1086
static label worldComm
Communicator for all ranks. May differ from commGlobal() if local worlds are in use.
Definition: UPstream.H:421
bool insert(const Key &key)
Insert a new entry, not overwriting existing entries.
Definition: HashSet.H:232
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:61
A List obtained as a section of another List.
Definition: SubList.H:50
dynamicFvMesh & mesh
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:130
const polyBoundaryMesh & boundaryMesh() const noexcept
Return boundary mesh.
Definition: polyMesh.H:609
void clear()
Remove all entries from table.
Definition: HashTable.C:749
virtual const labelList & faceOwner() const
Return face owner.
Definition: polyMesh.C:1116
label nInternalFaces() const noexcept
Number of internal faces.
bool isLocal(const label proci, const label i) const
Is on processor proci.
Definition: globalIndexI.H:294
A polyBoundaryMesh is a polyPatch list with additional search methods and registered IO...
A packed storage of objects of type <T> using an offset table for access.
A functor that returns its argument unchanged (cf. C++20 std::identity) Should never be specialized...
Definition: stdFoam.H:90
label totalSize() const noexcept
The total addressed size, which corresponds to the end (back) offset and also the sum of all localSiz...
label toGlobal(const label proci, const label i) const
From local to global on proci.
Definition: globalIndexI.H:308
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:59
label nCells() const noexcept
Number of mesh cells.
const vectorField & faceAreas() const
A class representing the concept of 0 (zero) that can be used to avoid manipulating objects known to ...
Definition: zero.H:57
label localStart(const label proci) const
Start of proci data.
Definition: globalIndexI.H:233
const List< T > & values() const noexcept
Return the packed values.
void resize_nocopy(const label mRows, const label nVals)
Redimension without preserving existing content.
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:75
static void calcCellCells(const polyMesh &mesh, const labelUList &agglom, const label nLocalCoarse, const bool parallel, CompactListList< label > &cellCells)
Determine (local or global) cellCells from mesh agglomeration.
static void calcCellCellsImpl(const polyMesh &mesh, const AgglomerationType &agglom, const label nLocalCoarse, const bool parallel, CompactListList< label > &cellCells, CompactListList< scalar > *cellCellWeightsPtr=nullptr)
A patch is a list of labels that address the faces in the global face list.
Definition: polyPatch.H:69
uindirectPrimitivePatch pp(UIndirectList< face >(mesh.faces(), faceLabels), mesh.points())
Namespace for OpenFOAM.
const polyMesh & mesh() const noexcept
Return the mesh reference.
const labelList::subList faceOwner() const
Return face owner for the entire boundary.