1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 1991-2009 OpenCFD Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of OpenFOAM.
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 the
13 Free Software Foundation; either version 2 of the License, or (at your
14 option) any later version.
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
21 You should have received a copy of the GNU General Public License
22 along with OpenFOAM; if not, write to the Free Software Foundation,
23 Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
26 Utility to split cells with flat faces.
28 Uses a geometric cut with a plane dividing the edge angle into two so
29 might produce funny cells. For hexes it will use by default a cut from
30 edge onto opposite edge (i.e. purely topological).
33 - split cells from cellSet only
34 - use geometric cut for hexes as well
36 The angle is the angle between two faces sharing an edge as seen from
37 inside each cell. So a cube will have all angles 90. If you want
38 to split cells with cell centre outside use e.g. angle 200
40 \*---------------------------------------------------------------------------*/
44 #include "polyTopoChange.H"
45 #include "polyTopoChanger.H"
46 #include "mapPolyMesh.H"
50 #include "cellModeller.H"
51 #include "meshCutter.H"
52 #include "mathematicalConstants.H"
53 #include "geomCellLooper.H"
55 #include "edgeVertex.H"
56 #include "meshTools.H"
61 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
64 labelList pack(const boolList& lst)
66 labelList packedLst(lst.size());
73 packedLst[packedI++] = i;
76 packedLst.setSize(packedI);
82 scalarField pack(const boolList& lst, const scalarField& elems)
84 scalarField packedElems(lst.size());
91 packedElems[packedI++] = elems[i];
94 packedElems.setSize(packedI);
100 // Given sin and cos of max angle between normals calculate whether f0 and f1
101 // on cellI make larger angle. Uses sinAngle only for quadrant detection.
104 const primitiveMesh& mesh,
105 const scalar cosAngle,
106 const scalar sinAngle,
109 const label f0, // face label
112 const vector& n0, // normal at f0
116 const labelList& own = mesh.faceOwner();
118 bool sameFaceOrder = !((own[f0] == cellI) ^ (own[f1] == cellI));
120 // Get cos between faceArea vectors. Correct so flat angle (180 degrees)
122 scalar normalCosAngle = n0 & n1;
126 normalCosAngle = -normalCosAngle;
130 // Get cos between faceCentre and normal vector to determine in
131 // which quadrant angle is. (Is correct for unwarped faces only!)
132 // Correct for non-outwards pointing normal.
133 vector c1c0(mesh.faceCentres()[f1] - mesh.faceCentres()[f0]);
134 c1c0 /= mag(c1c0) + VSMALL;
136 scalar fcCosAngle = n0 & c1c0;
138 if (own[f0] != cellI)
140 fcCosAngle = -fcCosAngle;
145 // Looking for concave angles (quadrant 3 or 4)
148 // Angle is convex so smaller.
153 if (normalCosAngle < cosAngle)
165 // Looking for convex angles (quadrant 1 or 2)
173 // Convex. Check cos of normal vectors.
174 if (normalCosAngle > cosAngle)
187 // Split hex (and hex only) along edgeI creating two prisms
190 const polyMesh& mesh,
194 DynamicList<label>& cutCells,
195 DynamicList<labelList>& cellLoops,
196 DynamicList<scalarField>& cellEdgeWeights
199 // cut handling functions
202 const edgeList& edges = mesh.edges();
203 const faceList& faces = mesh.faces();
205 const edge& e = edges[edgeI];
207 // Get faces on the side, i.e. faces not using edge but still using one of
208 // the edge endpoints.
215 const cell& cFaces = mesh.cells()[cellI];
219 label faceI = cFaces[i];
221 const face& f = faces[faceI];
223 label fp0 = findIndex(f, e[0]);
224 label fp1 = findIndex(f, e[1]);
230 // Face uses e[1] but not e[0]
236 // Have both faces so exit
245 // Face uses both e[1] and e[0]
260 if (leftI == -1 || rightI == -1)
262 FatalErrorIn("splitHex") << "Problem : leftI:" << leftI
263 << " rightI:" << rightI << abort(FatalError);
266 // Walk two vertices further on faces.
268 const face& leftF = faces[leftI];
270 label leftV = leftF[(leftFp + 2) % leftF.size()];
272 const face& rightF = faces[rightI];
274 label rightV = rightF[(rightFp + 2) % rightF.size()];
277 loop[0] = ev.vertToEVert(e[0]);
278 loop[1] = ev.vertToEVert(leftV);
279 loop[2] = ev.vertToEVert(rightV);
280 loop[3] = ev.vertToEVert(e[1]);
282 scalarField loopWeights(4);
283 loopWeights[0] = -GREAT;
284 loopWeights[1] = -GREAT;
285 loopWeights[2] = -GREAT;
286 loopWeights[3] = -GREAT;
288 cutCells.append(cellI);
289 cellLoops.append(loop);
290 cellEdgeWeights.append(loopWeights);
296 // Split cellI along edgeI with a plane along halfNorm direction.
299 const polyMesh& mesh,
300 const geomCellLooper& cellCutter,
304 const vector& halfNorm,
306 const boolList& vertIsCut,
307 const boolList& edgeIsCut,
308 const scalarField& edgeWeight,
310 DynamicList<label>& cutCells,
311 DynamicList<labelList>& cellLoops,
312 DynamicList<scalarField>& cellEdgeWeights
315 const edge& e = mesh.edges()[edgeI];
317 vector eVec = e.vec(mesh.points());
320 vector planeN = eVec ^ halfNorm;
322 // Slightly tilt plane to make it not cut edges exactly
323 // halfway on fully regular meshes (since we want cuts
324 // to be snapped to vertices)
325 planeN += 0.01*halfNorm;
327 planeN /= mag(planeN);
329 // Define plane through edge
330 plane cutPlane(mesh.points()[e.start()], planeN);
333 scalarField loopWeights;
349 // Did manage to cut cell. Copy into overall list.
350 cutCells.append(cellI);
351 cellLoops.append(loop);
352 cellEdgeWeights.append(loopWeights);
363 // Collects cuts for all cells in cellSet
366 const polyMesh& mesh,
367 const geomCellLooper& cellCutter,
371 const cellSet& cellsToCut,
373 DynamicList<label>& cutCells,
374 DynamicList<labelList>& cellLoops,
375 DynamicList<scalarField>& cellEdgeWeights
378 // Get data from mesh
379 const cellShapeList& cellShapes = mesh.cellShapes();
380 const labelList& own = mesh.faceOwner();
381 const labelListList& cellEdges = mesh.cellEdges();
382 const vectorField& faceAreas = mesh.faceAreas();
385 const cellModel& hex = *(cellModeller::lookup("hex"));
387 // cut handling functions
391 // Cut information per mesh entity
392 boolList vertIsCut(mesh.nPoints(), false);
393 boolList edgeIsCut(mesh.nEdges(), false);
394 scalarField edgeWeight(mesh.nEdges(), -GREAT);
398 cellSet::const_iterator iter = cellsToCut.begin();
399 iter != cellsToCut.end();
403 label cellI = iter.key();
405 const labelList& cEdges = cellEdges[cellI];
409 label edgeI = cEdges[i];
412 meshTools::getEdgeFaces(mesh, cellI, edgeI, f0, f1);
414 vector n0 = faceAreas[f0];
417 vector n1 = faceAreas[f1];
436 bool splitOk = false;
438 if (!geometry && cellShapes[cellI].model() == hex)
456 if ((own[f0] == cellI) ^ (own[f1] == cellI))
458 // Opposite owner orientation
459 halfNorm = 0.5*(n0 - n1);
463 // Faces have same owner or same neighbour so
464 // normals point in same direction
465 halfNorm = 0.5*(n0 + n1);
490 // Update cell/edge/vertex wise info.
491 label index = cellLoops.size() - 1;
492 const labelList& loop = cellLoops[index];
493 const scalarField& loopWeights = cellEdgeWeights[index];
501 edgeIsCut[ev.getEdge(cut)] = true;
502 edgeWeight[ev.getEdge(cut)] = loopWeights[i];
506 vertIsCut[ev.getVertex(cut)] = true;
510 // Stop checking edges for this cell.
519 cellEdgeWeights.shrink();
525 int main(int argc, char *argv[])
527 argList::noParallel();
528 argList::validOptions.insert("set", "cellSet name");
529 argList::validOptions.insert("geometry", "");
530 argList::validOptions.insert("tol", "edge snap tolerance");
531 argList::validOptions.insert("overwrite", "");
532 argList::validArgs.append("edge angle [0..360]");
534 # include "setRootCase.H"
535 # include "createTime.H"
536 runTime.functionObjects().off();
537 # include "createPolyMesh.H"
538 const word oldInstance = mesh.pointsInstance();
540 scalar featureAngle(readScalar(IStringStream(args.additionalArgs()[0])()));
542 scalar radAngle = featureAngle * mathematicalConstant::pi/180.0;
543 scalar minCos = Foam::cos(radAngle);
544 scalar minSin = Foam::sin(radAngle);
546 bool readSet = args.optionFound("set");
547 bool geometry = args.optionFound("geometry");
548 bool overwrite = args.optionFound("overwrite");
550 scalar edgeTol = 0.2;
551 args.optionReadIfPresent("tol", edgeTol);
553 Info<< "Trying to split cells with internal angles > feature angle\n" << nl
554 << "featureAngle : " << featureAngle << nl
555 << "edge snapping tol : " << edgeTol << nl;
558 Info<< "candidate cells : cellSet " << args.option("set") << nl;
562 Info<< "candidate cells : all cells" << nl;
566 Info<< "hex cuts : geometric; using edge tolerance" << nl;
570 Info<< "hex cuts : topological; cut to opposite edge" << nl;
575 // Cell circumference cutter
576 geomCellLooper cellCutter(mesh);
577 // Snap all edge cuts close to endpoints to vertices.
578 geomCellLooper::setSnapTol(edgeTol);
580 // Candidate cells to cut
581 cellSet cellsToCut(mesh, "cellsToCut", mesh.nCells()/100);
585 // Read cells to cut from cellSet
586 cellSet cells(mesh, args.option("set"));
595 // Try all cells for cutting
596 for (label cellI = 0; cellI < mesh.nCells(); cellI++)
598 cellsToCut.insert(cellI);
603 // Cut information per cut cell
604 DynamicList<label> cutCells(mesh.nCells()/10 + 10);
605 DynamicList<labelList> cellLoops(mesh.nCells()/10 + 10);
606 DynamicList<scalarField> cellEdgeWeights(mesh.nCells()/10 + 10);
622 cellSet cutSet(mesh, "cutSet", cutCells.size());
625 cutSet.insert(cutCells[i]);
628 // Gets cuts across cells from cuts through edges.
629 Info<< "Writing " << cutSet.size() << " cells to cut to cellSet "
630 << cutSet.instance()/cutSet.local()/cutSet.name()
634 // Analyze cuts for clashes.
638 cutCells, // cells candidate for cutting
643 Info<< "Actually cut cells:" << cuts.nLoops() << nl << endl;
645 if (cuts.nLoops() == 0)
650 // Remove cut cells from cellsToCut (Note:only relevant if -readSet)
651 forAll(cuts.cellLoops(), cellI)
653 if (cuts.cellLoops()[cellI].size())
655 //Info<< "Removing cut cell " << cellI << " from wishlist"
657 cellsToCut.erase(cellI);
661 // At least some cells are cut.
662 polyTopoChange meshMod(mesh);
665 meshCutter cutter(mesh);
667 // Insert mesh refinement into polyTopoChange.
668 cutter.setRefinement(cuts, meshMod);
671 Info<< "Morphing ..." << endl;
678 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
680 if (morphMap().hasMotionPoints())
682 mesh.movePoints(morphMap().preMotionPoints());
685 // Update stored labels on meshCutter
686 cutter.updateMesh(morphMap());
689 cellsToCut.updateMesh(morphMap());
691 Info<< "Remaining:" << cellsToCut.size() << endl;
693 // Write resulting mesh
696 mesh.setInstance(oldInstance);
699 Info<< "Writing refined morphMesh to time " << runTime.timeName()
705 Info << "End\n" << endl;
711 // ************************************************************************* //