1 /*---------------------------------------------------------------------------*\
3 \\ / F ield | cfMesh: A library for mesh generation
5 \\ / A nd | Author: Franjo Juretic (franjo.juretic@c-fields.com)
6 \\/ M anipulation | Copyright (C) Creative Fields, Ltd.
7 -------------------------------------------------------------------------------
9 This file is part of cfMesh.
11 cfMesh 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 cfMesh 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 cfMesh. If not, see <http://www.gnu.org/licenses/>.
26 \*---------------------------------------------------------------------------*/
28 #include "demandDrivenData.H"
29 #include "boundaryLayerOptimisation.H"
30 #include "meshSurfacePartitioner.H"
31 #include "meshSurfaceEngine.H"
32 #include "detectBoundaryLayers.H"
33 //#include "helperFunctions.H"
34 //#include "labelledScalar.H"
35 //#include "refLabelledPoint.H"
36 //#include "refLabelledPointScalar.H"
37 #include "polyMeshGenAddressing.H"
38 #include "meshSurfaceOptimizer.H"
39 #include "meshSurfaceEngineModifier.H"
40 //#include "partTetMeshSimplex.H"
41 //#include "volumeOptimizer.H"
50 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
55 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
57 void boundaryLayerOptimisation::writeVTK
59 const fileName& fName,
60 const pointField& origin,
61 const vectorField& vecs
64 if( origin.size() != vecs.size() )
67 "void boundaryLayerOptimisation::writeVTK(const fileName&,"
68 " const pointField&, const vectorField&)"
69 ) << "Sizes do not match" << abort(FatalError);
74 file << "# vtk DataFile Version 3.0\n";
75 file << "vtk output\n";
77 file << "DATASET POLYDATA\n";
80 file << "POINTS " << 2*origin.size() << " float\n";
83 const point& p = origin[pI];
85 file << p.x() << ' ' << p.y() << ' ' << p.z() << nl;
87 const point op = p + vecs[pI];
89 file << op.x() << ' ' << op.y() << ' ' << op.z() << nl;
93 file << "\nLINES " << vecs.size()
94 << " " << 3*vecs.size() << nl;
97 file << 2 << " " << 2*eI << " " << (2*eI+1) << nl;
103 void boundaryLayerOptimisation::writeHairEdges
105 const fileName& fName,
106 const direction eType,
107 const vectorField& vecs
110 if( vecs.size() != hairEdges_.size() )
113 "void boundaryLayerOptimisation::writeHairEdges"
114 "(const fileName&, const direction, const vectorField&) const"
115 ) << "Sizes do not match" << abort(FatalError);
117 //- count the number of hair edges matching this criteria
120 forAll(hairEdgeType_, heI)
121 if( hairEdgeType_[heI] & eType )
125 vectorField copyVecs(counter);
126 pointField pts(counter);
130 const pointFieldPMG& points = mesh_.points();
132 forAll(hairEdgeType_, heI)
134 if( hairEdgeType_[heI] & eType )
136 const edge& he = hairEdges_[heI];
138 pts[counter] = points[he.start()];
139 copyVecs[counter] = vecs[heI] * he.mag(points);
145 //- write data to file
146 writeVTK(fName, pts, copyVecs);
149 void boundaryLayerOptimisation::writeHairEdges
151 const fileName& fName,
152 const direction eType
155 //- count the number of hair edges matching this criteria
158 forAll(hairEdgeType_, heI)
159 if( hairEdgeType_[heI] & eType )
163 vectorField vecs(counter);
164 pointField pts(counter);
168 const pointFieldPMG& points = mesh_.points();
170 forAll(hairEdgeType_, heI)
172 if( hairEdgeType_[heI] & eType )
174 const edge& he = hairEdges_[heI];
176 pts[counter] = points[he.start()];
177 vecs[counter] = he.vec(points);
183 //- write data to file
184 writeVTK(fName,pts, vecs);
187 const meshSurfaceEngine& boundaryLayerOptimisation::meshSurface() const
189 if( !meshSurfacePtr_ )
192 if( omp_in_parallel() )
195 "const meshSurfaceEngine&"
196 " boundaryLayerOptimisation::meshSurface()"
197 ) << "Cannot generate meshSurfaceEngine" << abort(FatalError);
200 meshSurfacePtr_ = new meshSurfaceEngine(mesh_);
203 return *meshSurfacePtr_;
206 const meshSurfacePartitioner&
207 boundaryLayerOptimisation::surfacePartitioner() const
209 if( !partitionerPtr_ )
212 if( omp_in_parallel() )
215 "const meshSurfacePartitioner& "
216 "boundaryLayerOptimisation::surfacePartitioner()"
217 ) << "Cannot generate meshSurfacePartitioner" << abort(FatalError);
220 partitionerPtr_ = new meshSurfacePartitioner(meshSurface());
223 return *partitionerPtr_;
226 void boundaryLayerOptimisation::calculateHairEdges()
228 const meshSurfaceEngine& mse = meshSurface();
229 const edgeList& edges = mse.edges();
230 const VRWGraph& edgeFaces = mse.edgeFaces();
231 const VRWGraph& bpEdges = mse.boundaryPointEdges();
232 const labelList& faceOwner = mse.faceOwners();
233 const faceList::subList& bFaces = mse.boundaryFaces();
234 const labelList& bp = mse.bp();
236 const meshSurfacePartitioner& mPart = surfacePartitioner();
238 //- detect layers in the mesh
239 const detectBoundaryLayers detectLayers(mPart);
241 hairEdges_ = detectLayers.hairEdges();
242 hairEdgesAtBndPoint_ = detectLayers.hairEdgesAtBndPoint();
244 //- mark boundary faces which are base face for the boundary layer
245 const labelList& layerAtBndFace = detectLayers.faceInLayer();
246 isBndLayerBase_.setSize(bFaces.size());
248 # pragma omp parallel for schedule(dynamic, 100)
250 forAll(layerAtBndFace, bfI)
252 if( layerAtBndFace[bfI] < 0 )
254 isBndLayerBase_[bfI] = false;
258 isBndLayerBase_[bfI] = true;
263 const label bndLayerFaceId = mesh_.addFaceSubset("bndLayerFaces");
264 const label startBndFaceI = mesh_.boundaries()[0].patchStart();
265 forAll(isBndLayerBase_, bfI)
266 if( isBndLayerBase_[bfI] )
267 mesh_.addFaceToSubset(bndLayerFaceId, startBndFaceI+bfI);
270 //- check if a face is an exiting face for a bnd layer
271 isExitFace_.setSize(isBndLayerBase_.size());
275 # pragma omp parallel for schedule(dynamic, 100)
277 forAll(edgeFaces, edgeI)
279 //- avoid edges at inter-processor boundaries
280 if( edgeFaces.sizeOfRow(edgeI) != 2 )
283 const label f0 = edgeFaces(edgeI, 0);
284 const label f1 = edgeFaces(edgeI, 1);
286 //- both faces have to be part of the same cell
287 if( faceOwner[f0] != faceOwner[f1] )
290 //- check if the feature edge is attachd to exittin faces
293 (isBndLayerBase_[f0] && (bFaces[f1].size() == 4)) &&
294 (isBndLayerBase_[f1] && (bFaces[f0].size() == 4))
297 isExitFace_[f0] = true;
298 isExitFace_[f1] = true;
303 const label exittingFaceId = mesh_.addFaceSubset("exittingFaces");
304 forAll(isExitFace_, bfI)
305 if( isExitFace_[bfI] )
306 mesh_.addFaceToSubset(exittingFaceId, startBndFaceI+bfI);
309 //- classify hair edges as they require different tretment
310 //- in the smoothing procedure
311 hairEdgeType_.setSize(hairEdges_.size());
313 const labelHashSet& corners = mPart.corners();
314 const labelHashSet& edgePoints = mPart.edgePoints();
315 const labelHashSet& featureEdges = mPart.featureEdges();
318 # pragma omp parallel for schedule(dynamic, 100)
320 forAll(hairEdges_, hairEdgeI)
322 hairEdgeType_[hairEdgeI] = NONE;
324 const edge& e = hairEdges_[hairEdgeI];
325 const label bpI = bp[e.start()];
327 //- check if it is a boundary edge
328 forAllRow(bpEdges, bpI, peI)
330 const label beI = bpEdges(bpI, peI);
332 const edge& be = edges[bpEdges(bpI, peI)];
336 hairEdgeType_[hairEdgeI] |= BOUNDARY;
338 if( featureEdges.found(beI) )
339 hairEdgeType_[hairEdgeI] |= FEATUREEDGE;
342 if( corners.found(bpI) )
344 hairEdgeType_[hairEdgeI] |= ATCORNER;
346 else if( edgePoints.found(bpI) )
348 hairEdgeType_[hairEdgeI] |= ATEDGE;
352 if( !(hairEdgeType_[hairEdgeI] & BOUNDARY) )
353 hairEdgeType_[hairEdgeI] |= INSIDE;
356 thinnedHairEdge_.setSize(hairEdges_.size());
358 //- calculate which other hair edges influence a hair edges
359 //- and store it in a graph
360 hairEdgesNearHairEdge_.setSize(hairEdges_.size());
362 const cellListPMG& cells = mesh_.cells();
363 const faceList& faces = mesh_.faces();
365 VRWGraph bpFacesHelper(bpEdges.size());
366 forAll(faceOwner, bfI)
368 const label cellI = faceOwner[bfI];
370 const cell& c = cells[cellI];
374 const face& f = faces[c[fI]];
378 const label bpI = bp[f[pI]];
382 bpFacesHelper.appendIfNotIn(bpI, c[fI]);
387 forAll(hairEdges_, hairEdgeI)
389 const edge& e = hairEdges_[hairEdgeI];
390 const label bpI = bp[e.start()];
392 DynList<label> neiHairEdges;
394 //- find mesh faces comprising of the current hair edge
395 forAllRow(bpFacesHelper, bpI, pfI)
397 const face& f = faces[bpFacesHelper(bpI, pfI)];
399 //- face must be a quad
403 //- check if the current face comprises of the hair edge
406 if( f.faceEdge(eI) == e )
414 //- check if the opposite edge is also a hair edge
415 const label eJ = (faceEdge+2) % 4;
417 const edge fe = f.faceEdge(eJ);
419 for(label i=0;i<2;++i)
421 const label bpJ = bp[fe[i]];
425 forAllRow(hairEdgesAtBndPoint_, bpJ, pI)
427 const label heJ = hairEdgesAtBndPoint_(bpJ, pI);
428 if( hairEdges_[heJ] == fe )
429 neiHairEdges.append(heJ);
436 hairEdgesNearHairEdge_.setRow(hairEdgeI, neiHairEdges);
440 const label hairEdgesId = mesh_.addPointSubset("hairEdgePoints");
441 const label bndHairEdgeId = mesh_.addPointSubset("bndHairEdgePoints");
442 const label featureHairEdgeId = mesh_.addPointSubset("featureEdgePoints");
443 const label cornerHairEdgeId = mesh_.addPointSubset("cornerHairEdgePoints");
444 const label hairEdgeAtEdgeId = mesh_.addPointSubset("hairEdgeAtEdgePoints");
446 forAll(hairEdgeType_, heI)
448 const edge& e = hairEdges_[heI];
450 mesh_.addPointToSubset(hairEdgesId, e.start());
451 mesh_.addPointToSubset(hairEdgesId, e.end());
452 if( hairEdgeType_[heI] & FEATUREEDGE)
454 mesh_.addPointToSubset(featureHairEdgeId, e.start());
455 mesh_.addPointToSubset(featureHairEdgeId, e.end());
458 if( hairEdgeType_[heI] & BOUNDARY)
460 mesh_.addPointToSubset(bndHairEdgeId, e.start());
461 mesh_.addPointToSubset(bndHairEdgeId, e.end());
464 if( hairEdgeType_[heI] & ATCORNER)
466 mesh_.addPointToSubset(cornerHairEdgeId, e.start());
467 mesh_.addPointToSubset(cornerHairEdgeId, e.end());
470 if( hairEdgeType_[heI] & ATEDGE)
472 mesh_.addPointToSubset(hairEdgeAtEdgeId, e.start());
473 mesh_.addPointToSubset(hairEdgeAtEdgeId, e.end());
481 bool boundaryLayerOptimisation::optimiseLayersAtExittingFaces()
483 bool modified(false);
485 //- find edge points inside the mesh with more than one hair edge
487 labelList nEdgesAtPoint(mesh_.points().size(), 0);
490 # pragma omp parallel for schedule(dynamic, 50)
492 forAll(hairEdges_, heI)
497 ++nEdgesAtPoint[hairEdges_[heI].end()];
500 //- find the points with more than one hair edge which was modified
501 //- in the previous procedure
502 boolList thinnedPoints(mesh_.points().size(), false);
505 # pragma omp parallel for schedule(dynamic, 50)
507 forAll(thinnedHairEdge_, heI)
511 thinnedHairEdge_[heI] &&
512 (nEdgesAtPoint[hairEdges_[heI].end()] > 2)
516 thinnedPoints[hairEdges_[heI].end()] = true;
520 reduce(modified, maxOp<bool>());
525 Info << "Hair edges at exitting faces shall "
526 << "be modified due to inner constraints" << endl;
531 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
533 void boundaryLayerOptimisation::optimiseLayer()
535 //- create surface smoother
536 meshSurfaceOptimizer surfOpt(meshSurface());
538 //- lock exitting faces and feature edges
539 labelLongList lockedFaces;
540 forAll(isExitFace_, bfI)
541 if( isExitFace_[bfI] )
542 lockedFaces.append(bfI);
543 surfOpt.lockBoundaryFaces(lockedFaces);
544 surfOpt.lockFeatureEdges();
549 thinnedHairEdge_ = false;
551 //- calculate normals at the boundary
552 optimiseHairNormalsAtTheBoundary();
554 //- smoothing thickness variation of boundary hairs
555 optimiseThicknessVariation(BOUNDARY);
559 meshSurfaceEngineModifier bMod(meshSurface());
560 bMod.updateGeometry();
562 surfOpt.optimizeSurface(2);
563 bMod.updateGeometry();
568 forAll(thinnedHairEdge_, heI)
569 if( thinnedHairEdge_[heI] )
571 reduce(counter, sumOp<label>());
572 Info << "Thinned " << counter << " bnd hair edges" << endl;
575 //- optimise normals inside the mesh
576 optimiseHairNormalsInside();
578 //- optimise thickness variation inside the mesh
579 optimiseThicknessVariation(INSIDE);
582 label intCounter = 0;
583 forAll(thinnedHairEdge_, heI)
584 if( thinnedHairEdge_[heI] )
586 Info << "Thinned " << (intCounter - counter)
587 << " inner hair edges" << endl;
589 } while( optimiseLayersAtExittingFaces() && (++nIter < maxNumIterations_) );
592 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
594 } // End namespace Foam
596 // ************************************************************************* //