primitiveMeshEdges.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) 2018-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 "primitiveMesh.H"
30 #include "DynamicList.H"
31 #include "demandDrivenData.H"
32 #include "SortableList.H"
33 #include "ListOps.H"
34 
35 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
36 
37 namespace Foam
38 {
39 
40 // Helper: return (after optional creation) edge between two points
41 static label getEdge
42 (
45 
46  const label pointi,
47  const label nextPointi
48 )
49 {
50  // Find connection between pointi and nextPointi
51  for (const label edgei : pe[pointi])
52  {
53  if (edgei < es.size() && es[edgei].contains(nextPointi))
54  {
55  return edgei;
56  }
57  }
58 
59  // Make new edge.
60  const label edgei = es.size();
61  pe[pointi].push_back(edgei);
62 
63  if (nextPointi != pointi)
64  {
65  // Very occasionally (e.g. blockMesh) a face can have duplicate
66  // vertices. Make sure we register pointEdges only once.
67  pe[nextPointi].push_back(edgei);
68  }
69 
70  if (pointi < nextPointi)
71  {
72  es.emplace_back(pointi, nextPointi);
73  }
74  else
75  {
76  es.emplace_back(nextPointi, pointi);
77  }
78  return edgei;
79 }
80 
81 } // End namespace Foam
82 
83 
84 // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * //
85 
86 void Foam::primitiveMesh::calcEdges(const bool doFaceEdges) const
87 {
88  if (debug)
89  {
90  Pout<< "primitiveMesh::calcEdges(const bool) : "
91  << "calculating edges, pointEdges and optionally faceEdges"
92  << endl;
93  }
94 
95  // It is an error to attempt to recalculate edges
96  // if the pointer is already set
97  if ((edgesPtr_ || pePtr_) || (doFaceEdges && fePtr_))
98  {
100  << "edges or pointEdges or faceEdges already calculated"
101  << abort(FatalError);
102  }
103  else
104  {
105  // ALGORITHM:
106  // Go through the faces list. Search pointEdges for existing edge.
107  // If not found create edge and add to pointEdges.
108  // In second loop sort edges in order of increasing neighbouring
109  // point.
110  // This algorithm replaces the one using pointFaces which used more
111  // allocations but less memory and was on practical cases
112  // quite a bit slower.
113 
114  const faceList& fcs = faces();
115 
116  // Size up lists
117  // ~~~~~~~~~~~~~
118 
119  // Estimate pointEdges storage
120  List<DynamicList<label>> pe(nPoints());
121  forAll(pe, pointi)
122  {
123  pe[pointi].setCapacity(primitiveMesh::edgesPerPoint_);
124  }
125 
126  // Estimate edges storage
127  DynamicList<edge> es(pe.size()*primitiveMesh::edgesPerPoint_/2);
128 
129  // Estimate faceEdges storage
130  if (doFaceEdges)
131  {
132  fePtr_ = new labelListList(fcs.size());
133  labelListList& faceEdges = *fePtr_;
134  forAll(fcs, facei)
135  {
136  faceEdges[facei].setSize(fcs[facei].size());
137  }
138  }
139 
140 
141  // Find consecutive face points in edge list
142  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
143 
144  // Edges on boundary faces
145  label nExtEdges = 0;
146  // Edges using no boundary point
147  nInternal0Edges_ = 0;
148  // Edges using 1 boundary point
149  label nInt1Edges = 0;
150  // Edges using two boundary points but not on boundary face:
151  // edges.size()-nExtEdges-nInternal0Edges_-nInt1Edges
152 
153  // Ordering is different if points are ordered.
154  if (nInternalPoints_ == -1)
155  {
156  // No ordering. No distinction between types.
157  forAll(fcs, facei)
158  {
159  const face& f = fcs[facei];
160 
161  forAll(f, fp)
162  {
163  label pointi = f[fp];
164  label nextPointi = f[f.fcIndex(fp)];
165 
166  label edgeI = getEdge(pe, es, pointi, nextPointi);
167 
168  if (doFaceEdges)
169  {
170  (*fePtr_)[facei][fp] = edgeI;
171  }
172  }
173  }
174  // Assume all edges internal
175  nExtEdges = 0;
176  nInternal0Edges_ = es.size();
177  nInt1Edges = 0;
178  }
179  else
180  {
181  // 1. Do external faces first. This creates external edges.
182  for (label facei = nInternalFaces_; facei < fcs.size(); facei++)
183  {
184  const face& f = fcs[facei];
185 
186  forAll(f, fp)
187  {
188  label pointi = f[fp];
189  label nextPointi = f[f.fcIndex(fp)];
190 
191  label oldNEdges = es.size();
192  label edgeI = getEdge(pe, es, pointi, nextPointi);
193 
194  if (es.size() > oldNEdges)
195  {
196  nExtEdges++;
197  }
198  if (doFaceEdges)
199  {
200  (*fePtr_)[facei][fp] = edgeI;
201  }
202  }
203  }
204 
205  // 2. Do internal faces. This creates internal edges.
206  for (label facei = 0; facei < nInternalFaces_; facei++)
207  {
208  const face& f = fcs[facei];
209 
210  forAll(f, fp)
211  {
212  label pointi = f[fp];
213  label nextPointi = f[f.fcIndex(fp)];
214 
215  label oldNEdges = es.size();
216  label edgeI = getEdge(pe, es, pointi, nextPointi);
217 
218  if (es.size() > oldNEdges)
219  {
220  if (pointi < nInternalPoints_)
221  {
222  if (nextPointi < nInternalPoints_)
223  {
224  nInternal0Edges_++;
225  }
226  else
227  {
228  nInt1Edges++;
229  }
230  }
231  else
232  {
233  if (nextPointi < nInternalPoints_)
234  {
235  nInt1Edges++;
236  }
237  else
238  {
239  // Internal edge with two points on boundary
240  }
241  }
242  }
243  if (doFaceEdges)
244  {
245  (*fePtr_)[facei][fp] = edgeI;
246  }
247  }
248  }
249  }
250 
251 
252  // For unsorted meshes the edges will be in order of occurrence inside
253  // the faces. For sorted meshes the first nExtEdges will be the external
254  // edges.
255 
256  if (nInternalPoints_ != -1)
257  {
258  nInternalEdges_ = es.size()-nExtEdges;
259  nInternal1Edges_ = nInternal0Edges_+nInt1Edges;
260 
261  //Pout<< "Edge overview:" << nl
262  // << " total number of edges : " << es.size()
263  // << nl
264  // << " boundary edges : " << nExtEdges
265  // << nl
266  // << " edges using no boundary point : "
267  // << nInternal0Edges_
268  // << nl
269  // << " edges using one boundary points : " << nInt1Edges
270  // << nl
271  // << " edges using two boundary points : "
272  // << es.size()-nExtEdges-nInternal0Edges_-nInt1Edges << endl;
273  }
274 
275 
276  // Like faces sort edges in order of increasing neighbouring point.
277  // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
278  // Automatically if points are sorted into internal and external points
279  // the edges will be sorted into
280  // - internal point to internal point
281  // - internal point to external point
282  // - external point to external point
283  // The problem is that the last one mixes external edges with internal
284  // edges with two points on the boundary.
285 
286 
287  // Map to sort into new upper-triangular order
288  labelList oldToNew(es.size());
289  if (debug)
290  {
291  oldToNew = -1;
292  }
293 
294  // start of edges with 0 boundary points
295  label internal0EdgeI = 0;
296 
297  // start of edges with 1 boundary points
298  label internal1EdgeI = nInternal0Edges_;
299 
300  // start of edges with 2 boundary points
301  label internal2EdgeI = nInternal1Edges_;
302 
303  // start of external edges
304  label externalEdgeI = nInternalEdges_;
305 
306 
307  // To sort neighbouring points in increasing order. Defined outside
308  // for optimisation reasons: if all connectivity size same will need
309  // no reallocations
310  SortableList<label> nbrPoints(primitiveMesh::edgesPerPoint_);
311 
312  forAll(pe, pointi)
313  {
314  const DynamicList<label>& pEdges = pe[pointi];
315 
316  nbrPoints.setSize(pEdges.size());
317 
318  forAll(pEdges, i)
319  {
320  const edge& e = es[pEdges[i]];
321 
322  label nbrPointi = e.otherVertex(pointi);
323 
324  if (nbrPointi < pointi)
325  {
326  nbrPoints[i] = -1;
327  }
328  else
329  {
330  nbrPoints[i] = nbrPointi;
331  }
332  }
333  nbrPoints.sort();
334 
335 
336  if (nInternalPoints_ == -1)
337  {
338  // Sort all upper-triangular
339  forAll(nbrPoints, i)
340  {
341  if (nbrPoints[i] != -1)
342  {
343  label edgeI = pEdges[nbrPoints.indices()[i]];
344 
345  oldToNew[edgeI] = internal0EdgeI++;
346  }
347  }
348  }
349  else
350  {
351  if (pointi < nInternalPoints_)
352  {
353  forAll(nbrPoints, i)
354  {
355  label nbrPointi = nbrPoints[i];
356 
357  label edgeI = pEdges[nbrPoints.indices()[i]];
358 
359  if (nbrPointi != -1)
360  {
361  if (edgeI < nExtEdges)
362  {
363  // External edge
364  oldToNew[edgeI] = externalEdgeI++;
365  }
366  else if (nbrPointi < nInternalPoints_)
367  {
368  // Both points inside
369  oldToNew[edgeI] = internal0EdgeI++;
370  }
371  else
372  {
373  // One points inside, one outside
374  oldToNew[edgeI] = internal1EdgeI++;
375  }
376  }
377  }
378  }
379  else
380  {
381  forAll(nbrPoints, i)
382  {
383  label nbrPointi = nbrPoints[i];
384 
385  label edgeI = pEdges[nbrPoints.indices()[i]];
386 
387  if (nbrPointi != -1)
388  {
389  if (edgeI < nExtEdges)
390  {
391  // External edge
392  oldToNew[edgeI] = externalEdgeI++;
393  }
394  else if (nbrPointi < nInternalPoints_)
395  {
396  // Not possible!
398  << abort(FatalError);
399  }
400  else
401  {
402  // Both points outside
403  oldToNew[edgeI] = internal2EdgeI++;
404  }
405  }
406  }
407  }
408  }
409  }
410 
411 
412  if (debug)
413  {
414  label edgeI = oldToNew.find(-1);
415 
416  if (edgeI != -1)
417  {
418  const edge& e = es[edgeI];
419 
421  << "Did not sort edge " << edgeI << " points:" << e
422  << " coords:" << points()[e[0]] << points()[e[1]]
423  << endl
424  << "Current buckets:" << endl
425  << " internal0EdgeI:" << internal0EdgeI << endl
426  << " internal1EdgeI:" << internal1EdgeI << endl
427  << " internal2EdgeI:" << internal2EdgeI << endl
428  << " externalEdgeI:" << externalEdgeI << endl
429  << abort(FatalError);
430  }
431  }
432 
433 
434 
435  // Renumber and transfer.
436 
437  // Edges
438  edgesPtr_ = new edgeList(es.size());
439  edgeList& edges = *edgesPtr_;
440  forAll(es, edgeI)
441  {
442  edges[oldToNew[edgeI]] = es[edgeI];
443  }
444 
445  // pointEdges
446  pePtr_ = new labelListList(nPoints());
447  labelListList& pointEdges = *pePtr_;
448  forAll(pe, pointi)
449  {
450  DynamicList<label>& pEdges = pe[pointi];
451  pEdges.shrink();
452  inplaceRenumber(oldToNew, pEdges);
453  pointEdges[pointi].transfer(pEdges);
454  Foam::sort(pointEdges[pointi]);
455  }
456 
457  // faceEdges
458  if (doFaceEdges)
459  {
460  labelListList& faceEdges = *fePtr_;
461  forAll(faceEdges, facei)
462  {
463  inplaceRenumber(oldToNew, faceEdges[facei]);
464  }
465  }
466  }
467 }
468 
469 
470 // * * * * * * * * * * * * * * * Local Functions * * * * * * * * * * * * * * //
471 
472 namespace Foam
473 {
474 
475 // Helper: for on-the-fly addressing calculation
477 (
478  const labelUList& list1,
479  const labelUList& list2
480 )
481 {
482  label result = -1;
483 
484  auto iter1 = list1.begin();
485  auto iter2 = list2.begin();
486 
487  while (iter1 != list1.end() && iter2 != list2.end())
488  {
489  if (*iter1 < *iter2)
490  {
491  ++iter1;
492  }
493  else if (*iter1 > *iter2)
494  {
495  ++iter2;
496  }
497  else
498  {
499  result = *iter1;
500  break;
501  }
502  }
503  if (result == -1)
504  {
506  << "No common elements in lists " << list1 << " and " << list2
507  << abort(FatalError);
508  }
509  return result;
510 }
511 
512 } // End namespace Foam
513 
514 
515 // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * //
516 
518 {
519  if (!edgesPtr_)
520  {
521  //calcEdges(true);
522  calcEdges(false);
523  }
524 
525  return *edgesPtr_;
526 }
527 
529 {
530  if (!pePtr_)
531  {
532  //calcEdges(true);
533  calcEdges(false);
534  }
535 
536  return *pePtr_;
537 }
538 
539 
541 {
542  if (!fePtr_)
543  {
544  if (debug)
545  {
546  Pout<< "primitiveMesh::faceEdges() : "
547  << "calculating faceEdges" << endl;
548  }
549 
550  //calcEdges(true);
551  const faceList& fcs = faces();
552  const labelListList& pe = pointEdges();
553  const edgeList& es = edges();
554 
555  fePtr_ = new labelListList(fcs.size());
556  labelListList& faceEdges = *fePtr_;
557 
558  forAll(fcs, facei)
559  {
560  const face& f = fcs[facei];
561 
562  labelList& fEdges = faceEdges[facei];
563  fEdges.setSize(f.size());
564 
565  forAll(f, fp)
566  {
567  label pointi = f[fp];
568  label nextPointi = f[f.fcIndex(fp)];
569 
570  // Find edge between pointi, nextPontI
571  const labelList& pEdges = pe[pointi];
572 
573  forAll(pEdges, i)
574  {
575  label edgeI = pEdges[i];
576 
577  if (es[edgeI].otherVertex(pointi) == nextPointi)
578  {
579  fEdges[fp] = edgeI;
580  break;
581  }
582  }
583  }
584  }
585  }
586 
587  return *fePtr_;
588 }
589 
590 
591 void Foam::primitiveMesh::clearOutEdges()
592 {
593  deleteDemandDrivenData(edgesPtr_);
594  deleteDemandDrivenData(pePtr_);
596  labels_.clear();
597  labelSet_.clear();
598 }
599 
600 
602 (
603  const label facei,
604  DynamicList<label>& storage
605 ) const
606 {
607  if (hasFaceEdges())
608  {
609  return faceEdges()[facei];
610  }
611 
612  const labelListList& pointEs = pointEdges();
613  const face& f = faces()[facei];
614 
615  storage.clear();
616  if (storage.capacity() < f.size())
617  {
618  storage.setCapacity(f.size());
619  }
620 
621  forAll(f, fp)
622  {
623  storage.push_back
624  (
626  (
627  pointEs[f[fp]],
628  pointEs[f.nextLabel(fp)]
629  )
630  );
631  }
632 
633  return storage;
634 }
635 
636 
637 const Foam::labelList& Foam::primitiveMesh::faceEdges(const label facei) const
638 {
639  return faceEdges(facei, labels_);
640 }
641 
642 
644 (
645  const label celli,
646  labelHashSet& set,
647  DynamicList<label>& storage
648 ) const
649 {
650  if (hasCellEdges())
651  {
652  return cellEdges()[celli];
653  }
654 
655  const labelList& cFaces = cells()[celli];
656 
657  set.clear();
658 
659  for (const label facei : cFaces)
660  {
661  set.insert(faceEdges(facei));
662  }
663 
664  storage.clear();
665  if (storage.capacity() < set.size())
666  {
667  storage.setCapacity(set.size());
668  }
669 
670  for (const label edgei : set)
671  {
672  storage.push_back(edgei);
673  }
674 
675  return storage;
676 }
677 
678 
679 const Foam::labelList& Foam::primitiveMesh::cellEdges(const label celli) const
680 {
681  return cellEdges(celli, labelSet_, labels_);
682 }
683 
684 
685 // ************************************************************************* //
const labelListList & cellEdges() const
void size(const label n)
Older name for setAddressableSize.
Definition: UList.H:116
void transfer(List< T > &list)
Transfer the contents of the argument List into this list and annul the argument list.
Definition: List.C:326
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...
const labelListList & faceEdges() const
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:598
List< edge > edgeList
List of edge.
Definition: edgeList.H:32
A 1D array of objects of type <T>, where the size of the vector is known and used for subscript bound...
Definition: BitOps.H:56
const labelListList & pointEdges() const
static label findFirstCommonElementFromSortedLists(const labelUList &list1, const labelUList &list2)
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:531
virtual const pointField & points() const =0
Return mesh points.
void setCapacity(const label len)
Alter the size of the underlying storage.
Definition: DynamicListI.H:303
static const unsigned edgesPerPoint_
Estimated number of edges per point.
List< labelList > labelListList
List of labelList.
Definition: labelList.H:38
bool contains(const T &val) const
True if the value is contained in the list.
Definition: UListI.H:300
Various functions to operate on Lists.
label capacity() const noexcept
Size of the underlying storage.
Definition: DynamicList.H:225
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:421
label fcIndex(const label i) const noexcept
The forward circular index. The next index in the list which returns to the first at the end of the l...
Definition: UListI.H:97
List< face > faceList
List of faces.
Definition: faceListFwd.H:39
static label getEdge(List< DynamicList< label >> &pe, DynamicList< edge > &es, const label pointi, const label nextPointi)
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:316
const cellShapeList & cells
void clear()
Clear the list, i.e. set size to zero.
Definition: ListI.H:137
T & emplace_back(Args &&... args)
Construct an element at the end of the list, return reference to the new list element.
Definition: DynamicListI.H:538
void sort(UList< T > &list)
Sort the list.
Definition: UList.C:296
errorManip< error > abort(error &err)
Definition: errorManip.H:139
iterator begin() noexcept
Return an iterator to begin traversing the UList.
Definition: UListI.H:391
int debug
Static debugging option.
void clear() noexcept
Clear the addressed list, i.e. set the size to zero.
Definition: DynamicListI.H:405
labelList f(nPoints)
void push_back(const T &val)
Copy append an element to the end of this list.
Definition: DynamicListI.H:555
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
Template functions to aid in the implementation of demand driven data.
void inplaceRenumber(const labelUList &oldToNew, IntListType &input)
Inplace renumber the values (not the indices) of a list.
virtual const faceList & faces() const =0
Return faces.
iterator end() noexcept
Return an iterator to end traversing the UList.
Definition: UListI.H:435
List< label > labelList
A List of labels.
Definition: List.H:62
void deleteDemandDrivenData(DataPtr &dataPtr)
prefixOSstream Pout
OSstream wrapped stdout (std::cout) with parallel prefix.
Namespace for OpenFOAM.