pointMeshTools.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) 2024 OpenCFD Ltd.
9 -------------------------------------------------------------------------------
10 License
11  This file is part of OpenFOAM.
12 
13  OpenFOAM is free software: you can redistribute it and/or modify it
14  under the terms of the GNU General Public License as published by
15  the Free Software Foundation, either version 3 of the License, or
16  (at your option) any later version.
17 
18  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
19  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
20  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
21  for more details.
22 
23  You should have received a copy of the GNU General Public License
24  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
25 
26 \*---------------------------------------------------------------------------*/
27 
28 #include "pointMeshTools.H"
29 #include "syncTools.H"
30 #include "PatchTools.H"
31 #include "dummyTransform.H"
32 #include "unitConversion.H"
33 //#include "OFstream.H"
34 
35 // * * * * * * * * * * * * * Static Member Functions * * * * * * * * * * * * //
36 
38 (
39  const polyMesh& mesh,
40 
41  const uindirectPrimitivePatch& allBoundary,
42 
43  // Per boundary face to zone
44  const labelUList& faceToZone,
45  // Number of zones
46  const label nZones,
47 
48  const scalar edgeFeatureAngle,
49  //const scalar pointFeatureAngle, //not yet done
50 
51  // Feature edge(points) internal to a zone
52  labelListList& zoneToMeshPoints,
53  List<pointConstraintList>& zoneToConstraints,
54 
55  // Feature edge(points) in between zones
56  labelList& twoZoneMeshPoints,
57  pointConstraintList& twoZoneConstraints,
58 
59  // Feature points on > 2 zones
60  labelList& multiZoneMeshPoints,
61  pointConstraintList& multiZoneConstraints
62 )
63 {
64  // Analyses edges on mesh faces and splits them up according to topology
65  // and geometry:
66  // - edges in between faces on same zone but making a feature angle
67  // - edges in between faces on two different zones
68  // - points on faces on > 2 zones
69 
70 
71  const globalMeshData& globalData = mesh.globalData();
72  const indirectPrimitivePatch& cpp = globalData.coupledPatch();
73  const mapDistribute& map = globalData.globalEdgeSlavesMap();
74  const auto& mp = allBoundary.meshPoints();
75 
76  const vector nullVector(vector::uniform(0));
77  const auto assignNonNull = [&](vector& x, const vector& y)
78  {
79  if (x == nullVector && y != nullVector)
80  {
81  x = y;
82  }
83  };
84 
85  // Calculate parallel-consistent point normals (as unweighted average
86  // of faceNormals). Note: only valid on patch points, not on mesh points
87  // that are coupled to these.
88  const pointField pointNormals
89  (
91  (
92  mesh,
93  allBoundary
94  )
95  );
96 
97 
98  // Find correspondence between allBoundary and coupled edges
99  labelList allEdges;
100  labelList coupledEdges;
101  bitSet sameEdgeOrientation;
103  (
104  allBoundary,
105  cpp,
106  allEdges,
107  coupledEdges,
108  sameEdgeOrientation
109  );
110 
111 
112 
113  // To construct the patches we need to know per edge:
114  // - patch on either side (if topological feature edge)
115  // - faceNormal on either side (if feature angle)
116  // We need to know per point:
117  // - patches on all connected faces
118  // - faceNormals on all connected faces? And compare to average?
119  // or edge normals on all connected edges
120 
121 
122  typedef Tuple2<label, vector> PN;
123  const PN nullPN(-1, nullVector);
124 
125 
126 
127  // Point-based analysis
128  // ~~~~~~~~~~~~~~~~~~~~
129 
130  // Collect per (mesh)point the zones (1, 2 or >2). Note: per mesh to
131  // make it easier to sync. See edge-based code below where we explicitly
132  // have to transfer from patch-edge to mesh-point etc. Note sure which one
133  // fits better....
134  // State:
135  // (-1, -1) : initial state
136  // (-2, -2) : more than 2 zones
137  // (>=0, >=0) : zones from connected faces
138  labelPairList pointToZones(mesh.nPoints(), labelPair(-1, -1));
139  {
140  // Combine zones.
141  const auto combineZones = [&](labelPair& x, const labelPair& y)
142  {
143  if (x == labelPair(-2, -2))
144  {
145  // Already marked as having >2 zones
146  }
147  else if (y == labelPair(-2, -2))
148  {
149  x = y;
150  }
151  else
152  {
153  // Find first free slot
154  if (x[0] == -1)
155  {
156  if (y[0] != -1)
157  {
158  x[0] = y[0];
159  }
160  else
161  {
162  x[0] = y[1];
163  }
164  }
165  else if (x[1] == -1)
166  {
167  if (y[0] != -1 && y[0] != x[0])
168  {
169  x[1] = y[0];
170  }
171  else if (y[1] != -1 && y[1] != x[1])
172  {
173  x[1] = y[1];
174  }
175  }
176  else
177  {
178  // Both x slots filled. See if y adds a 3rd element
179  if (y[0] != -1 && y[0] != x[0] && y[0] != x[1])
180  {
181  x = labelPair(-2, -2);
182  }
183  else if (y[1] != -1 && y[1] != x[0] && y[1] != x[1])
184  {
185  x = labelPair(-2, -2);
186  }
187  }
188  }
189  };
190 
191 
192  forAll(allBoundary, facei)
193  {
194  const auto& f = allBoundary[facei];
195  const label zonei = faceToZone[facei];
196  for (const label pointi : f)
197  {
198  auto& pZones = pointToZones[pointi];
199 
200  if (pZones != labelPair(-2, -2) && !pZones.contains(zonei))
201  {
202  if (pZones.first() == -1)
203  {
204  pZones.first() = zonei;
205  }
206  else if (pZones.second() == -1)
207  {
208  pZones.second() = zonei;
209  }
210  else
211  {
212  // Mark as >2 zones
213  pZones = labelPair(-2, -2);
214  }
215  }
216  }
217  }
218 
220  (
221  mesh,
222  pointToZones,
223  combineZones,
224  labelPair(-1, -1),
226  );
227  }
228 
229 
230 
231 
232  // Edge-based analysis
233  // ~~~~~~~~~~~~~~~~~~~~
234 
235  // 1. Local analysis
236 
237  List<Pair<PN>> allEdgeToFaces
238  (
239  allBoundary.nEdges(),
240  Pair<PN>(nullPN, nullPN)
241  );
242  {
243  const auto& edgeFaces = allBoundary.edgeFaces();
244  const auto& faceNormals = allBoundary.faceNormals();
245 
246  forAll(edgeFaces, edgei)
247  {
248  const auto& eFaces = edgeFaces[edgei];
249  const vector& n0 = faceNormals[eFaces[0]];
250  const label zone0 = faceToZone[eFaces[0]];
251  if (eFaces.size() == 1)
252  {
253  allEdgeToFaces[edgei] = Pair<PN>(PN(zone0, n0), nullPN);
254  }
255  else
256  {
257  const vector& n1 = faceNormals[eFaces[1]];
258  const label zone1 = faceToZone[eFaces[1]];
259  allEdgeToFaces[edgei] = Pair<PN>
260  (
261  PN(zone0, n0),
262  PN(zone1, n1)
263  );
264  }
265  }
266  }
267 
268 
269  // 2. Sync across coupled patches
270 
271  {
272  // Combine pair of normals
273  const auto vectorPairMax = [&](Pair<PN>& x, const Pair<PN>& y)
274  {
275  if (x[0] == nullPN)
276  {
277  if (y[0] != nullPN)
278  {
279  x[0] = y[0];
280  }
281  else
282  {
283  x[0] = y[1];
284  }
285  }
286  else if (x[1] == nullPN)
287  {
288  if (y[0] != nullPN && y[0] != x[0])
289  {
290  x[1] = y[0];
291  }
292  else
293  {
294  x[1] = y[1];
295  }
296  }
297  };
298 
299  List<Pair<PN>> cppEdgeData
300  (
301  map.constructSize(),
302  Pair<PN>(nullPN, nullPN)
303  );
304  UIndirectList<Pair<PN>>(cppEdgeData, coupledEdges) =
305  UIndirectList<Pair<PN>>(allEdgeToFaces, allEdges);
306 
307  globalData.syncData
308  (
309  cppEdgeData,
310  globalData.globalEdgeSlaves(),
311  globalData.globalEdgeTransformedSlaves(),
312  map,
313  globalData.globalTransforms(),
314  vectorPairMax,
316  );
317 
318  UIndirectList<Pair<PN>>(allEdgeToFaces, allEdges) =
319  UIndirectList<Pair<PN>>(cppEdgeData, coupledEdges);
320  }
321 
322 
323  // Now we have all the per-patch edge information
324  // - do inter-patch edges
325  // - do feature-angle edges
326  // Store on mesh points
327 
328  const auto assignNonNullPN = [&](PN& x, const PN& y)
329  {
330  if (x.second() == nullVector && y.second() != nullVector)
331  {
332  x = y;
333  }
334  };
335 
336 
337  const scalar featEdgeCos = Foam::cos(degToRad(edgeFeatureAngle));
338 
339 
340  //OFstream os("allBoundary.obj");
341  //Pout<< "Dumping feature edges to " << os.name() << endl;
342  //OFstream interOs("interZoneBoundary.obj");
343  //Pout<< "Dumping inter-zone edges to " << os.name() << endl;
344 
345  // Storing the normal for points that are on inter-patch edges
346  vectorField patchEdgeNormal(mesh.nPoints(), nullVector);
347  // Storing the constraint for points that are on patch-internal feat edges
348  List<PN> featEdgeNormal(mesh.nPoints(), nullPN);
349 
350  // Use point-based sync
351  {
352  forAll(allEdgeToFaces, edgei)
353  {
354  const edge& e = allBoundary.edges()[edgei];
355  const label mp0 = mp[e[0]];
356  const label mp1 = mp[e[1]];
357 
358  const Pair<PN>& facesInfo = allEdgeToFaces[edgei];
359 
360  if (facesInfo[1] == nullPN)
361  {
362  // Real boundary edge. On single patch only so no need
363  // to put in separate point patch ... (? tbd)
364  }
365  else
366  {
367  const label zone0 = facesInfo[0].first();
368  const label zone1 = facesInfo[1].first();
369 
370  const point& p0 = allBoundary.points()[mp0];
371  const point& p1 = allBoundary.points()[mp1];
372  vector eVec(p1-p0);
373  eVec.normalise();
374 
375  if (zone0 != zone1)
376  {
377  // Inter-patch. TBD: check for feature angle as well?
378 
379  //patchEdgeNormal[mp0] = pointNormals[e[0]];
380  //patchEdgeNormal[mp1] = pointNormals[e[1]];
381 
382  patchEdgeNormal[mp0] = eVec;
383  patchEdgeNormal[mp1] = eVec;
384  }
385  else
386  {
387  // Same patch - check for feature angle
388 
389  const vector& n0 = facesInfo[0].second();
390  const vector& n1 = facesInfo[1].second();
391 
392  if ((n0 & n1) < featEdgeCos)
393  {
394  //Pout<< "** feature edge:" << edgei
395  // << " points:" << allBoundary.points()[mp0]
396  // << allBoundary.points()[mp1]
397  // << nl
398  // << " faceNormal0:" << n0
399  // << " faceNormal1:" << n1 << nl
400  // << " zone0:" << zone0
401  // << " zone1:" << zone1 << nl
402  // << " pointNormals0:" << pointNormals[e[0]]
403  // << " pointNormals1:" << pointNormals[e[1]]
404  // << nl
405  // << endl;
406 
407  if (patchEdgeNormal[mp0] == nullVector)
408  {
409  //featEdgeNormal[mp0] = PN
410  //(
411  // zone0, // zone
412  // pointNormals[e[0]]
413  //);
414  featEdgeNormal[mp0].first() = zone0;
415  // Assign edge direction. TBD: average & parallel
416  featEdgeNormal[mp0].second() = eVec;
417  }
418  if (patchEdgeNormal[mp1] == nullVector)
419  {
420  //featEdgeNormal[mp1] = PN
421  //(
422  // zone1, // zone
423  // pointNormals[e[1]]
424  //);
425  featEdgeNormal[mp1].first() = zone1;
426  // Assign edge direction. TBD: average & parallel
427  featEdgeNormal[mp1].second() = eVec;
428  }
429  }
430  }
431  }
432  }
433 
435  (
436  mesh,
437  patchEdgeNormal,
438  assignNonNull,
439  nullVector
440  );
442  (
443  mesh,
444  featEdgeNormal,
445  assignNonNullPN,
446  nullPN,
448  );
449  }
450 
451  // Make sure that inter-patch points are not also in feature-edge
452  // points. Note: not absolutely necessary since all inter-patch points
453  // will also be in the 'normal' facePointPatches.
454 
455  DynamicList<label> dynMultiZoneMeshPoints(allBoundary.nPoints());
456  DynamicList<pointConstraint> dynMultiZoneConstraints(allBoundary.nPoints());
457  forAll(pointToZones, pointi)
458  {
459  if (pointToZones[pointi] == labelPair(-2, -2))
460  {
461  dynMultiZoneMeshPoints.append(pointi);
462  // Note: pointConstraint just a slip constraint for now
463  dynMultiZoneConstraints.append
464  (
466  (
467  3, // feature point
468  Zero //meshPointNormals[pointi]
469  )
470  );
471  // Unmark as feature angle point
472  patchEdgeNormal[pointi] = nullVector;
473  featEdgeNormal[pointi] = nullPN;
474  }
475  }
476  multiZoneMeshPoints = std::move(dynMultiZoneMeshPoints);
477  multiZoneConstraints = std::move(dynMultiZoneConstraints);
478 
479 
480  DynamicList<label> dynTwoZoneMeshPoints(allBoundary.nPoints());
481  DynamicList<pointConstraint> dynTwoZoneConstraints(allBoundary.nPoints());
482  forAll(patchEdgeNormal, pointi)
483  {
484  if (patchEdgeNormal[pointi] != nullVector)
485  {
486  dynTwoZoneMeshPoints.append(pointi);
487  // Note: pointConstraint just a slip constraint for now
488  dynTwoZoneConstraints.append
489  (
491  (
492  2, // feature edge
493  patchEdgeNormal[pointi]
494  )
495  );
496 
497  // Unmark as feature angle point
498  featEdgeNormal[pointi] = nullPN;
499  }
500  }
501  twoZoneMeshPoints = std::move(dynTwoZoneMeshPoints);
502  twoZoneConstraints = std::move(dynTwoZoneConstraints);
503 
504 
505  // Sort featEdgeNormal according to zone
506  zoneToMeshPoints.resize_nocopy(nZones);
507  zoneToConstraints.resize_nocopy(nZones);
508  {
509  labelList sizes(nZones, 0);
510  forAll(featEdgeNormal, pointi)
511  {
512  const auto& pInfo = featEdgeNormal[pointi];
513  if (pInfo != nullPN)
514  {
515  const label zonei = pInfo.first();
516  sizes[zonei]++;
517  }
518  }
519  forAll(zoneToMeshPoints, zonei)
520  {
521  zoneToMeshPoints[zonei].setSize(sizes[zonei]);
522  zoneToConstraints[zonei].setSize(sizes[zonei]);
523  }
524  sizes = 0;
525  forAll(featEdgeNormal, pointi)
526  {
527  const auto& pInfo = featEdgeNormal[pointi];
528  if (pInfo != nullPN)
529  {
530  const label zonei = pInfo.first();
531  const label index = sizes[zonei]++;
532 
533  zoneToMeshPoints[zonei][index] = pointi;
534  zoneToConstraints[zonei][index] =
535  pointConstraint(2, pInfo.second()); // constrained to slide
536  }
537  }
538  }
539 }
540 
541 
542 // ************************************************************************* //
const T & first() const noexcept
Access the first element.
Definition: Pair.H:137
label nPoints() const
Number of points supporting patch faces.
static void featurePointsEdges(const polyMesh &mesh, const uindirectPrimitivePatch &boundary, const labelUList &faceToZone, const label nZones, const scalar edgeFeatureAngle, labelListList &zoneToMeshPoints, List< pointConstraintList > &zoneToConstraints, labelList &twoZoneMeshPoints, pointConstraintList &twoZoneConstraints, labelList &multiZoneMeshPoints, pointConstraintList &multiZoneConstraints)
Analyse patch for feature edges, feature points. Handles points not being on a face of patch but coup...
const mapDistribute & globalEdgeSlavesMap() const
Various mesh related information for a parallel run. Upon construction, constructs all info using par...
Unit conversion functions.
static void matchEdges(const PrimitivePatch< FaceList1, PointField1 > &p1, const PrimitivePatch< FaceList2, PointField2 > &p2, labelList &p1EdgeLabels, labelList &p2EdgeLabels, bitSet &sameOrientation)
Find corresponding edges on patches sharing the same points.
label constructSize() const noexcept
Constructed data size.
void resize_nocopy(const label len)
Adjust allocated size of list without necessarily.
Definition: ListI.H:168
const labelListList & globalEdgeSlaves() const
const labelList & meshPoints() const
Return labelList of mesh points in patch.
#define forAll(list, i)
Loop across all elements in list.
Definition: stdFoam.H:286
static void syncData(List< Type > &elems, const labelListList &slaves, const labelListList &transformedSlaves, const mapDistribute &slavesMap, const globalIndexAndTransform &, const CombineOp &cop, const TransformOp &top)
Helper: synchronise data with transforms.
Dummy transform to be used with syncTools.
const Field< point_type > & faceNormals() const
Return face unit normals for patch.
scalar y
A list of faces which address into the list of points.
const dimensionedScalar e
Elementary charge.
Definition: createFields.H:11
void setSize(const label n)
Alias for resize()
Definition: List.H:325
dynamicFvMesh & mesh
dimensionedScalar cos(const dimensionedScalar &ds)
const globalIndexAndTransform & globalTransforms() const
Global transforms numbering.
An edge is a list of two vertex labels. This can correspond to a directed graph edge or an edge on a ...
Definition: edge.H:59
const labelListList & edgeFaces() const
Return edge-face addressing.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
const Field< point_type > & points() const noexcept
Return reference to global points.
Accumulates point constraints through successive applications of the applyConstraint function...
static void syncPointList(const polyMesh &mesh, List< T > &pointValues, const CombineOp &cop, const T &nullValue, const TransformOp &top)
Synchronize values on all mesh points.
label nEdges() const
Number of edges in patch.
A Vector of values with scalar precision, where scalar is float/double depending on the compilation f...
Pair< label > labelPair
A pair of labels.
Definition: Pair.H:51
labelList f(nPoints)
const indirectPrimitivePatch & coupledPatch() const
Return patch of all coupled faces.
Class containing processor-to-processor mapping information.
A bitSet stores bits (elements with only two states) in packed internal format and supports a variety...
Definition: bitSet.H:59
A List with indirect addressing. Like IndirectList but does not store addressing. ...
Definition: faMatrix.H:52
Mesh consisting of general polyhedral cells.
Definition: polyMesh.H:75
const T & second() const noexcept
Access the second element.
Definition: Pair.H:147
static tmp< pointField > pointNormals(const polyMesh &, const PrimitivePatch< FaceList, PointField > &, const bitSet &flipMap=bitSet::null())
Return parallel consistent point normals for patches using mesh points.
constexpr scalar degToRad(const scalar deg) noexcept
Conversion from degrees to radians.
const volScalarField & p0
Definition: EEqn.H:36
const dimensionedScalar mp
Proton mass.
IOporosityModelList pZones(mesh)
const labelListList & globalEdgeTransformedSlaves() const
static constexpr const zero Zero
Global zero (0)
Definition: zero.H:127