View Issue Details
ID | Project | Category | View Status | Date Submitted | Last Update |
---|---|---|---|---|---|
0001112 | OpenFOAM | Bug | public | 2013-12-20 00:58 | 2015-03-29 21:53 |
Reporter | Assigned To | henry | |||
Priority | normal | Severity | minor | Reproducibility | always |
Status | resolved | Resolution | fixed | ||
Summary | 0001112: refineMesh utility in 2D refines the third direction while running in parallel | ||||
Description | Hi, I found a small bug using refineMesh in parallel: I defined a simple 2D square with blockMesh (says, 10*20*1 cells), with front and back defined as empty (two separate patches). If I use refineMesh, I get a new mesh of 20*40*1 cells. However, if I decompose the domain (says 4 processors) and run refineMesh in parallel, I obtain 20*40*2 cells. The third direction is also refined. Regards, Cyp | ||||
Tags | No tags attached. | ||||
|
refineMesh.C (12,476 bytes)
/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | Copyright (C) 2011-2013 OpenFOAM Foundation \\/ M anipulation | ------------------------------------------------------------------------------- License This file is part of OpenFOAM. OpenFOAM is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. OpenFOAM is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenFOAM. If not, see <http://www.gnu.org/licenses/>. Application refineMesh Description Utility to refine cells in multiple directions. Either supply -all option to refine all cells (3D refinement for 3D cases; 2D for 2D cases) or reads a refineMeshDict with - cellSet to refine - directions to refine \*---------------------------------------------------------------------------*/ #include "argList.H" #include "polyMesh.H" #include "Time.H" #include "undoableMeshCutter.H" #include "hexCellLooper.H" #include "cellSet.H" #include "twoDPointCorrector.H" #include "directions.H" #include "OFstream.H" #include "multiDirRefinement.H" #include "labelIOList.H" #include "wedgePolyPatch.H" #include "plane.H" #include "SubField.H" using namespace Foam; // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Max cos angle for edges to be considered aligned with axis. static const scalar edgeTol = 1e-3; // Calculate some edge statistics on mesh. void printEdgeStats(const primitiveMesh& mesh) { label nX = 0; label nY = 0; label nZ = 0; scalar minX = GREAT; scalar maxX = -GREAT; vector x(1, 0, 0); scalar minY = GREAT; scalar maxY = -GREAT; vector y(0, 1, 0); scalar minZ = GREAT; scalar maxZ = -GREAT; vector z(0, 0, 1); scalar minOther = GREAT; scalar maxOther = -GREAT; const edgeList& edges = mesh.edges(); forAll(edges, edgeI) { const edge& e = edges[edgeI]; vector eVec(e.vec(mesh.points())); scalar eMag = mag(eVec); eVec /= eMag; if (mag(eVec & x) > 1-edgeTol) { minX = min(minX, eMag); maxX = max(maxX, eMag); nX++; } else if (mag(eVec & y) > 1-edgeTol) { minY = min(minY, eMag); maxY = max(maxY, eMag); nY++; } else if (mag(eVec & z) > 1-edgeTol) { minZ = min(minZ, eMag); maxZ = max(maxZ, eMag); nZ++; } else { minOther = min(minOther, eMag); maxOther = max(maxOther, eMag); } } Pout<< "Mesh edge statistics:" << endl << " x aligned : number:" << nX << "\tminLen:" << minX << "\tmaxLen:" << maxX << endl << " y aligned : number:" << nY << "\tminLen:" << minY << "\tmaxLen:" << maxY << endl << " z aligned : number:" << nZ << "\tminLen:" << minZ << "\tmaxLen:" << maxZ << endl << " other : number:" << mesh.nEdges() - nX - nY - nZ << "\tminLen:" << minOther << "\tmaxLen:" << maxOther << endl << endl; } // Return index of coordinate axis. label axis(const vector& normal) { label axisIndex = -1; if (mag(normal & point(1, 0, 0)) > (1-edgeTol)) { axisIndex = 0; } else if (mag(normal & point(0, 1, 0)) > (1-edgeTol)) { axisIndex = 1; } else if (mag(normal & point(0, 0, 1)) > (1-edgeTol)) { axisIndex = 2; } return axisIndex; } //- Returns -1 or cartesian coordinate component (0=x, 1=y, 2=z) of normal // in case of 2D mesh label twoDNess(const polyMesh& mesh) { const pointField& ctrs = mesh.cellCentres(); if (ctrs.size() < 2) { return -1; } // // 1. All cell centres on single plane aligned with x, y or z // // Determine 3 points to base plane on. vector vec10 = ctrs[1] - ctrs[0]; vec10 /= mag(vec10); label otherCellI = -1; for (label cellI = 2; cellI < ctrs.size(); cellI++) { vector vec(ctrs[cellI] - ctrs[0]); vec /= mag(vec); if (mag(vec & vec10) < 0.9) { // ctrs[cellI] not in line with n otherCellI = cellI; break; } } if (otherCellI == -1) { // Cannot find cell to make decent angle with cell0-cell1 vector. // Note: what to do here? All cells (almost) in one line. Maybe 1D case? return -1; } plane cellPlane(ctrs[0], ctrs[1], ctrs[otherCellI]); forAll(ctrs, cellI) { const labelList& cEdges = mesh.cellEdges()[cellI]; scalar minLen = GREAT; forAll(cEdges, i) { minLen = min(minLen, mesh.edges()[cEdges[i]].mag(mesh.points())); } if (cellPlane.distance(ctrs[cellI]) > 1e-6*minLen) { // Centres not in plane return -1; } } label axisIndex = axis(cellPlane.normal()); if (axisIndex == -1) { return axisIndex; } const polyBoundaryMesh& patches = mesh.boundaryMesh(); // // 2. No edges without points on boundary // // Mark boundary points boolList boundaryPoint(mesh.points().size(), false); forAll(patches, patchI) { const polyPatch& patch = patches[patchI]; forAll(patch, patchFaceI) { const face& f = patch[patchFaceI]; forAll(f, fp) { boundaryPoint[f[fp]] = true; } } } const edgeList& edges = mesh.edges(); forAll(edges, edgeI) { const edge& e = edges[edgeI]; if (!boundaryPoint[e.start()] && !boundaryPoint[e.end()]) { // Edge has no point on boundary. return -1; } } // 3. For all non-wedge patches: all faces either perp or aligned with // cell-plane normal. (wedge patches already checked upon construction) forAll(patches, patchI) { const polyPatch& patch = patches[patchI]; if (!isA<wedgePolyPatch>(patch) && !patch.empty()) { const vectorField& n = patch.faceAreas(); const scalarField cosAngle(mag(n/mag(n) & cellPlane.normal())); if (mag(min(cosAngle) - max(cosAngle)) > 1e-6) { // cosAngle should be either ~1 over all faces (2D front and // back) or ~0 (all other patches perp to 2D) return -1; } } } return axisIndex; } int main(int argc, char *argv[]) { argList::addNote ( "refine cells in multiple directions" ); #include "addOverwriteOption.H" #include "addRegionOption.H" #include "addDictOption.H" #include "setRootCase.H" #include "createTime.H" runTime.functionObjects().off(); #include "createNamedPolyMesh.H" const word oldInstance = mesh.pointsInstance(); printEdgeStats(mesh); // // Read/construct control dictionary // const bool readDict = args.optionFound("dict"); const bool overwrite = args.optionFound("overwrite"); // List of cells to refine labelList refCells; // Dictionary to control refinement dictionary refineDict; if (readDict) { const word dictName("refineMeshDict"); #include "setSystemMeshDictionaryIO.H" Info<< "Refining according to " << dictName << nl << endl; refineDict = IOdictionary(dictIO); const word setName(refineDict.lookup("set")); cellSet cells(mesh, setName); Pout<< "Read " << cells.size() << " cells from cellSet " << cells.instance()/cells.local()/cells.name() << endl << endl; refCells = cells.toc(); } else { Info<< "Refining all cells" << nl << endl; // Select all cells refCells.setSize(mesh.nCells()); forAll(mesh.cells(), cellI) { refCells[cellI] = cellI; } // Set refinement directions based on 2D/3D label axisIndex = twoDNess(mesh); if (axisIndex == -1) { Info<< "3D case; refining all directions" << nl << endl; wordList directions(3); directions[0] = "tan1"; directions[1] = "tan2"; directions[2] = "normal"; refineDict.add("directions", directions); // Use hex cutter refineDict.add("useHexTopology", "true"); } else { wordList directions(2); if (axisIndex == 0) { Info<< "2D case; refining in directions y,z\n" << endl; directions[0] = "tan2"; directions[1] = "normal"; } else if (axisIndex == 1) { Info<< "2D case; refining in directions x,z\n" << endl; directions[0] = "tan1"; directions[1] = "normal"; } else { Info<< "2D case; refining in directions x,y\n" << endl; directions[0] = "tan1"; directions[1] = "tan2"; } refineDict.add("directions", directions); // Use standard cutter refineDict.add("useHexTopology", "false"); } refineDict.add("coordinateSystem", "global"); dictionary coeffsDict; coeffsDict.add("tan1", vector(1, 0, 0)); coeffsDict.add("tan2", vector(0, 1, 0)); refineDict.add("globalCoeffs", coeffsDict); refineDict.add("geometricCut", "false"); refineDict.add("writeMesh", "false"); } string oldTimeName(runTime.timeName()); if (!overwrite) { runTime++; } // Multi-directional refinement (does multiple iterations) multiDirRefinement multiRef(mesh, refCells, refineDict); // Write resulting mesh if (overwrite) { mesh.setInstance(oldInstance); } mesh.write(); // Get list of cell splits. // (is for every cell in old mesh the cells they have been split into) const labelListList& oldToNew = multiRef.addedCells(); // Create cellSet with added cells for easy inspection cellSet newCells(mesh, "refinedCells", refCells.size()); forAll(oldToNew, oldCellI) { const labelList& added = oldToNew[oldCellI]; forAll(added, i) { newCells.insert(added[i]); } } Pout<< "Writing refined cells (" << newCells.size() << ") to cellSet " << newCells.instance()/newCells.local()/newCells.name() << endl << endl; newCells.write(); // // Invert cell split to construct map from new to old // labelIOList newToOld ( IOobject ( "cellMap", runTime.timeName(), polyMesh::meshSubDir, mesh, IOobject::NO_READ, IOobject::AUTO_WRITE ), mesh.nCells() ); newToOld.note() = "From cells in mesh at " + runTime.timeName() + " to cells in mesh at " + oldTimeName; forAll(oldToNew, oldCellI) { const labelList& added = oldToNew[oldCellI]; if (added.size()) { forAll(added, i) { newToOld[added[i]] = oldCellI; } } else { // Unrefined cell newToOld[oldCellI] = oldCellI; } } Info<< "Writing map from new to old cell to " << newToOld.objectPath() << nl << endl; newToOld.write(); // Some statistics. printEdgeStats(mesh); Info<< "End\n" << endl; return 0; } // ************************************************************************* // |
|
bug1112.patch (604 bytes)
diff --git a/applications/utilities/mesh/manipulation/refineMesh/refineMesh.C b/applications/utilities/mesh/manipulation/refineMesh/refineMesh.C index cbb528e..35fc778 100644 --- a/applications/utilities/mesh/manipulation/refineMesh/refineMesh.C +++ b/applications/utilities/mesh/manipulation/refineMesh/refineMesh.C @@ -271,7 +271,7 @@ label twoDNess(const polyMesh& mesh) { const polyPatch& patch = patches[patchI]; - if (!isA<wedgePolyPatch>(patch)) + if (!isA<wedgePolyPatch>(patch) && !patch.empty()) { const vectorField& n = patch.faceAreas(); |
|
Attached are the following files: - "refineMesh.C" for replacing the one at "OpenFOAM-2.3.x/applications/utilities/mesh/manipulation/refineMesh" - "bug1112.patch" which shows the change needed. Essentially the problem is that the function "twoDNess" would check even the patches that are empty, which results in "mag(min(cosAngle) - max(cosAngle)) > 1e-6" being an impossible check (is no-one greater than 1e-6 ?), hence it probably gives something like "2*GREAT>1e-6". The proposed bug fix is to first check if the patch is empty or not, before checking if it's perpendicular. |
|
Should the test be for patches with 0 faces or of type "empty"? The patches only have 0 faces due to the decomposition; I think a reduction over the processors is needed. Rather than use all the complex geometric testing I think that the polyMesh functions //- Return the vector of geometric directions in mesh. // Defined according to the presence of empty and wedge patches. // 1 indicates unconstrained direction and -1 a constrained // direction. const Vector<label>& geometricD() const; //- Return the number of valid geometric dimensions in the mesh label nGeometricD() const; could be used. These rely on 2D-ness being defined by the presence of empty and wedge patches rather than being a single layer of cell. Alternatively the twoDNess function in refineMesh would need to be parallelized because at the moment it is possible for different processors to return different results from the 2D test. |
|
!! Ooops, I should have checked what "empty()" really meant... Yes, instead of "!patch.empty()" it should be "patch.size() > 0". And yes, "nGeometricD()" should give a more accurate and non-redundant code definition about the mesh. |
|
You were correct about the meaning of empty() (size = 0) but it is not clear that this test is valid as there is no reduction over the processors to ensure that they all agree. I am happy to change the code to use geometricD() but it does mean that for 2D cases with front and back defined other than empty (sometimes this in needed) it will be refined in all 3 directions. One option would be to change the patch type to empty and back for the refinement process. |
|
refineMeshDict (1,946 bytes)
/*--------------------------------*- C++ -*----------------------------------*\ | ========= | | | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox | | \\ / O peration | Version: 2.3.0 | | \\ / A nd | Web: www.OpenFOAM.org | | \\/ M anipulation | | \*---------------------------------------------------------------------------*/ FoamFile { version 2.0; format ascii; class dictionary; object refineMeshDict; } // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // // Cells to refine; name of cell set set c0; // Type of coordinate system: // - global : coordinate system same for every cell. Usually aligned with // x,y,z axis. Specify in globalCoeffs section below. // - patchLocal : coordinate system different for every cell. Specify in // patchLocalCoeffs section below. coordinateSystem global; //coordinateSystem patchLocal; // .. and its coefficients. x,y in this case. (normal direction is calculated // as tan1^tan2) globalCoeffs { tan1 (1 0 0); tan2 (0 1 0); } patchLocalCoeffs { patch outside; // Normal direction is facenormal of zero'th face of patch tan1 (1 0 0); } // List of directions to refine directions ( tan1 tan2 ); // Whether to use hex topology. This will // - if patchLocal: all cells on selected patch should be hex // - split all hexes in 2x2x2 through the middle of edges. useHexTopology false; // Cut purely geometric (will cut hexes through vertices) or take topology // into account. Incompatible with useHexTopology geometricCut false; // Write meshes from intermediate steps writeMesh false; // ************************************************************************* // |
|
From what I saw, "refineMesh" was already devised with some corner cases already in mind, therefore you don't need to contemplate all corner cases in a single automatic way. That is why the "refineMeshDict" is optional and I did manage to use it to enforce to only refine as if it were 2D when executed in parallel (sorry, forgot to say it sooner). The attached dictionary file proves this and can be tested with the tutorial "multiphase/interFoam/ras/damBreak". The downside of using this dictionary file is that we also have to create a "cellSet" that has the whole list of cell IDs. As for the function "twoDNess", it seemed reliable for the basic 1 cell thickness meshes, without the need to have to sync all processors. But this feels like it's redundant code, if "geometricD()" and "nGeometricD()" already give a similar functionality. |
|
I am not convinced the twoDNess function will always work; with the change you made the result may be different on processors with patch-faces and those without. Anyway I have it running with "geometricD()" and "nGeometricD()" and will push the change when I have completed the tests. |
|
Resolved by commit d7d91261a91b06828cd7977fcc47150446bdb807 |
Date Modified | Username | Field | Change |
---|---|---|---|
2013-12-20 00:58 |
|
New Issue | |
2015-03-29 17:39 | wyldckat | File Added: refineMesh.C | |
2015-03-29 17:39 | wyldckat | File Added: bug1112.patch | |
2015-03-29 17:44 | wyldckat | Note Added: 0004537 | |
2015-03-29 19:42 | henry | Note Added: 0004538 | |
2015-03-29 20:13 | wyldckat | Note Added: 0004543 | |
2015-03-29 20:28 | henry | Note Added: 0004545 | |
2015-03-29 20:41 | wyldckat | File Added: refineMeshDict | |
2015-03-29 20:48 | wyldckat | Note Added: 0004546 | |
2015-03-29 21:39 | henry | Note Added: 0004547 | |
2015-03-29 21:52 | henry | Note Added: 0004548 | |
2015-03-29 21:52 | henry | Status | new => resolved |
2015-03-29 21:52 | henry | Resolution | open => fixed |
2015-03-29 21:52 | henry | Assigned To | => henry |