1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | OpenFOAM: The Open Source CFD Toolbox
5 \\ / A nd | Copyright (C) 2011 OpenFOAM Foundation
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
13 the Free Software Foundation, either version 3 of the License, or
14 (at your 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, see <http://www.gnu.org/licenses/>.
25 Utility to split cells with flat faces.
27 Uses a geometric cut with a plane dividing the edge angle into two so
28 might produce funny cells. For hexes it will use by default a cut from
29 edge onto opposite edge (i.e. purely topological).
32 - split cells from cellSet only
33 - use geometric cut for hexes as well
35 The angle is the angle between two faces sharing an edge as seen from
36 inside each cell. So a cube will have all angles 90. If you want
37 to split cells with cell centre outside use e.g. angle 200
39 \*---------------------------------------------------------------------------*/
43 #include "polyTopoChange.H"
44 #include "polyTopoChanger.H"
45 #include "mapPolyMesh.H"
49 #include "cellModeller.H"
50 #include "meshCutter.H"
51 #include "unitConversion.H"
52 #include "geomCellLooper.H"
54 #include "edgeVertex.H"
55 #include "meshTools.H"
60 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
63 labelList pack(const boolList& lst)
65 labelList packedLst(lst.size());
72 packedLst[packedI++] = i;
75 packedLst.setSize(packedI);
81 scalarField pack(const boolList& lst, const scalarField& elems)
83 scalarField packedElems(lst.size());
90 packedElems[packedI++] = elems[i];
93 packedElems.setSize(packedI);
99 // Given sin and cos of max angle between normals calculate whether f0 and f1
100 // on cellI make larger angle. Uses sinAngle only for quadrant detection.
103 const primitiveMesh& mesh,
104 const scalar cosAngle,
105 const scalar sinAngle,
108 const label f0, // face label
111 const vector& n0, // normal at f0
115 const labelList& own = mesh.faceOwner();
117 bool sameFaceOrder = !((own[f0] == cellI) ^ (own[f1] == cellI));
119 // Get cos between faceArea vectors. Correct so flat angle (180 degrees)
121 scalar normalCosAngle = n0 & n1;
125 normalCosAngle = -normalCosAngle;
129 // Get cos between faceCentre and normal vector to determine in
130 // which quadrant angle is. (Is correct for unwarped faces only!)
131 // Correct for non-outwards pointing normal.
132 vector c1c0(mesh.faceCentres()[f1] - mesh.faceCentres()[f0]);
133 c1c0 /= mag(c1c0) + VSMALL;
135 scalar fcCosAngle = n0 & c1c0;
137 if (own[f0] != cellI)
139 fcCosAngle = -fcCosAngle;
144 // Looking for concave angles (quadrant 3 or 4)
147 // Angle is convex so smaller.
152 if (normalCosAngle < cosAngle)
164 // Looking for convex angles (quadrant 1 or 2)
172 // Convex. Check cos of normal vectors.
173 if (normalCosAngle > cosAngle)
186 // Split hex (and hex only) along edgeI creating two prisms
189 const polyMesh& mesh,
193 DynamicList<label>& cutCells,
194 DynamicList<labelList>& cellLoops,
195 DynamicList<scalarField>& cellEdgeWeights
198 // cut handling functions
201 const edgeList& edges = mesh.edges();
202 const faceList& faces = mesh.faces();
204 const edge& e = edges[edgeI];
206 // Get faces on the side, i.e. faces not using edge but still using one of
207 // the edge endpoints.
214 const cell& cFaces = mesh.cells()[cellI];
218 label faceI = cFaces[i];
220 const face& f = faces[faceI];
222 label fp0 = findIndex(f, e[0]);
223 label fp1 = findIndex(f, e[1]);
229 // Face uses e[1] but not e[0]
235 // Have both faces so exit
244 // Face uses both e[1] and e[0]
259 if (leftI == -1 || rightI == -1)
261 FatalErrorIn("splitHex") << "Problem : leftI:" << leftI
262 << " rightI:" << rightI << abort(FatalError);
265 // Walk two vertices further on faces.
267 const face& leftF = faces[leftI];
269 label leftV = leftF[(leftFp + 2) % leftF.size()];
271 const face& rightF = faces[rightI];
273 label rightV = rightF[(rightFp + 2) % rightF.size()];
276 loop[0] = ev.vertToEVert(e[0]);
277 loop[1] = ev.vertToEVert(leftV);
278 loop[2] = ev.vertToEVert(rightV);
279 loop[3] = ev.vertToEVert(e[1]);
281 scalarField loopWeights(4);
282 loopWeights[0] = -GREAT;
283 loopWeights[1] = -GREAT;
284 loopWeights[2] = -GREAT;
285 loopWeights[3] = -GREAT;
287 cutCells.append(cellI);
288 cellLoops.append(loop);
289 cellEdgeWeights.append(loopWeights);
295 // Split cellI along edgeI with a plane along halfNorm direction.
298 const polyMesh& mesh,
299 const geomCellLooper& cellCutter,
303 const vector& halfNorm,
305 const boolList& vertIsCut,
306 const boolList& edgeIsCut,
307 const scalarField& edgeWeight,
309 DynamicList<label>& cutCells,
310 DynamicList<labelList>& cellLoops,
311 DynamicList<scalarField>& cellEdgeWeights
314 const edge& e = mesh.edges()[edgeI];
316 vector eVec = e.vec(mesh.points());
319 vector planeN = eVec ^ halfNorm;
321 // Slightly tilt plane to make it not cut edges exactly
322 // halfway on fully regular meshes (since we want cuts
323 // to be snapped to vertices)
324 planeN += 0.01*halfNorm;
326 planeN /= mag(planeN);
328 // Define plane through edge
329 plane cutPlane(mesh.points()[e.start()], planeN);
332 scalarField loopWeights;
348 // Did manage to cut cell. Copy into overall list.
349 cutCells.append(cellI);
350 cellLoops.append(loop);
351 cellEdgeWeights.append(loopWeights);
362 // Collects cuts for all cells in cellSet
365 const polyMesh& mesh,
366 const geomCellLooper& cellCutter,
370 const cellSet& cellsToCut,
372 DynamicList<label>& cutCells,
373 DynamicList<labelList>& cellLoops,
374 DynamicList<scalarField>& cellEdgeWeights
377 // Get data from mesh
378 const cellShapeList& cellShapes = mesh.cellShapes();
379 const labelList& own = mesh.faceOwner();
380 const labelListList& cellEdges = mesh.cellEdges();
381 const vectorField& faceAreas = mesh.faceAreas();
384 const cellModel& hex = *(cellModeller::lookup("hex"));
386 // cut handling functions
390 // Cut information per mesh entity
391 boolList vertIsCut(mesh.nPoints(), false);
392 boolList edgeIsCut(mesh.nEdges(), false);
393 scalarField edgeWeight(mesh.nEdges(), -GREAT);
395 forAllConstIter(cellSet, cellsToCut, iter)
397 const label cellI = iter.key();
398 const labelList& cEdges = cellEdges[cellI];
402 label edgeI = cEdges[i];
405 meshTools::getEdgeFaces(mesh, cellI, edgeI, f0, f1);
407 vector n0 = faceAreas[f0];
410 vector n1 = faceAreas[f1];
429 bool splitOk = false;
431 if (!geometry && cellShapes[cellI].model() == hex)
449 if ((own[f0] == cellI) ^ (own[f1] == cellI))
451 // Opposite owner orientation
452 halfNorm = 0.5*(n0 - n1);
456 // Faces have same owner or same neighbour so
457 // normals point in same direction
458 halfNorm = 0.5*(n0 + n1);
483 // Update cell/edge/vertex wise info.
484 label index = cellLoops.size() - 1;
485 const labelList& loop = cellLoops[index];
486 const scalarField& loopWeights = cellEdgeWeights[index];
494 edgeIsCut[ev.getEdge(cut)] = true;
495 edgeWeight[ev.getEdge(cut)] = loopWeights[i];
499 vertIsCut[ev.getVertex(cut)] = true;
503 // Stop checking edges for this cell.
512 cellEdgeWeights.shrink();
518 int main(int argc, char *argv[])
522 "split cells with flat faces"
524 #include "addOverwriteOption.H"
525 argList::noParallel();
526 argList::validArgs.append("edgeAngle [0..360]");
532 "split cells from specified cellSet only"
534 argList::addBoolOption
537 "use geometric cut for hexes as well"
542 "scalar", "edge snap tolerance (default 0.2)"
545 #include "setRootCase.H"
546 #include "createTime.H"
547 runTime.functionObjects().off();
548 #include "createPolyMesh.H"
549 const word oldInstance = mesh.pointsInstance();
551 const scalar featureAngle = args.argRead<scalar>(1);
552 const scalar minCos = Foam::cos(degToRad(featureAngle));
553 const scalar minSin = Foam::sin(degToRad(featureAngle));
555 const bool readSet = args.optionFound("set");
556 const bool geometry = args.optionFound("geometry");
557 const bool overwrite = args.optionFound("overwrite");
559 const scalar edgeTol = args.optionLookupOrDefault("tol", 0.2);
561 Info<< "Trying to split cells with internal angles > feature angle\n" << nl
562 << "featureAngle : " << featureAngle << nl
563 << "edge snapping tol : " << edgeTol << nl;
566 Info<< "candidate cells : cellSet " << args["set"] << nl;
570 Info<< "candidate cells : all cells" << nl;
574 Info<< "hex cuts : geometric; using edge tolerance" << nl;
578 Info<< "hex cuts : topological; cut to opposite edge" << nl;
583 // Cell circumference cutter
584 geomCellLooper cellCutter(mesh);
585 // Snap all edge cuts close to endpoints to vertices.
586 geomCellLooper::setSnapTol(edgeTol);
588 // Candidate cells to cut
589 cellSet cellsToCut(mesh, "cellsToCut", mesh.nCells()/100);
593 // Read cells to cut from cellSet
594 cellSet cells(mesh, args["set"]);
603 // Try all cells for cutting
604 for (label cellI = 0; cellI < mesh.nCells(); cellI++)
606 cellsToCut.insert(cellI);
611 // Cut information per cut cell
612 DynamicList<label> cutCells(mesh.nCells()/10 + 10);
613 DynamicList<labelList> cellLoops(mesh.nCells()/10 + 10);
614 DynamicList<scalarField> cellEdgeWeights(mesh.nCells()/10 + 10);
630 cellSet cutSet(mesh, "cutSet", cutCells.size());
633 cutSet.insert(cutCells[i]);
636 // Gets cuts across cells from cuts through edges.
637 Info<< "Writing " << cutSet.size() << " cells to cut to cellSet "
638 << cutSet.instance()/cutSet.local()/cutSet.name()
642 // Analyze cuts for clashes.
646 cutCells, // cells candidate for cutting
651 Info<< "Actually cut cells:" << cuts.nLoops() << nl << endl;
653 if (cuts.nLoops() == 0)
658 // Remove cut cells from cellsToCut (Note:only relevant if -readSet)
659 forAll(cuts.cellLoops(), cellI)
661 if (cuts.cellLoops()[cellI].size())
663 //Info<< "Removing cut cell " << cellI << " from wishlist"
665 cellsToCut.erase(cellI);
669 // At least some cells are cut.
670 polyTopoChange meshMod(mesh);
673 meshCutter cutter(mesh);
675 // Insert mesh refinement into polyTopoChange.
676 cutter.setRefinement(cuts, meshMod);
679 Info<< "Morphing ..." << endl;
686 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
688 if (morphMap().hasMotionPoints())
690 mesh.movePoints(morphMap().preMotionPoints());
693 // Update stored labels on meshCutter
694 cutter.updateMesh(morphMap());
697 cellsToCut.updateMesh(morphMap());
699 Info<< "Remaining:" << cellsToCut.size() << endl;
701 // Write resulting mesh
704 mesh.setInstance(oldInstance);
707 Info<< "Writing refined morphMesh to time " << runTime.timeName()
713 Info<< "End\n" << endl;
719 // ************************************************************************* //