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 Extrude mesh from existing patch (by default outwards facing normals;
26 optional flips faces) or from patch read from file.
28 Note: Merges close points so be careful.
30 Type of extrusion prescribed by run-time selectable model.
32 \*---------------------------------------------------------------------------*/
36 #include "dimensionedTypes.H"
38 #include "polyTopoChange.H"
39 #include "polyTopoChanger.H"
40 #include "edgeCollapser.H"
41 #include "globalMeshData.H"
42 #include "perfectInterface.H"
43 #include "addPatchCellLayer.H"
45 #include "MeshedSurfaces.H"
46 #include "globalIndex.H"
48 #include "extrudedMesh.H"
49 #include "extrudeModel.H"
53 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
65 const char* NamedEnum<ExtrudeMode, 3>::names[] =
73 static const NamedEnum<ExtrudeMode, 3> ExtrudeModeNames;
76 void createDummyFvMeshFiles(const polyMesh& mesh, const word& regionName)
78 // Create dummy system/fv*
91 Info<< "Testing:" << io.objectPath() << endl;
95 Info<< "Writing dummy " << regionName/io.name() << endl;
98 dummyDict.add("divSchemes", divDict);
100 dummyDict.add("gradSchemes", gradDict);
102 dummyDict.add("laplacianSchemes", laplDict);
104 IOdictionary(io, dummyDict).regIOobject::write();
111 mesh.time().system(),
121 Info<< "Writing dummy " << regionName/io.name() << endl;
122 dictionary dummyDict;
123 IOdictionary(io, dummyDict).regIOobject::write();
129 label findPatchID(const polyBoundaryMesh& patches, const word& name)
131 const label patchID = patches.findPatchID(name);
135 FatalErrorIn("findPatchID(const polyBoundaryMesh&, const word&)")
136 << "Cannot find patch " << name
137 << " in the source mesh.\n"
138 << "Valid patch names are " << patches.names()
145 labelList patchFaces(const polyBoundaryMesh& patches, const wordList& names)
151 const polyPatch& pp = patches[findPatchID(patches, names[i])];
155 labelList faceLabels(n);
159 const polyPatch& pp = patches[findPatchID(patches, names[i])];
163 faceLabels[n++] = pp.start()+j;
171 void updateFaceLabels(const mapPolyMesh& map, labelList& faceLabels)
173 const labelList& reverseMap = map.reverseFaceMap();
175 labelList newFaceLabels(faceLabels.size());
178 forAll(faceLabels, i)
180 label oldFaceI = faceLabels[i];
182 if (reverseMap[oldFaceI] >= 0)
184 newFaceLabels[newI++] = reverseMap[oldFaceI];
187 newFaceLabels.setSize(newI);
188 faceLabels.transfer(newFaceLabels);
195 int main(int argc, char *argv[])
197 #include "addRegionOption.H"
198 #include "setRootCase.H"
199 #include "createTimeExtruded.H"
201 // Get optional regionName
204 if (args.optionReadIfPresent("region", regionName))
206 regionDir = regionName;
207 Info<< "Create mesh " << regionName << " for time = "
208 << runTimeExtruded.timeName() << nl << endl;
212 regionName = fvMesh::defaultRegion;
213 Info<< "Create mesh for time = "
214 << runTimeExtruded.timeName() << nl << endl;
223 runTimeExtruded.system(),
225 IOobject::MUST_READ_IF_MODIFIED
230 autoPtr<extrudeModel> model(extrudeModel::New(dict));
232 // Whether to flip normals
233 const Switch flipNormals(dict.lookup("flipNormals"));
236 const ExtrudeMode mode = ExtrudeModeNames.read
238 dict.lookup("constructFrom")
242 // Generated mesh (one of either)
243 autoPtr<fvMesh> meshFromMesh;
244 autoPtr<polyMesh> meshFromSurface;
246 // Faces on front and back for stitching (in case of mergeFaces)
248 labelList frontPatchFaces;
250 labelList backPatchFaces;
252 if (mode == PATCH || mode == MESH)
256 FatalErrorIn(args.executable())
257 << "Flipping normals not supported for extrusions from patch."
261 fileName sourceCasePath(dict.lookup("sourceCase"));
262 sourceCasePath.expand();
263 fileName sourceRootDir = sourceCasePath.path();
264 fileName sourceCaseDir = sourceCasePath.name();
265 if (Pstream::parRun())
269 /"processor" + Foam::name(Pstream::myProcNo());
271 wordList sourcePatches;
272 dict.lookup("sourcePatches") >> sourcePatches;
274 if (sourcePatches.size() == 1)
276 frontPatchName = sourcePatches[0];
279 Info<< "Extruding patches " << sourcePatches
280 << " on mesh " << sourceCasePath << nl
285 Time::controlDictName,
290 #include "createMesh.H"
292 const polyBoundaryMesh& patches = mesh.boundaryMesh();
295 // Extrusion engine. Either adding to existing mesh or
296 // creating separate mesh.
297 addPatchCellLayer layerExtrude(mesh, (mode == MESH));
299 const labelList meshFaces(patchFaces(patches, sourcePatches));
300 indirectPrimitivePatch extrudePatch
310 // Determine extrudePatch normal
311 pointField extrudePatchPointNormals
313 PatchTools::pointNormals //calcNormals
322 // Precalculate mesh edges for pp.edges.
323 const labelList meshEdges
325 extrudePatch.meshEdges
332 // Global face indices engine
333 const globalIndex globalFaces(mesh.nFaces());
335 // Determine extrudePatch.edgeFaces in global numbering (so across
337 labelListList edgeGlobalFaces
339 addPatchCellLayer::globalEdgeFaces
348 // Determine what patches boundary edges need to get extruded into.
349 // This might actually cause edge-connected processors to become
350 // face-connected so might need to introduce new processor boundaries.
352 // - per pp.edge the patch to extrude into
353 // - any additional processor boundaries (patchToNbrProc = map from
354 // new patchID to neighbour processor)
355 // - number of new patches (nPatches)
357 labelList sidePatchID;
359 Map<label> nbrProcToPatch;
360 Map<label> patchToNbrProc;
361 addPatchCellLayer::calcSidePatch
377 label nAdded = nPatches - mesh.boundaryMesh().size();
378 reduce(nAdded, sumOp<label>());
380 Info<< "Adding overall " << nAdded << " processor patches." << endl;
384 DynamicList<polyPatch*> newPatches(nPatches);
385 forAll(mesh.boundaryMesh(), patchI)
389 mesh.boundaryMesh()[patchI].clone
397 label patchI = mesh.boundaryMesh().size();
402 label nbrProcI = patchToNbrProc[patchI];
405 + Foam::name(Pstream::myProcNo())
407 + Foam::name(nbrProcI);
409 Pout<< "Adding patch " << patchI
411 << " between " << Pstream::myProcNo()
412 << " and " << nbrProcI
418 new processorPolyPatch
422 mesh.nFaces(), // start
424 mesh.boundaryMesh(),// polyBoundaryMesh
425 Pstream::myProcNo(),// myProcNo
426 nbrProcI // neighbProcNo
431 // Add patches. Do no parallel updates.
432 mesh.removeFvBoundary();
433 mesh.addFvPatches(newPatches, true);
438 // Only used for addPatchCellLayer into new mesh
439 labelList exposedPatchID;
442 dict.lookup("exposedPatchName") >> backPatchName;
443 exposedPatchID.setSize
446 findPatchID(patches, backPatchName)
450 // Determine points and extrusion
451 pointField layer0Points(extrudePatch.nPoints());
452 pointField displacement(extrudePatch.nPoints());
453 forAll(displacement, pointI)
455 const vector& patchNormal = extrudePatchPointNormals[pointI];
458 layer0Points[pointI] = model()
460 extrudePatch.localPoints()[pointI],
465 point extrudePt = model()
467 extrudePatch.localPoints()[pointI],
471 displacement[pointI] = extrudePt - layer0Points[pointI];
476 Info<< "Flipping faces." << nl << endl;
477 displacement = -displacement;
480 // Check if wedge (has layer0 different from original patch points)
481 // If so move the mesh to starting position.
482 if (gMax(mag(layer0Points-extrudePatch.localPoints())) > SMALL)
484 Info<< "Moving mesh to layer0 points since differ from original"
485 << " points - this can happen for wedge extrusions." << nl
488 pointField newPoints(mesh.points());
489 forAll(extrudePatch.meshPoints(), i)
491 newPoints[extrudePatch.meshPoints()[i]] = layer0Points[i];
493 mesh.movePoints(newPoints);
498 labelList nFaceLayers(extrudePatch.size(), model().nLayers());
500 labelList nPointLayers(extrudePatch.nPoints(), model().nLayers());
501 // Displacement for first layer
502 vectorField firstLayerDisp(displacement*model().sumThickness(1));
504 // Expansion ratio not used.
505 scalarField ratio(extrudePatch.nPoints(), 1.0);
507 // Topo change container. Either copy an existing mesh or start
508 // with empty storage (number of patches only needed for checking)
509 autoPtr<polyTopoChange> meshMod
513 ? new polyTopoChange(mesh)
514 : new polyTopoChange(patches.size())
518 layerExtrude.setRefinement
523 ratio, // expansion ratio
524 extrudePatch, // patch faces to extrude
525 sidePatchID, // if boundary edge: patch to use
526 exposedPatchID, // if new mesh: patches for exposed faces
533 // Reset points according to extrusion model
534 forAll(layerExtrude.addedPoints(), pointI)
536 const labelList& pPoints = layerExtrude.addedPoints()[pointI];
537 forAll(pPoints, pPointI)
539 label meshPointI = pPoints[pPointI];
545 extrudePatch.localPoints()[pointI],
546 extrudePatchPointNormals[pointI],
551 const_cast<DynamicList<point>&>
554 )[meshPointI] = modelPt;
558 // Store faces on front and exposed patch (if mode=patch there are
559 // only added faces so cannot used map to old faces)
560 const labelListList& layerFaces = layerExtrude.layerFaces();
561 backPatchFaces.setSize(layerFaces.size());
562 frontPatchFaces.setSize(layerFaces.size());
563 forAll(backPatchFaces, i)
565 backPatchFaces[i] = layerFaces[i].first();
566 frontPatchFaces[i] = layerFaces[i].last();
569 // Create dummy fvSchemes, fvSolution
570 createDummyFvMeshFiles(mesh, regionDir);
572 // Create actual mesh from polyTopoChange container
573 autoPtr<mapPolyMesh> map = meshMod().makeMesh
579 runTimeExtruded.constant(),
582 IOobject::AUTO_WRITE,
588 // Calculate face labels for front and back.
589 frontPatchFaces = renumber
591 map().reverseFaceMap(),
594 backPatchFaces = renumber
596 map().reverseFaceMap(),
603 fileName surfName(dict.lookup("surface"));
605 Info<< "Extruding surfaceMesh read from file " << surfName << nl
608 MeshedSurface<face> fMesh(surfName);
612 Info<< "Flipping faces." << nl << endl;
613 faceList& faces = const_cast<faceList&>(fMesh.faces());
616 faces[i] = fMesh[i].reverseFace();
620 Info<< "Extruding surface with :" << nl
621 << " points : " << fMesh.points().size() << nl
622 << " faces : " << fMesh.size() << nl
623 << " normals[0] : " << fMesh.faceNormals()[0]
627 meshFromSurface.reset
633 extrudedMesh::defaultRegion,
634 runTimeExtruded.constant(),
643 // Get the faces on front and back
644 frontPatchName = "originalPatch";
645 frontPatchFaces = patchFaces
647 meshFromSurface().boundaryMesh(),
648 wordList(1, frontPatchName)
650 backPatchName = "otherSide";
651 backPatchFaces = patchFaces
653 meshFromSurface().boundaryMesh(),
654 wordList(1, backPatchName)
667 const boundBox& bb = mesh.bounds();
668 const vector span = bb.span();
669 const scalar mergeDim = 1E-4 * bb.minDim();
671 Info<< "Mesh bounding box : " << bb << nl
672 << " with span : " << span << nl
673 << "Merge distance : " << mergeDim << nl
681 Info<< "Collapsing edges < " << mergeDim << " ..." << nl << endl;
683 // Edge collapsing engine
684 edgeCollapser collapser(mesh);
686 const edgeList& edges = mesh.edges();
687 const pointField& points = mesh.points();
691 const edge& e = edges[edgeI];
693 scalar d = e.mag(points);
697 Info<< "Merging edge " << e << " since length " << d
698 << " << " << mergeDim << nl;
700 // Collapse edge to e[0]
701 collapser.collapseEdge(edgeI, e[0]);
705 // Topo change container
706 polyTopoChange meshMod(mesh);
707 // Put all modifications into meshMod
708 bool anyChange = collapser.setRefinement(meshMod);
712 // Construct new mesh from polyTopoChange.
713 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false);
716 mesh.updateMesh(map);
718 // Update stored data
719 updateFaceLabels(map(), frontPatchFaces);
720 updateFaceLabels(map(), backPatchFaces);
722 // Move mesh (if inflation used)
723 if (map().hasMotionPoints())
725 mesh.movePoints(map().preMotionPoints());
731 // Merging front and back patch faces
732 // ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
734 Switch mergeFaces(dict.lookup("mergeFaces"));
739 FatalErrorIn(args.executable())
740 << "Cannot stitch front and back of extrusion since"
741 << " in 'mesh' mode (extrusion appended to mesh)."
745 Info<< "Assuming full 360 degree axisymmetric case;"
746 << " stitching faces on patches "
747 << frontPatchName << " and "
748 << backPatchName << " together ..." << nl << endl;
750 if (frontPatchFaces.size() != backPatchFaces.size())
752 FatalErrorIn(args.executable())
753 << "Differing number of faces on front ("
754 << frontPatchFaces.size() << ") and back ("
755 << backPatchFaces.size() << ")"
761 polyTopoChanger stitcher(mesh);
764 const word cutZoneName("originalCutFaceZone");
773 boolList(frontPatchFaces.size(), false),
779 mesh.addZones(List<pointZone*>(0), fz, List<cellZone*>(0));
781 // Add the perfect interface mesh modifier
782 perfectInterface perfectStitcher
788 word::null, // dummy patch name
789 word::null // dummy patch name
792 // Topo change container
793 polyTopoChange meshMod(mesh);
795 perfectStitcher.setRefinement
797 indirectPrimitivePatch
806 indirectPrimitivePatch
818 // Construct new mesh from polyTopoChange.
819 autoPtr<mapPolyMesh> map = meshMod.changeMesh(mesh, false);
822 mesh.updateMesh(map);
824 // Move mesh (if inflation used)
825 if (map().hasMotionPoints())
827 mesh.movePoints(map().preMotionPoints());
831 mesh.setInstance(runTimeExtruded.constant());
832 Info<< "Writing mesh to " << mesh.objectPath() << nl << endl;
836 FatalErrorIn(args.executable()) << "Failed writing mesh"
840 Info<< "End\n" << endl;
846 // ************************************************************************* //