modifyMesh.C
Go to the documentation of this file.
1 /*---------------------------------------------------------------------------*\
2  ========= |
3  \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
4  \\ / O peration | Website: https://openfoam.org
5  \\ / A nd | Copyright (C) 2011-2023 OpenFOAM Foundation
6  \\/ M anipulation |
7 -------------------------------------------------------------------------------
8 License
9  This file is part of OpenFOAM.
10 
11  OpenFOAM is free software: you can redistribute it and/or modify it
12  under the terms of the GNU General Public License as published by
13  the Free Software Foundation, either version 3 of the License, or
14  (at your option) any later version.
15 
16  OpenFOAM is distributed in the hope that it will be useful, but WITHOUT
17  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
18  FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
19  for more details.
20 
21  You should have received a copy of the GNU General Public License
22  along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>.
23 
24 Application
25  modifyMesh
26 
27 Description
28  Manipulates mesh elements.
29 
30  Actions are:
31  (boundary)points:
32  - move
33 
34  (boundary)edges:
35  - split and move introduced point
36 
37  (boundary)faces:
38  - split(triangulate) and move introduced point
39 
40  edges:
41  - collapse
42 
43  cells:
44  - split into polygonal base pyramids around newly introduced mid
45  point
46 
47  Is a bit of a loose collection of mesh change drivers.
48 
49 \*---------------------------------------------------------------------------*/
50 
51 #include "argList.H"
52 #include "Time.H"
53 #include "polyMesh.H"
54 #include "polyTopoChange.H"
55 #include "polyTopoChangeMap.H"
56 #include "boundaryCutter.H"
57 #include "cellSplitter.H"
58 #include "edgeCollapser.H"
59 #include "meshTools.H"
60 #include "Pair.H"
61 #include "globalIndex.H"
62 #include "systemDict.H"
63 
64 using namespace Foam;
65 
66 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
67 
68 // Locate point on patch. Returns (mesh) point label.
69 label findPoint(const primitivePatch& pp, const point& nearPoint)
70 {
71  const pointField& points = pp.points();
72  const labelList& meshPoints = pp.meshPoints();
73 
74  // Find nearest and next nearest
75  scalar minDistSqr = great;
76  label minI = -1;
77 
78  scalar almostMinDistSqr = great;
79  label almostMinI = -1;
80 
81  forAll(meshPoints, i)
82  {
83  label pointi = meshPoints[i];
84 
85  scalar distSqr = magSqr(nearPoint - points[pointi]);
86 
87  if (distSqr < minDistSqr)
88  {
89  almostMinDistSqr = minDistSqr;
90  almostMinI = minI;
91 
92  minDistSqr = distSqr;
93  minI = pointi;
94  }
95  else if (distSqr < almostMinDistSqr)
96  {
97  almostMinDistSqr = distSqr;
98  almostMinI = pointi;
99  }
100  }
101 
102 
103  // Decide if nearPoint unique enough.
104  Info<< "Found to point " << nearPoint << nl
105  << " nearest point : " << minI
106  << " distance " << Foam::sqrt(minDistSqr)
107  << " at " << points[minI] << nl
108  << " next nearest point : " << almostMinI
109  << " distance " << Foam::sqrt(almostMinDistSqr)
110  << " at " << points[almostMinI] << endl;
111 
112  if (almostMinDistSqr < 4*minDistSqr)
113  {
114  Info<< "Next nearest too close to nearest. Aborting" << endl;
115 
116  return -1;
117  }
118  else
119  {
120  return minI;
121  }
122 }
123 
124 
125 // Locate edge on patch. Return mesh edge label.
127 (
128  const primitiveMesh& mesh,
129  const primitivePatch& pp,
130  const point& nearPoint
131 )
132 {
133  const pointField& localPoints = pp.localPoints();
134  const pointField& points = pp.points();
135  const labelList& meshPoints = pp.meshPoints();
136  const edgeList& edges = pp.edges();
137 
138  // Find nearest and next nearest
139  scalar minDist = great;
140  label minI = -1;
141 
142  scalar almostMinDist = great;
143  label almostMinI = -1;
144 
145  forAll(edges, edgeI)
146  {
147  const edge& e = edges[edgeI];
148 
149  pointHit pHit(e.line(localPoints).nearestDist(nearPoint));
150 
151  if (pHit.hit())
152  {
153  if (pHit.distance() < minDist)
154  {
155  almostMinDist = minDist;
156  almostMinI = minI;
157 
158  minDist = pHit.distance();
159  minI = meshTools::findEdge
160  (
161  mesh,
162  meshPoints[e[0]],
163  meshPoints[e[1]]
164  );
165  }
166  else if (pHit.distance() < almostMinDist)
167  {
168  almostMinDist = pHit.distance();
169  almostMinI = meshTools::findEdge
170  (
171  mesh,
172  meshPoints[e[0]],
173  meshPoints[e[1]]
174  );
175  }
176  }
177  }
178 
179  if (minI == -1)
180  {
181  Info<< "Did not find edge close to point " << nearPoint << endl;
182 
183  return -1;
184  }
185 
186 
187  // Decide if nearPoint unique enough.
188  Info<< "Found to point " << nearPoint << nl
189  << " nearest edge : " << minI
190  << " distance " << minDist << " endpoints "
191  << mesh.edges()[minI].line(points) << nl
192  << " next nearest edge : " << almostMinI
193  << " distance " << almostMinDist << " endpoints "
194  << mesh.edges()[almostMinI].line(points) << nl
195  << endl;
196 
197  if (almostMinDist < 2*minDist)
198  {
199  Info<< "Next nearest too close to nearest. Aborting" << endl;
200 
201  return -1;
202  }
203  else
204  {
205  return minI;
206  }
207 }
208 
209 
210 // Find face on patch. Return mesh face label.
211 label findFace
212 (
213  const primitiveMesh& mesh,
214  const primitivePatch& pp,
215  const point& nearPoint
216 )
217 {
218  const pointField& points = pp.points();
219 
220  // Find nearest and next nearest
221  scalar minDist = great;
222  label minI = -1;
223 
224  scalar almostMinDist = great;
225  label almostMinI = -1;
226 
227  forAll(pp, patchFacei)
228  {
229  pointHit pHit(pp[patchFacei].nearestPoint(nearPoint, points));
230 
231  if (pHit.hit())
232  {
233  if (pHit.distance() < minDist)
234  {
235  almostMinDist = minDist;
236  almostMinI = minI;
237 
238  minDist = pHit.distance();
239  minI = patchFacei + mesh.nInternalFaces();
240  }
241  else if (pHit.distance() < almostMinDist)
242  {
243  almostMinDist = pHit.distance();
244  almostMinI = patchFacei + mesh.nInternalFaces();
245  }
246  }
247  }
248 
249  if (minI == -1)
250  {
251  Info<< "Did not find face close to point " << nearPoint << endl;
252 
253  return -1;
254  }
255 
256 
257  // Decide if nearPoint unique enough.
258  Info<< "Found to point " << nearPoint << nl
259  << " nearest face : " << minI
260  << " distance " << minDist
261  << " to face centre " << mesh.faceCentres()[minI] << nl
262  << " next nearest face : " << almostMinI
263  << " distance " << almostMinDist
264  << " to face centre " << mesh.faceCentres()[almostMinI] << nl
265  << endl;
266 
267  if (almostMinDist < 2*minDist)
268  {
269  Info<< "Next nearest too close to nearest. Aborting" << endl;
270 
271  return -1;
272  }
273  else
274  {
275  return minI;
276  }
277 }
278 
279 
280 // Find cell with cell centre close to given point.
281 label findCell(const primitiveMesh& mesh, const point& nearPoint)
282 {
283  label celli = mesh.findCell(nearPoint);
284 
285  if (celli != -1)
286  {
287  scalar distToCcSqr = magSqr(nearPoint - mesh.cellCentres()[celli]);
288 
289  const labelList& cPoints = mesh.cellPoints()[celli];
290 
291  label minI = -1;
292  scalar minDistSqr = great;
293 
294  forAll(cPoints, i)
295  {
296  label pointi = cPoints[i];
297 
298  scalar distSqr = magSqr(nearPoint - mesh.points()[pointi]);
299 
300  if (distSqr < minDistSqr)
301  {
302  minDistSqr = distSqr;
303  minI = pointi;
304  }
305  }
306 
307  // Decide if nearPoint unique enough.
308  Info<< "Found to point " << nearPoint << nl
309  << " nearest cell : " << celli
310  << " distance " << Foam::sqrt(distToCcSqr)
311  << " to cell centre " << mesh.cellCentres()[celli] << nl
312  << " nearest mesh point : " << minI
313  << " distance " << Foam::sqrt(minDistSqr)
314  << " to " << mesh.points()[minI] << nl
315  << endl;
316 
317  if (minDistSqr < 4*distToCcSqr)
318  {
319  Info<< "Mesh point too close to nearest cell centre. Aborting"
320  << endl;
321 
322  celli = -1;
323  }
324  }
325 
326  return celli;
327 }
328 
329 
330 int main(int argc, char *argv[])
331 {
332  #include "addOverwriteOption.H"
333 
334  #include "setRootCase.H"
336  #include "createPolyMesh.H"
337  const word oldInstance = mesh.pointsInstance();
338 
339  const bool overwrite = args.optionFound("overwrite");
340 
341  const dictionary dict(systemDict("modifyMeshDict", args, mesh));
342 
343  // Read all from the dictionary.
344  List<Pair<point>> pointsToMove(dict.lookup("pointsToMove"));
345  List<Pair<point>> edgesToSplit(dict.lookup("edgesToSplit"));
346  List<Pair<point>> facesToTriangulate
347  (
348  dict.lookup("facesToTriangulate")
349  );
350 
351  bool cutBoundary =
352  (
353  pointsToMove.size()
354  || edgesToSplit.size()
355  || facesToTriangulate.size()
356  );
357 
358  List<Pair<point>> edgesToCollapse(dict.lookup("edgesToCollapse"));
359 
360  bool collapseEdge = edgesToCollapse.size();
361 
362  List<Pair<point>> cellsToPyramidise(dict.lookup("cellsToSplit"));
363 
364  bool cellsToSplit = cellsToPyramidise.size();
365 
366  // List<Tuple2<pointField,point>>
367  // cellsToCreate(dict.lookup("cellsToCreate"));
368 
369  Info<< "Read from " << dict.name() << nl
370  << " Boundary cutting module:" << nl
371  << " points to move :" << pointsToMove.size() << nl
372  << " edges to split :" << edgesToSplit.size() << nl
373  << " faces to triangulate:" << facesToTriangulate.size() << nl
374  << " Cell splitting module:" << nl
375  << " cells to split :" << cellsToPyramidise.size() << nl
376  << " Edge collapsing module:" << nl
377  << " edges to collapse :" << edgesToCollapse.size() << nl
378  //<< " cells to create :" << cellsToCreate.size() << nl
379  << endl;
380 
381  if
382  (
383  (cutBoundary && collapseEdge)
384  || (cutBoundary && cellsToSplit)
385  || (collapseEdge && cellsToSplit)
386  )
387  {
389  << "Used more than one mesh modifying module "
390  << "(boundary cutting, cell splitting, edge collapsing)" << nl
391  << "Please do them in separate passes." << exit(FatalError);
392  }
393 
394 
395 
396  // Get calculating engine for all of outside
397  const SubList<face> outsideFaces
398  (
399  mesh.faces(),
400  mesh.nFaces() - mesh.nInternalFaces(),
401  mesh.nInternalFaces()
402  );
403 
404  primitivePatch allBoundary(outsideFaces, mesh.points());
405 
406 
407  // Look up mesh labels and convert to input for boundaryCutter.
408 
409  bool validInputs = true;
410 
411 
412  Info<< nl << "Looking up points to move ..." << nl << endl;
413  Map<point> pointToPos(pointsToMove.size());
414  forAll(pointsToMove, i)
415  {
416  const Pair<point>& pts = pointsToMove[i];
417 
418  label pointi = findPoint(allBoundary, pts.first());
419 
420  if (pointi == -1 || !pointToPos.insert(pointi, pts.second()))
421  {
422  Info<< "Could not insert mesh point " << pointi
423  << " for input point " << pts.first() << nl
424  << "Perhaps the point is already marked for moving?" << endl;
425  validInputs = false;
426  }
427  }
428 
429 
430  Info<< nl << "Looking up edges to split ..." << nl << endl;
431  Map<List<point>> edgeToCuts(edgesToSplit.size());
432  forAll(edgesToSplit, i)
433  {
434  const Pair<point>& pts = edgesToSplit[i];
435 
436  label edgeI = findEdge(mesh, allBoundary, pts.first());
437 
438  if
439  (
440  edgeI == -1
441  || !edgeToCuts.insert(edgeI, List<point>(1, pts.second()))
442  )
443  {
444  Info<< "Could not insert mesh edge " << edgeI
445  << " for input point " << pts.first() << nl
446  << "Perhaps the edge is already marked for cutting?" << endl;
447 
448  validInputs = false;
449  }
450  }
451 
452 
453  Info<< nl << "Looking up faces to triangulate ..." << nl << endl;
454  Map<point> faceToDecompose(facesToTriangulate.size());
455  forAll(facesToTriangulate, i)
456  {
457  const Pair<point>& pts = facesToTriangulate[i];
458 
459  label facei = findFace(mesh, allBoundary, pts.first());
460 
461  if (facei == -1 || !faceToDecompose.insert(facei, pts.second()))
462  {
463  Info<< "Could not insert mesh face " << facei
464  << " for input point " << pts.first() << nl
465  << "Perhaps the face is already marked for splitting?" << endl;
466 
467  validInputs = false;
468  }
469  }
470 
471 
472 
473  Info<< nl << "Looking up cells to convert to pyramids around"
474  << " cell centre ..." << nl << endl;
475  Map<point> cellToPyrCentre(cellsToPyramidise.size());
476  forAll(cellsToPyramidise, i)
477  {
478  const Pair<point>& pts = cellsToPyramidise[i];
479 
480  label celli = findCell(mesh, pts.first());
481 
482  if (celli == -1 || !cellToPyrCentre.insert(celli, pts.second()))
483  {
484  Info<< "Could not insert mesh cell " << celli
485  << " for input point " << pts.first() << nl
486  << "Perhaps the cell is already marked for splitting?" << endl;
487 
488  validInputs = false;
489  }
490  }
491 
492 
493  Info<< nl << "Looking up edges to collapse ..." << nl << endl;
494  Map<point> edgeToPos(edgesToCollapse.size());
495  forAll(edgesToCollapse, i)
496  {
497  const Pair<point>& pts = edgesToCollapse[i];
498 
499  label edgeI = findEdge(mesh, allBoundary, pts.first());
500 
501  if (edgeI == -1 || !edgeToPos.insert(edgeI, pts.second()))
502  {
503  Info<< "Could not insert mesh edge " << edgeI
504  << " for input point " << pts.first() << nl
505  << "Perhaps the edge is already marked for collapsing?" << endl;
506 
507  validInputs = false;
508  }
509  }
510 
511 
512 
513  if (!validInputs)
514  {
515  Info<< nl << "There was a problem in one of the inputs in the"
516  << " dictionary. Not modifying mesh." << endl;
517  }
518  else if (cellToPyrCentre.size())
519  {
520  Info<< nl << "All input cells located. Modifying mesh." << endl;
521 
522  // Mesh change engine
523  cellSplitter cutter(mesh);
524 
525  // Topo change container
526  polyTopoChange meshMod(mesh);
527 
528  // Insert commands into meshMod
529  cutter.setRefinement(cellToPyrCentre, meshMod);
530 
531  // Do changes
532  autoPtr<polyTopoChangeMap> map = meshMod.changeMesh(mesh, false);
533 
534  if (map().hasMotionPoints())
535  {
536  mesh.setPoints(map().preMotionPoints());
537  }
538 
539  cutter.topoChange(map());
540 
541  if (!overwrite)
542  {
543  runTime++;
544  }
545  else
546  {
547  mesh.setInstance(oldInstance);
548  }
549 
550  // Write resulting mesh
551  Info<< "Writing modified mesh to time " << runTime.name() << endl;
552  mesh.write();
553  }
554  else if (edgeToPos.size())
555  {
556  Info<< nl << "All input edges located. Modifying mesh." << endl;
557 
558  // Mesh change engine
559  edgeCollapser cutter(mesh);
560 
561  const edgeList& edges = mesh.edges();
562  const pointField& points = mesh.points();
563 
564  pointField newPoints(points);
565 
567  Map<point> collapsePointToLocation(mesh.nPoints());
568 
569  // Get new positions and construct collapse network
570  forAllConstIter(Map<point>, edgeToPos, iter)
571  {
572  label edgeI = iter.key();
573  const edge& e = edges[edgeI];
574 
575  collapseEdge[edgeI] = true;
576  collapsePointToLocation.set(e[1], points[e[0]]);
577 
578  newPoints[e[0]] = iter();
579  }
580 
581  // Move master point to destination.
582  mesh.setPoints(newPoints);
583 
584  List<pointEdgeCollapse> allPointInfo;
585  const globalIndex globalPoints(mesh.nPoints());
586  labelList pointPriority(mesh.nPoints(), 0);
587 
588  cutter.consistentCollapse
589  (
590  globalPoints,
591  pointPriority,
592  collapsePointToLocation,
593  collapseEdge,
594  allPointInfo
595  );
596 
597  // Topo change container
598  polyTopoChange meshMod(mesh);
599 
600  // Insert
601  cutter.setRefinement(allPointInfo, meshMod);
602 
603  // Do changes
604  autoPtr<polyTopoChangeMap> map = meshMod.changeMesh(mesh, false);
605 
606  if (map().hasMotionPoints())
607  {
608  mesh.setPoints(map().preMotionPoints());
609  }
610 
611  // Not implemented yet:
612  // cutter.topoChange(map());
613 
614 
615  if (!overwrite)
616  {
617  runTime++;
618  }
619  else
620  {
621  mesh.setInstance(oldInstance);
622  }
623 
624  // Write resulting mesh
625  Info<< "Writing modified mesh to time " << runTime.name() << endl;
626  mesh.write();
627  }
628  else
629  {
630  Info<< nl << "All input points located. Modifying mesh." << endl;
631 
632  // Mesh change engine
633  boundaryCutter cutter(mesh);
634 
635  // Topo change container
636  polyTopoChange meshMod(mesh);
637 
638  // Insert commands into meshMod
639  cutter.setRefinement
640  (
641  pointToPos,
642  edgeToCuts,
643  Map<labelPair>(0), // Faces to split diagonally
644  faceToDecompose, // Faces to triangulate
645  meshMod
646  );
647 
648  // Do changes
649  autoPtr<polyTopoChangeMap> map = meshMod.changeMesh(mesh, false);
650 
651  if (map().hasMotionPoints())
652  {
653  mesh.setPoints(map().preMotionPoints());
654  }
655 
656  cutter.topoChange(map());
657 
658  if (!overwrite)
659  {
660  runTime++;
661  }
662  else
663  {
664  mesh.setInstance(oldInstance);
665  }
666 
667  // Write resulting mesh
668  Info<< "Writing modified mesh to time " << runTime.name() << endl;
669  mesh.write();
670  }
671 
672 
673  Info<< "\nEnd\n" << endl;
674  return 0;
675 }
676 
677 
678 // ************************************************************************* //
#define forAll(list, i)
Loop across all elements in list.
Definition: UList.H:434
#define forAllConstIter(Container, container, iter)
Iterate across all elements in the container object of type.
Definition: UList.H:477
A HashTable to objects of type <T> with a label key.
Definition: Map.H:52
A bit-packed bool list.
An ordered pair of two objects of type <T> with first() and second() elements.
Definition: Pair.H:65
const Type & second() const
Return second.
Definition: Pair.H:110
const Type & first() const
Return first.
Definition: Pair.H:98
A list of faces which address into the list of points.
const edgeList & edges() const
Return list of edges, address into LOCAL point list.
const labelList & meshPoints() const
Return labelList of mesh points in patch. They are constructed.
const Field< PointType > & points() const
Return reference to global points.
const Field< PointType > & localPoints() const
Return pointField of points in patch.
A List obtained as a section of another List.
Definition: SubList.H:56
bool optionFound(const word &opt) const
Return true if the named option is found.
Definition: argListI.H:114
An auto-pointer similar to the STL auto_ptr but with automatic casting to a reference to the type and...
Definition: autoPtr.H:51
Does modifications to boundary faces.
Does pyramidal decomposition of selected cells. So all faces will become base of pyramid with as top ...
Definition: cellSplitter.H:58
const fileName & name() const
Return the dictionary name.
Definition: dictionary.H:109
A list of keyword definitions, which are a keyword followed by any number of values (e....
Definition: dictionary.H:160
ITstream & lookup(const word &, bool recursive=false, bool patternMatch=true) const
Find and return an entry data stream.
Definition: dictionary.C:860
Does polyTopoChanges to remove edges. Can remove faces due to edge collapse but can not remove cells ...
Definition: edgeCollapser.H:67
An edge is a list of two point labels. The functionality it provides supports the discretisation on a...
Definition: edge.H:61
Calculates a unique integer (label so might not have enough room - 2G max) for processor + local inde...
Definition: globalIndex.H:64
Calculates points shared by more than two processor patches or cyclic patches.
Definition: globalPoints.H:101
Direct mesh changes based on v1.3 polyTopoChange syntax.
Cell-face mesh analysis engine.
Definition: primitiveMesh.H:75
label nEdges() const
label nPoints() const
const edgeList & edges() const
Return mesh edges. Uses calcEdges.
const vectorField & faceCentres() const
virtual const faceList & faces() const =0
Return faces.
const vectorField & cellCentres() const
label findCell(const point &location) const
Find cell enclosing this location (-1 if not in mesh)
const labelListList & cellPoints() const
label nInternalFaces() const
label nFaces() const
virtual const pointField & points() const =0
Return mesh points.
A class for handling words, derived from string.
Definition: word.H:62
label collapseEdge(triSurface &surf, const scalar minLen)
Keep collapsing all edges < minLen.
#define FatalErrorInFunction
Report an error message using Foam::FatalError.
Definition: error.H:306
int main(int argc, char *argv[])
Definition: financialFoam.C:44
const pointField & points
label findEdge(const edgeList &edges, const labelList &candidates, const label v0, const label v1)
Return edge among candidates that uses the two vertices.
Definition: meshTools.C:337
Namespace for OpenFOAM.
errorManipArg< error, int > exit(error &err, const int errNo=1)
Definition: errorManip.H:124
const doubleScalar e
Definition: doubleScalar.H:105
scalar minDist(const List< pointIndexHit > &hitList)
intWM_LABEL_SIZE_t label
A label is an int32_t or int64_t as specified by the pre-processor macro WM_LABEL_SIZE.
Definition: label.H:59
IOdictionary systemDict(const word &dictName, const argList &args, const objectRegistry &ob, const word &regionName=polyMesh::defaultRegion)
Definition: systemDict.C:92
Ostream & endl(Ostream &os)
Add newline and flush stream.
Definition: Ostream.H:251
messageStream Info
dimensionedScalar sqrt(const dimensionedScalar &ds)
error FatalError
static const char nl
Definition: Ostream.H:260
dimensioned< scalar > magSqr(const dimensioned< Type > &)
dictionary dict
Foam::argList args(argc, argv)