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 "surfaceOptimizer.H"
34 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
39 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
41 const vector surfaceOptimizer::dirVecs[4] =
43 vector(-1.0, -1.0, 0.0),
44 vector(1.0, -1.0, 0.0),
45 vector(-1.0, 1.0, 0.0),
49 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
51 scalar surfaceOptimizer::evaluateStabilisationFactor() const
53 //- find the minimum area
54 scalar Amin(VGREAT), LsqMax(0.0);
57 const point& p0 = pts_[trias_[triI][0]];
58 const point& p1 = pts_[trias_[triI][1]];
59 const point& p2 = pts_[trias_[triI][2]];
64 (p1.x() - p0.x()) * (p2.y() - p0.y()) -
65 (p2.x() - p0.x()) * (p1.y() - p0.y())
69 Info << "Triangle " << triI << " area " << Atri << endl;
78 Amin = Foam::min(Amin, Atri);
79 LsqMax = Foam::max(LsqMax, LSqrTri);
83 Info << "Amin " << Amin << endl;
84 Info << "LsqMax " << LsqMax << endl;
87 //- K is greater than zero in case the stabilisation is needed
89 if( Amin < SMALL * LsqMax )
97 scalar surfaceOptimizer::evaluateFunc(const scalar& K) const
103 const point& p0 = pts_[trias_[triI][0]];
104 const point& p1 = pts_[trias_[triI][1]];
105 const point& p2 = pts_[trias_[triI][2]];
110 (p1.x() - p0.x()) * (p2.y() - p0.y()) -
111 (p2.x() - p0.x()) * (p1.y() - p0.y())
114 const scalar stab = sqrt(sqr(Atri) + K);
117 Info << "Triangle " << triI << " area " << Atri << endl;
126 const scalar Astab = Foam::max(VSMALL, 0.5 * (Atri + stab));
127 func += LSqrTri / Astab;
133 //- evaluate gradients needed for optimisation
134 void surfaceOptimizer::evaluateGradients
141 gradF = vector::zero;
142 gradGradF = tensor::zero;
144 tensor gradGradLt(tensor::zero);
145 gradGradLt.xx() = 4.0;
146 gradGradLt.yy() = 4.0;
150 const point& p0 = pts_[trias_[triI][0]];
151 const point& p1 = pts_[trias_[triI][1]];
152 const point& p2 = pts_[trias_[triI][2]];
154 if( magSqr(p1 - p2) < VSMALL ) continue;
165 (p1.x() - p0.x()) * (p2.y() - p0.y()) -
166 (p2.x() - p0.x()) * (p1.y() - p0.y())
169 const scalar stab = sqrt(sqr(Atri) + K);
171 const scalar Astab = Foam::max(ROOTVSMALL, 0.5 * (Atri + stab));
173 const vector gradAtri
175 0.5 * (p1.y() - p2.y()),
176 0.5 * (p2.x() - p1.x()),
180 const vector gradAstab = 0.5 * (gradAtri + Atri * gradAtri / stab);
182 const tensor gradGradAstab =
185 (gradAtri * gradAtri) / stab -
186 sqr(Atri) * (gradAtri * gradAtri) / pow(stab, 3)
189 const vector gradLt(4.0 * p0 - 2.0 * p1 - 2.0 * p2);
191 //- calculate the gradient
192 const scalar sqrAstab = sqr(Astab);
193 gradF += gradLt / Astab - (LSqrTri * gradAstab) / sqrAstab;
195 //- calculate the second gradient
198 twoSymm(gradLt * gradAstab) / sqrAstab -
199 gradGradAstab * LSqrTri / sqrAstab +
200 2.0 * LSqrTri * (gradAstab * gradAstab) / (sqrAstab * Astab);
203 //- stabilise diagonal terms
204 if( mag(gradGradF.xx()) < VSMALL ) gradGradF.xx() = VSMALL;
205 if( mag(gradGradF.yy()) < VSMALL ) gradGradF.yy() = VSMALL;
208 scalar surfaceOptimizer::optimiseDivideAndConquer(const scalar tol)
210 point& pOpt = pts_[trias_[0][0]];
212 pOpt = 0.5 * (pMax_ + pMin_);
213 point currCentre = pOpt;
214 scalar dx = (pMax_.x() - pMin_.x()) / 2.0;
215 scalar dy = (pMax_.y() - pMin_.y()) / 2.0;
219 //- find the value of the functional in the centre of the bnd box
220 scalar K = evaluateStabilisationFactor();
221 scalar funcBefore, funcAfter(evaluateFunc(K));
225 funcBefore = funcAfter;
228 point minCentre(vector::zero);
230 for(label i=0;i<4;++i)
232 pOpt.x() = currCentre.x() + 0.5 * dirVecs[i].x() * dx;
233 pOpt.y() = currCentre.y() + 0.5 * dirVecs[i].y() * dy;
235 K = evaluateStabilisationFactor();
236 const scalar func = evaluateFunc(K);
238 if( func < funcAfter )
245 //- set the centre with the minimum value
246 //- as the centre for future search
247 currCentre = minCentre;
250 //- halve the search range
254 //- calculate the tolerence
255 const scalar t = mag(funcAfter - funcBefore) / funcAfter;
258 Info << "Point position " << pOpt << endl;
259 Info << "Func before " << funcBefore << endl;
260 Info << "Func after " << funcAfter << endl;
261 Info << "Normalised difference " << t << endl;
266 } while( ++iter < 100 );
271 scalar surfaceOptimizer::optimiseSteepestDescent(const scalar tol)
273 point& pOpt = pts_[trias_[0][0]];
275 //- find the bounding box
276 const scalar avgEdge = Foam::mag(pMax_ - pMin_);
278 //- find the minimum value on the 5 x 5 raster
279 scalar K = evaluateStabilisationFactor();
280 scalar funcBefore, funcAfter(evaluateFunc(K));
282 //- start with steepest descent optimisation
288 direction nIterations(0);
291 funcBefore = funcAfter;
293 evaluateGradients(K, gradF, gradGradF);
295 //- store data into a matrix
297 mat[0][0] = gradGradF.xx();
298 mat[0][1] = gradGradF.xy();
299 mat[1][0] = gradGradF.yx();
300 mat[1][1] = gradGradF.yy();
301 FixedList<scalar, 2> source;
302 source[0] = gradF.x();
303 source[1] = gradF.y();
305 //- calculate the determinant
306 const scalar det = mat.determinant();
308 if( mag(det) < VSMALL )
314 disp.x() = mat.solveFirst(source);
315 disp.y() = mat.solveSecond(source);
317 if( mag(disp) > 0.2 * avgEdge )
319 vector dir = disp / mag(disp);
321 disp = dir * 0.2 * avgEdge;
326 Info << "Second gradient " << gradGradF << endl;
327 Info << "Gradient " << gradF << endl;
328 Info << "Displacement " << disp << endl;
329 Info << "K = " << K << endl;
334 K = evaluateStabilisationFactor();
335 funcAfter = evaluateFunc(K);
337 if( mag(funcAfter - funcBefore) / funcBefore < tol )
341 Info << "New coordinates " << pOpt << endl;
344 } while( ++nIterations < 100 );
349 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
351 surfaceOptimizer::surfaceOptimizer
354 const DynList<triFace>& trias
362 pMin_ = pts_[trias_[0][1]];
367 const triFace& tf = trias_[triI];
369 for(label i=1;i<3;++i)
371 pMin_ = Foam::min(pMin_, pts_[tf[i]]);
372 pMax_ = Foam::max(pMax_, pts_[tf[i]]);
377 surfaceOptimizer::~surfaceOptimizer()
380 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
382 point surfaceOptimizer::optimizePoint(const scalar tol)
384 const scalar scale = mag(pMax_ - pMin_);
390 point& pOpt = pts_[trias_[0][0]];
392 const scalar funcDivide = optimiseDivideAndConquer(tol);
393 const point newPoint = pOpt;
395 const scalar funcSteepest = optimiseSteepestDescent(tol);
397 if( funcSteepest > funcDivide )
408 // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * //
410 } // End namespace Foam
412 // ************************************************************************* //