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