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 "tetMeshOptimisation.H"
30 #include "partTetMesh.H"
31 #include "meshSurfaceEngine.H"
32 #include "meshSurfaceEngineModifier.H"
34 #include "partTetMeshSimplex.H"
35 #include "meshUntangler.H"
36 #include "volumeOptimizer.H"
37 #include "knuppMetric.H"
45 // #define DEBUGSearch
47 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
52 // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * //
54 // Construct from mesh
55 tetMeshOptimisation::tetMeshOptimisation(partTetMesh& mesh)
60 // * * * * * * * * * * * * * * * * Destructor * * * * * * * * * * * * * * * //
62 tetMeshOptimisation::~tetMeshOptimisation()
65 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
67 void tetMeshOptimisation::optimiseUsingKnuppMetric()
69 const LongList<point>& points = tetMesh_.points();
70 const LongList<partTet>& tets = tetMesh_.tets();
71 const LongList<direction>& smoothVertex = tetMesh_.smoothVertex();
73 boolList negativeNode(smoothVertex.size()), invertedTets(tets.size());
75 //- try getting rid of negative volume using the Patrik Knupp's metric
76 //- which gets non-negative contributions from invertex tets, only
78 # pragma omp parallel for if( tets.size() > 100 ) \
83 invertedTets[tetI] = false;
85 if( tets[tetI].mag(points) < VSMALL )
86 invertedTets[tetI] = true;
89 label nIter(0), nNegative, nNegativeBefore;
93 //- find the number of inverted tets
97 # pragma omp parallel for if( tets.size() > 100 ) \
98 schedule(dynamic, 10) reduction(+ : nNegative)
100 forAll(invertedTets, tetI)
102 if( invertedTets[tetI] )
105 const partTet& tet = tets[tetI];
107 for(label i=0;i<4;++i)
108 negativeNode[tet[i]] = true;
112 reduce(nNegative, sumOp<label>());
116 //- make sure that the points at procesor boundaries are selected
117 //- at all processors
118 if( Pstream::parRun() )
119 unifyNegativePoints(negativeNode);
122 List<LongList<labelledPoint> > newPositions;
124 # pragma omp parallel if( smoothVertex.size() > 100 )
130 newPositions.setSize(omp_get_num_threads());
135 LongList<labelledPoint>& np = newPositions[omp_get_thread_num()];
137 newPositions.setSize(1);
138 LongList<labelledPoint>& np = newPositions[0];
142 # pragma omp for schedule(dynamic, 10)
144 forAll(smoothVertex, nodeI)
146 if( !negativeNode[nodeI] )
149 if( smoothVertex[nodeI] & partTetMesh::SMOOTH )
151 partTetMeshSimplex simplex(tetMesh_, nodeI);
152 knuppMetric(simplex).optimizeNodePosition();
153 np.append(labelledPoint(nodeI, simplex.centrePoint()));
158 //- update mesh vertices
159 tetMesh_.updateVerticesSMP(newPositions);
160 newPositions.clear();
162 if( Pstream::parRun() )
164 updateBufferLayerPoints();
165 unifyCoordinatesParallel(&negativeNode);
168 //- check which tets have been repaired
169 boolList helper(invertedTets.size());
170 nNegativeBefore = nNegative;
174 # pragma omp parallel for if( tets.size() > 100 ) \
175 schedule(dynamic, 10) reduction(+ : nNegative)
179 helper[tetI] = false;
181 if( invertedTets[tetI] && (tets[tetI].mag(points) < VSMALL) )
185 const partTet& tet = tets[tetI];
187 for(label i=0;i<4;++i)
188 negativeNode[tet[i]] = true;
191 invertedTets.transfer(helper);
193 reduce(nNegative, sumOp<label>());
197 } while( (nNegative < nNegativeBefore) || (++nIter < 5) );
200 void tetMeshOptimisation::optimiseUsingMeshUntangler()
202 const LongList<point>& points = tetMesh_.points();
203 const LongList<partTet>& tets = tetMesh_.tets();
204 const LongList<direction>& smoothVertex = tetMesh_.smoothVertex();
206 boolList negativeNode(smoothVertex.size()), invertedTets(tets.size());
208 //- try getting rid of negative volume using the untangler
210 # pragma omp parallel for if( tets.size() > 100 ) \
211 schedule(dynamic, 10)
215 invertedTets[tetI] = false;
217 if( tets[tetI].mag(points) < VSMALL )
218 invertedTets[tetI] = true;
221 label nIter(0), nNegative, nNegativeBefore;
225 //- find the number of inverted tets
227 negativeNode = false;
229 # pragma omp parallel for if( tets.size() > 100 ) \
230 schedule(dynamic, 10) reduction(+ : nNegative)
232 forAll(invertedTets, tetI)
234 if( invertedTets[tetI] )
237 const partTet& tet = tets[tetI];
239 for(label i=0;i<4;++i)
240 negativeNode[tet[i]] = true;
244 reduce(nNegative, sumOp<label>());
248 //- make sure that the points at procesor boundaries are selected
249 //- at all processors
250 if( Pstream::parRun() )
251 unifyNegativePoints(negativeNode);
254 List<LongList<labelledPoint> > newPositions;
256 # pragma omp parallel if( smoothVertex.size() > 100 )
262 newPositions.setSize(omp_get_num_threads());
267 LongList<labelledPoint>& np = newPositions[omp_get_thread_num()];
269 newPositions.setSize(1);
270 LongList<labelledPoint>& np = newPositions[0];
274 # pragma omp for schedule(dynamic, 10)
276 forAll(smoothVertex, nodeI)
278 if( !negativeNode[nodeI] )
281 if( smoothVertex[nodeI] & partTetMesh::SMOOTH )
283 partTetMeshSimplex simplex(tetMesh_, nodeI);
284 meshUntangler(simplex).optimizeNodePosition();
285 np.append(labelledPoint(nodeI, simplex.centrePoint()));
290 //- update mesh vertices
291 tetMesh_.updateVerticesSMP(newPositions);
292 newPositions.clear();
294 if( Pstream::parRun() )
296 updateBufferLayerPoints();
297 unifyCoordinatesParallel(&negativeNode);
300 //- check which tets have been repaired
301 boolList helper(invertedTets.size());
302 nNegativeBefore = nNegative;
305 # pragma omp parallel for if( tets.size() > 100 ) \
306 schedule(dynamic, 10) reduction(+ : nNegative)
310 helper[tetI] = false;
312 if( invertedTets[tetI] && (tets[tetI].mag(points) < VSMALL) )
315 const partTet& tet = tets[tetI];
317 for(label i=0;i<4;++i)
318 negativeNode[tet[i]] = true;
322 reduce(nNegative, sumOp<label>());
325 invertedTets.transfer(helper);
327 } while( (nNegative < nNegativeBefore) || (++nIter < 5) );
330 void tetMeshOptimisation::optimiseUsingVolumeOptimizer()
332 const LongList<direction>& smoothVertex = tetMesh_.smoothVertex();
334 //- use mesh optimizer to improve the result
335 for(label i=0;i<2;++i)
337 List<LongList<labelledPoint> > newPositions;
340 # pragma omp parallel if( smoothVertex.size() > 100 )
346 newPositions.setSize(omp_get_num_threads());
351 LongList<labelledPoint>& np = newPositions[omp_get_thread_num()];
353 newPositions.setSize(1);
354 LongList<labelledPoint>& np = newPositions[0];
358 # pragma omp for schedule(dynamic, 10)
360 forAll(smoothVertex, nodeI)
361 if( smoothVertex[nodeI] & partTetMesh::SMOOTH )
363 partTetMeshSimplex simplex(tetMesh_, nodeI);
365 volumeOptimizer vOpt(simplex);
366 vOpt.optimizeNodePosition(1e-5);
368 np.append(labelledPoint(nodeI, simplex.centrePoint()));
372 //- update mesh vertices
373 tetMesh_.updateVerticesSMP(newPositions);
374 newPositions.clear();
376 if( Pstream::parRun() )
378 updateBufferLayerPoints();
379 unifyCoordinatesParallel();
384 void tetMeshOptimisation::optimiseBoundaryVolumeOptimizer
386 const bool nonShrinking
389 const LongList<point>& points = tetMesh_.points();
390 const LongList<direction>& smoothVertex = tetMesh_.smoothVertex();
393 label nThreads = omp_get_num_procs();
394 if( smoothVertex.size() < 100 )
397 const label nThreads(1);
400 for(label i=0;i<3;++i)
402 List<LongList<labelledPoint> > newPositions(nThreads);
405 # pragma omp parallel num_threads(nThreads)
409 LongList<labelledPoint>& np = newPositions[omp_get_thread_num()];
411 LongList<labelledPoint>& np = newPositions[0];
415 # pragma omp for schedule(dynamic, 5)
417 forAll(smoothVertex, nodeI)
418 if( smoothVertex[nodeI] & partTetMesh::BOUNDARY )
420 partTetMeshSimplex simplex(tetMesh_, nodeI);
422 volumeOptimizer vOpt(simplex);
423 vOpt.optimizeNodePosition(1e-5);
427 //- find boundary faces of the simplex
428 const DynList<point, 128>& pts = simplex.pts();
429 const DynList<partTet, 128>& tets = simplex.tets();
430 DynList<edge, 64> bEdges;
431 DynList<label, 64> numAppearances;
435 const partTet& tet = tets[tetI];
436 for(label i=0;i<3;++i)
438 edge e(tet[i], tet[(i+1)%3]);
440 const label pos = bEdges.containsAtPosition(e);
444 numAppearances.append(1);
448 ++numAppearances(pos);
453 //- create normal tensor of the simplex
454 symmTensor nt(symmTensor::zero);
457 if( numAppearances[beI] != 1 )
460 triangle<point, point> tri
462 pts[bEdges[beI].start()],
463 pts[bEdges[beI].end()],
467 vector n = tri.normal();
468 n /= (mag(n) + VSMALL);
469 for(direction i=0;i<vector::nComponents;++i)
470 if( Foam::mag(n[i]) < (100. * SMALL) )
476 const vector ev = eigenValues(nt);
478 //- make sure the point stays on the surface
479 vector disp = simplex.centrePoint() - points[nodeI];
481 if( mag(ev[2]) > (mag(ev[1]) + mag(ev[0])) )
483 //- ordinary surface vertex
484 vector normal = eigenVector(nt, ev[2]);
485 normal /= (mag(normal)+VSMALL);
486 disp -= (disp & normal) * normal;
488 else if( mag(ev[1]) > 0.5 * (mag(ev[2]) + mag(ev[0])) )
490 //- this vertex is on an edge
491 vector normal1 = eigenVector(nt, ev[1]);
492 normal1 /= (mag(normal1)+VSMALL);
493 vector normal2 = eigenVector(nt, ev[2]);
494 normal2 /= (mag(normal2)+VSMALL);
496 vector eVec = normal1 ^ normal2;
497 eVec /= (mag(eVec) + VSMALL);
499 disp = (disp & eVec) * eVec;
503 //- this vertex is a corner. do not move it
507 const point newP = points[nodeI] + disp;
508 np.append(labelledPoint(nodeI, newP));
512 //- move the vertex without constraining it
513 np.append(labelledPoint(nodeI, simplex.centrePoint()));
519 tetMesh_.updateVerticesSMP(newPositions);
520 newPositions.clear();
522 if( Pstream::parRun() )
524 updateBufferLayerPoints();
525 unifyCoordinatesParallel();
530 void tetMeshOptimisation::optimiseBoundarySurfaceLaplace()
532 const LongList<direction>& smoothVertex = tetMesh_.smoothVertex();
535 label nThreads = omp_get_num_procs();
536 if( smoothVertex.size() < 1000 )
539 const label nThreads(1);
542 for(label i=0;i<3;++i)
544 List<LongList<labelledPoint> > newPositions(nThreads);
547 # pragma omp parallel num_threads(nThreads)
551 LongList<labelledPoint>& np =
552 newPositions[omp_get_thread_num()];
554 LongList<labelledPoint>& np = newPositions[0];
558 # pragma omp for schedule(dynamic, 5)
560 forAll(smoothVertex, nodeI)
562 if( smoothVertex[nodeI] & partTetMesh::BOUNDARY )
564 partTetMeshSimplex simplex(tetMesh_, nodeI);
566 //- find boundary faces of the simplex
567 const DynList<point, 128>& pts = simplex.pts();
568 const DynList<partTet, 128>& tets = simplex.tets();
569 DynList<edge, 64> bndEdges;
570 DynList<label, 64> numAppearances;
572 //- find boundary edges of the simplex
575 const partTet& tet = tets[tetI];
576 for(label i=0;i<3;++i)
578 const edge e(tet[i], tet[(i+1)%3]);
579 const label pos = bndEdges.containsAtPosition(e);
584 numAppearances.append(1);
588 ++numAppearances(pos);
593 point newP(vector::zero);
595 forAll(bndEdges, beI)
597 if( numAppearances[beI] != 1 )
600 triangle<point, point> tri
602 pts[bndEdges[beI].start()],
603 pts[bndEdges[beI].end()],
604 simplex.centrePoint()
607 newP += tri.centre();
614 np.append(labelledPoint(nodeI, newP));
620 //- update tetMesh with new vertex positions
621 tetMesh_.updateVerticesSMP(newPositions);
622 newPositions.clear();
624 if( Pstream::parRun() )
626 updateBufferLayerPoints();
627 unifyCoordinatesParallel();
632 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
634 } // End namespace Foam
636 // ************************************************************************* //