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 "meshSurfaceEngine.H"
31 #include "helperFunctions.H"
32 #include "labelledScalar.H"
33 #include "polyMeshGenAddressing.H"
41 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
46 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
48 void boundaryLayerOptimisation::hairEdgesAtBndFace
51 const label baseFaceI,
52 DynList<edge>& hairEdges
55 const faceListPMG& faces = mesh_.faces();
57 const cell& c = mesh_.cells()[cellI];
59 //- check cell topology
60 DynList<edge, 48> edges;
61 DynList<DynList<label, 2>, 48> edgeFaces;
62 DynList<DynList<label, 10>, 24> faceEdges;
63 faceEdges.setSize(c.size());
67 if( c[fI] == baseFaceI )
72 const face& f = faces[c[fI]];
73 faceEdges[fI].setSize(f.size());
77 const edge e = f.faceEdge(eI);
79 label pos = edges.containsAtPosition(e);
85 edgeFaces.setSize(pos+1);
88 edgeFaces[pos].append(fI);
89 faceEdges[fI][eI] = pos;
93 const face& bf = faces[c[baseFace]];
94 hairEdges.setSize(bf.size());
98 const label nextEdge = faceEdges[baseFace][pI];
99 const label prevEdge = faceEdges[baseFace][bf.rcIndex(pI)];
101 if( edgeFaces[nextEdge].size() != 2 || edgeFaces[prevEdge].size() != 2 )
104 //- find the face attached to the edge after the current point
105 label otherNextFace = edgeFaces[nextEdge][0];
106 if( otherNextFace == baseFace )
107 otherNextFace = edgeFaces[nextEdge][1];
109 //- find the face attached to the edge before the current point
110 label otherPrevFace = edgeFaces[prevEdge][0];
111 if( otherPrevFace == baseFace )
112 otherPrevFace = edgeFaces[prevEdge][1];
115 for(commonEdge=0;commonEdge<edges.size();++commonEdge)
117 edgeFaces[commonEdge].contains(otherNextFace) &&
118 edgeFaces[commonEdge].contains(otherPrevFace)
122 if( commonEdge == edges.size() )
125 //- there exists a common edge which shall be used as a hair
126 if( edges[commonEdge].start() == bf[pI] )
128 hairEdges[pI] = edges[commonEdge];
132 hairEdges[pI] = edges[commonEdge].reverseEdge();
137 scalar boundaryLayerOptimisation::calculateThickness
143 const pointFieldPMG& points = mesh_.points();
145 //- references to hair edges
146 const edge& he = hairEdges_[heI];
147 const edge& nhe = hairEdges_[heJ];
149 //- distance vector between the surface points of hair edges
150 const point& sp = points[he[0]];
151 const point& ep = points[nhe[0]];
152 const vector dv = ep - sp;
153 const scalar magDv = mag(dv);
155 //- calculate layer thickness
156 const scalar currThickness = he.mag(points);
157 scalar retThickness = currThickness;
159 const scalar currNeiThickness = nhe.mag(points);
160 scalar suggestedNeiThickness = currNeiThickness;
162 //- calculate layer height at the current point
163 const point npAlpha = help::nearestPointOnTheEdge(sp, ep, points[he[1]]);
164 const scalar currHeight = mag(npAlpha - points[he[1]]);
165 scalar retHeight = currHeight;
166 const scalar cosAlpha = sign((npAlpha - sp) & dv) * mag(npAlpha - sp);
173 Foam::min(1.0, cosAlpha / (currThickness + VSMALL))
177 //- calculate the height of the layer at the neighbour
179 const point npBeta = help::nearestPointOnTheEdge(ep, sp, points[nhe[1]]);
180 const scalar currNeiHeight = mag(npBeta - points[nhe[1]]);
181 scalar suggestedNeiHeight = currNeiHeight;
182 const scalar cosBeta = sign((npBeta - ep) & -dv) * mag(npBeta - ep);
189 Foam::min(1.0, cosBeta / (currNeiThickness + VSMALL))
193 //- check if the current thickness is Ok for the local curvature
194 if( (alpha + beta) < M_PI )
196 const scalar gamma = M_PI - (alpha + beta);
197 const scalar sinGamma = Foam::max(SMALL, Foam::sin(gamma));
198 const scalar sinAlpha = Foam::max(SMALL, Foam::sin(alpha));
199 const scalar sinBeta = Foam::max(SMALL, Foam::sin(beta));
201 //- max allowed thickness and layer height due to curvature
206 featureSizeFactor_ * magDv * sinBeta / sinGamma
208 retHeight *= (retThickness / (currThickness + VSMALL));
210 //- max allowed neighbour hair thickness
211 //- and layer height due to curvature
212 suggestedNeiThickness =
215 suggestedNeiThickness,
216 featureSizeFactor_ * magDv * sinAlpha / sinGamma
218 suggestedNeiHeight *=
219 (suggestedNeiThickness / (currNeiThickness + VSMALL));
222 //- check the height variation
223 const scalar tanVal = (retHeight - suggestedNeiHeight) / (magDv + VSMALL);
225 if( tanVal > relThicknessTol_ )
227 retHeight = suggestedNeiHeight + relThicknessTol_ * magDv;
229 retThickness = (retHeight / currHeight) * currThickness;
235 scalar boundaryLayerOptimisation::calculateThicknessOverCell
239 const label baseFaceI
242 const pointFieldPMG& points = mesh_.points();
243 const faceListPMG& faces = mesh_.faces();
245 const cell& c = mesh_.cells()[cellI];
247 const face& bf = faces[baseFaceI];
249 const edge& he = hairEdges_[heI];
251 const point& sp = points[he[0]];
252 const point& ep = points[he[1]];
254 scalar maxThickness = he.mag(points);
256 //- the base face must not contain the hair edge
257 //- this is the case at exitting layers
259 if( bf.faceEdge(eI) == he )
264 if( c[fI] == baseFaceI )
267 const face& f = faces[c[fI]];
269 if( help::shareAnEdge(bf, f) && (f.which(he.start()) == -1) )
273 if( !help::lineFaceIntersection(sp, ep, f, points, intersection) )
276 const scalar maxDist = featureSizeFactor_ * mag(intersection - sp);
279 Foam::min(maxThickness, maxDist);
286 void boundaryLayerOptimisation::optimiseThicknessVariation
288 const direction edgeType
291 pointFieldPMG& points = mesh_.points();
293 const meshSurfaceEngine& mse = meshSurface();
294 const labelList& bp = mse.bp();
295 const VRWGraph& pFaces = mse.pointFaces();
296 const faceList::subList& bFaces = mse.boundaryFaces();
297 const label start = mesh_.nInternalFaces();
298 const labelList& faceOwner = mse.faceOwners();
300 vectorField hairDirections(hairEdges_.size());
301 scalarField hairLength(hairEdges_.size());
304 # pragma omp parallel for schedule(dynamic, 50)
306 forAll(hairEdges_, hairEdgeI)
308 vector n = hairEdges_[hairEdgeI].vec(points);
310 hairLength[hairEdgeI] = (Foam::mag(n) + VSMALL);
311 hairDirections[hairEdgeI] = n / hairLength[hairEdgeI];
314 //- reduce thickness of the layer
315 //- such that the variation of layer thickness
316 //- It is an iterative process where the layer is thinned in the regions
317 //- where the tangent is greater than the tolerance value or the curvature
318 //- permits thicker boundary layers.
319 boolList activeHairEdge(hairEdges_.size(), true);
326 boolList modifiedHairEdge(hairEdges_.size(), false);
327 boolList influencedEdges(hairEdges_.size(), false);
329 //- check if the hair edge intersects some other face in the cells
330 //- attached to the hair edge
332 # pragma omp parallel for schedule(dynamic, 50)
334 forAll(hairEdges_, hairEdgeI)
338 (hairEdgeType_[hairEdgeI] & edgeType) &&
339 activeHairEdge[hairEdgeI]
342 const edge& he = hairEdges_[hairEdgeI];
344 const label bpI = bp[he.start()];
346 scalar maxThickness = hairLength[hairEdgeI];
348 DynList<label, 64> influencers;
350 forAllRow(pFaces, bpI, pfI)
352 const label bfI = pFaces(bpI, pfI);
354 const face& bf = bFaces[bfI];
355 if( bf.which(he.end()) >= 0 )
358 const label baseFaceI = start + bfI;
359 const label cOwn = faceOwner[bfI];
361 //- check if there exist any self-intersections
366 calculateThicknessOverCell
374 //- check thickness variation over all hair edges
375 DynList<edge> hairEdges;
376 hairEdgesAtBndFace(faceOwner[bfI], baseFaceI, hairEdges);
380 const edge& nhe = hairEdges[pI];
382 const label bpJ = bp[nhe.start()];
384 forAllRow(hairEdgesAtBndPoint_, bpJ, peJ)
386 const label heJ = hairEdgesAtBndPoint_(bpJ, peJ);
388 if( hairEdgeI == heJ )
391 if( nhe == hairEdges_[heJ] )
393 influencers.append(heJ);
395 const scalar edgeThickness =
396 calculateThickness(hairEdgeI, heJ);
399 Foam::min(maxThickness, edgeThickness);
405 if( hairLength[hairEdgeI] > maxThickness )
407 //- make the hair edge shorter
408 hairLength[hairEdgeI] = maxThickness;
409 modifiedHairEdge[hairEdgeI] = true;
412 thinnedHairEdge_[hairEdgeI] = true;
414 forAll(influencers, i)
415 influencedEdges[influencers[i]] = true;
421 # pragma omp parallel for schedule(dynamic, 50)
423 forAll(hairEdgesNearHairEdge_, hairEdgeI)
425 const scalar magN = hairLength[hairEdgeI];
430 "void boundaryLayerOptimisation::optimiseThicknessVariation"
431 "(const direction, const label, const scalar, const scalar)"
432 ) << "Zero layer thickness at hair edge " << hairEdgeI
433 << ". Exitting..." << exit(FatalError);
435 if( hairEdgeType_[hairEdgeI] & edgeType )
437 forAllRow(hairEdgesNearHairEdge_, hairEdgeI, nheI)
439 const label hairEdgeJ =
440 hairEdgesNearHairEdge_(hairEdgeI, nheI);
442 if( !activeHairEdge[hairEdgeJ] )
445 const scalar maxThickness =
452 if( hairLength[hairEdgeI] > maxThickness )
454 //- make the hair edge shorter
455 hairLength[hairEdgeI] = maxThickness;
458 thinnedHairEdge_[hairEdgeI] = true;
459 modifiedHairEdge[hairEdgeI] = true;
466 if( Pstream::parRun() )
468 //- collect data at inter-processor boundaries
469 const Map<label>& globalToLocal =
470 mse.globalToLocalBndPointAddressing();
472 const edgeList& edges = mesh_.addressingData().edges();
473 const VRWGraph& pointEdges = mesh_.addressingData().pointEdges();
474 const VRWGraph& edgesAtProcs =
475 mesh_.addressingData().edgeAtProcs();
476 const labelLongList& globalEdgeLabel =
477 mesh_.addressingData().globalEdgeLabel();
478 const Map<label>& globalToLocalEdge =
479 mesh_.addressingData().globalToLocalEdgeAddressing();
480 const DynList<label>& eNeiProcs =
481 mesh_.addressingData().edgeNeiProcs();
483 std::map<label, LongList<labelledScalar> > exchangeData;
485 exchangeData[eNeiProcs[i]].clear();
487 forAllConstIter(Map<label>, globalToLocal, it)
489 const label bpI = it();
491 forAllRow(hairEdgesAtBndPoint_, bpI, i)
493 const label hairEdgeI = hairEdgesAtBndPoint_(bpI, i);
495 if( !(hairEdgeType_[hairEdgeI] & edgeType) )
498 const edge& he = hairEdges_[hairEdgeI];
500 forAllRow(pointEdges, he.start(), peI)
502 const label edgeI = pointEdges(he.start(), peI);
504 if( he == edges[edgeI] )
506 labelledScalar lScalar
508 globalEdgeLabel[edgeI],
509 hairLength[hairEdgeI]
512 forAllRow(edgesAtProcs, edgeI, j)
514 const label neiProc = edgesAtProcs(edgeI, j);
516 if( neiProc == Pstream::myProcNo() )
519 LongList<labelledScalar>& dts =
520 exchangeData[neiProc];
529 LongList<labelledScalar> receivedData;
530 help::exchangeMap(exchangeData, receivedData);
532 forAll(receivedData, i)
534 const labelledScalar& lScalar = receivedData[i];
535 const label edgeI = globalToLocalEdge[lScalar.scalarLabel()];
536 const edge& e = edges[edgeI];
539 for(label pI=0;pI<2;++pI)
541 const label bpI = bp[e[pI]];
546 forAllRow(hairEdgesAtBndPoint_, bpI, i)
548 const label hairEdgeI = hairEdgesAtBndPoint_(bpI, i);
550 if( hairEdges_[hairEdgeI] == e )
552 if( lScalar.value() < hairLength[hairEdgeI] )
554 hairLength[hairEdgeI] = lScalar.value();
556 thinnedHairEdge_[hairEdgeI] = true;
557 modifiedHairEdge[hairEdgeI] = true;
571 //- reduce the information over all processors
572 reduce(changed, maxOp<bool>());
577 //- move boundary vertices to the new positions
579 # pragma omp parallel for schedule(dynamic, 100)
581 forAll(hairEdges_, hairEdgeI)
583 if( hairEdgeType_[hairEdgeI] & edgeType )
585 const edge& he = hairEdges_[hairEdgeI];
586 const vector& hv = hairDirections[hairEdgeI];
587 const point& s = points[he.start()];
589 points[he.end()] = s + hairLength[hairEdgeI] * hv;
593 //- mark edges which may be changed
594 activeHairEdge.transfer(influencedEdges);
595 } while( changed && (++nIter < 1000) );
599 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
601 } // End namespace Foam
603 // ************************************************************************* //