1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | foam-extend: Open Source CFD
4 \\ / O peration | Version: 3.2
5 \\ / A nd | Web: http://www.foam-extend.org
6 \\/ M anipulation | For copyright notice see file Copyright
7 -------------------------------------------------------------------------------
9 This file is part of foam-extend.
11 foam-extend 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 3 of the License, or (at your
14 option) any later version.
16 foam-extend is distributed in the hope that it will be useful, but
17 WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 General Public License for more details.
21 You should have received a copy of the GNU General Public License
22 along with foam-extend. 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 \*---------------------------------------------------------------------------*/
42 #include "objectRegistry.H"
44 #include "directTopoChange.H"
45 #include "mapPolyMesh.H"
49 #include "cellModeller.H"
50 #include "meshCutter.H"
51 #include "mathematicalConstants.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);
397 cellSet::const_iterator iter = cellsToCut.begin();
398 iter != cellsToCut.end();
402 label cellI = iter.key();
404 const labelList& cEdges = cellEdges[cellI];
408 label edgeI = cEdges[i];
411 meshTools::getEdgeFaces(mesh, cellI, edgeI, f0, f1);
413 vector n0 = faceAreas[f0];
416 vector n1 = faceAreas[f1];
435 bool splitOk = false;
437 if (!geometry && cellShapes[cellI].model() == hex)
455 if ((own[f0] == cellI) ^ (own[f1] == cellI))
457 // Opposite owner orientation
458 halfNorm = 0.5*(n0 - n1);
462 // Faces have same owner or same neighbour so
463 // normals point in same direction
464 halfNorm = 0.5*(n0 + n1);
489 // Update cell/edge/vertex wise info.
490 label index = cellLoops.size() - 1;
491 const labelList& loop = cellLoops[index];
492 const scalarField& loopWeights = cellEdgeWeights[index];
500 edgeIsCut[ev.getEdge(cut)] = true;
501 edgeWeight[ev.getEdge(cut)] = loopWeights[i];
505 vertIsCut[ev.getVertex(cut)] = true;
509 // Stop checking edges for this cell.
518 cellEdgeWeights.shrink();
524 int main(int argc, char *argv[])
526 argList::noParallel();
527 argList::validOptions.insert("set", "cellSet name");
528 argList::validOptions.insert("geometry", "");
529 argList::validOptions.insert("tol", "edge snap tolerance");
530 argList::validOptions.insert("overwrite", "");
531 argList::validArgs.append("edge angle [0..360]");
533 # include "setRootCase.H"
534 # include "createTime.H"
535 runTime.functionObjects().off();
536 # include "createPolyMesh.H"
537 const word oldInstance = mesh.pointsInstance();
539 scalar featureAngle(readScalar(IStringStream(args.additionalArgs()[0])()));
541 scalar radAngle = featureAngle * mathematicalConstant::pi/180.0;
542 scalar minCos = Foam::cos(radAngle);
543 scalar minSin = Foam::sin(radAngle);
545 bool readSet = args.optionFound("set");
546 bool geometry = args.optionFound("geometry");
547 bool overwrite = args.optionFound("overwrite");
549 scalar edgeTol = 0.2;
550 args.optionReadIfPresent("tol", edgeTol);
552 Info<< "Trying to split cells with internal angles > feature angle\n" << nl
553 << "featureAngle : " << featureAngle << nl
554 << "edge snapping tol : " << edgeTol << nl;
557 Info<< "candidate cells : cellSet " << args.option("set") << nl;
561 Info<< "candidate cells : all cells" << nl;
565 Info<< "hex cuts : geometric; using edge tolerance" << nl;
569 Info<< "hex cuts : topological; cut to opposite edge" << nl;
574 // Cell circumference cutter
575 geomCellLooper cellCutter(mesh);
576 // Snap all edge cuts close to endpoints to vertices.
577 geomCellLooper::setSnapTol(edgeTol);
579 // Candidate cells to cut
580 cellSet cellsToCut(mesh, "cellsToCut", mesh.nCells()/100);
584 // Read cells to cut from cellSet
585 cellSet cells(mesh, args.option("set"));
594 // Try all cells for cutting
595 for (label cellI = 0; cellI < mesh.nCells(); cellI++)
597 cellsToCut.insert(cellI);
602 // Cut information per cut cell
603 DynamicList<label> cutCells(mesh.nCells()/10 + 10);
604 DynamicList<labelList> cellLoops(mesh.nCells()/10 + 10);
605 DynamicList<scalarField> cellEdgeWeights(mesh.nCells()/10 + 10);
621 cellSet cutSet(mesh, "cutSet", cutCells.size());
624 cutSet.insert(cutCells[i]);
627 // Gets cuts across cells from cuts through edges.
628 Info<< "Writing " << cutSet.size() << " cells to cut to cellSet "
629 << cutSet.instance()/cutSet.local()/cutSet.name()
633 // Analyze cuts for clashes.
637 cutCells, // cells candidate for cutting
642 Info<< "Actually cut cells:" << cuts.nLoops() << nl << endl;
644 if (cuts.nLoops() == 0)
649 // Remove cut cells from cellsToCut (Note:only relevant if -readSet)
650 forAll(cuts.cellLoops(), cellI)
652 if (cuts.cellLoops()[cellI].size())
654 //Info<< "Removing cut cell " << cellI << " from wishlist"
656 cellsToCut.erase(cellI);
660 // At least some cells are cut.
661 directTopoChange meshMod(mesh);
664 meshCutter cutter(mesh);
666 // Insert mesh refinement into directTopoChange.
667 cutter.setRefinement(cuts, meshMod);
670 Info<< "Morphing ..." << endl;
677 autoPtr<mapPolyMesh> morphMap = meshMod.changeMesh(mesh, false);
679 if (morphMap().hasMotionPoints())
681 mesh.movePoints(morphMap().preMotionPoints());
684 // Update stored labels on meshCutter
685 cutter.updateMesh(morphMap());
688 cellsToCut.updateMesh(morphMap());
690 Info<< "Remaining:" << cellsToCut.size() << endl;
692 // Write resulting mesh
695 mesh.setInstance(oldInstance);
698 Info<< "Writing refined morphMesh to time " << runTime.timeName()
704 Info << "End\n" << endl;
710 // ************************************************************************* //