/*---------------------------------------------------------------------------*\ ========= | \\ / F ield | OpenFOAM: The Open Source CFD Toolbox \\ / O peration | \\ / A nd | www.openfoam.com \\/ M anipulation | ------------------------------------------------------------------------------- Copyright (C) 2007-2019 PCOpt/NTUA Copyright (C) 2013-2019 FOSS GP Copyright (C) 2019 OpenCFD Ltd. ------------------------------------------------------------------------------- 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 . \*---------------------------------------------------------------------------*/ #include "NURBS3DCurve.H" #include "vectorField.H" // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * // namespace Foam { // * * * * * * * * * * * * * Private Member Functions * * * * * * * * * * * // label NURBS3DCurve::sgn(const scalar val) const { return val>=scalar(0) ? 1 : -1; } scalar NURBS3DCurve::abs(const scalar val) const { return (sgn(val) == 1) ? val : -val; } label NURBS3DCurve::mod(const label x, const label interval) const { label ratio(x%interval); return ratio < 0 ? ratio + interval : ratio; } void NURBS3DCurve::setUniformU() { const vectorField& curve(*this); label nPts(curve.size()); forAll(curve, ptI) { u_[ptI] = scalar(ptI)/scalar(nPts - 1); } } bool NURBS3DCurve::bound ( scalar& u, const scalar minVal = 1e-7, const scalar maxVal = 0.999999 ) const { // lower value bounding if (u < scalar(0)) { u = minVal; return true; //Info<< "Lower bound hit." << endl; } // upper value bounding if (u > scalar(1)) { u = maxVal; return true; //Info<< "Upper bound hit." << endl; } return false; } void NURBS3DCurve::setEquidistantU ( scalarList& U, const label lenAcc = 25, const label maxIter = 10, const label spacingCorrInterval=-1, const scalar tolerance = 1.e-5 ) const { const label nPts(U.size()); const scalar xLength(length() /(nPts - 1)); const scalar uLength(scalar(1) / scalar(nPts - 1)); U[0] = Zero; U[nPts - 1] = scalar(1); for (label ptI = 1; ptI<(nPts - 1); ptI++) { const scalar UPrev(U[ptI - 1]); scalar& UCurr(U[ptI]); scalar direc(scalar(1)); scalar xDiff(scalar(0)); scalar delta(scalar(0)); bool overReached(false); UCurr = UPrev + uLength; // Find the starting U value to ensure target is within 1 uLength. while (true) { bool bounded(bound(UCurr)); delta = length(UPrev, UCurr, lenAcc); xDiff = xLength - delta; // Found the point. if (abs(xDiff) < tolerance) { break; } else { direc = sgn(xDiff); if (bounded && (direc == 1)) { // uLength addition makes U exceed 1 so it becomes bounded. // However, the desired x location still exceeds how far the // bounded uLength can move (~e-5 error). // Must force U to be u=0.999999. overReached = true; break; } else if (direc == scalar(1)) { UCurr += uLength; } else { break; } } } if (!overReached) { label iter(0); while (iter < maxIter) { // Set the new search length to ensure convergence and next v. direc /= scalar(2); UCurr += direc * uLength; bound(UCurr); // Can employ an occasional tolerance check from beg of curve. if ( (spacingCorrInterval != -1) && (mod(ptI, spacingCorrInterval) == 0) ) { delta = length(scalar(0), UCurr, ptI*lenAcc); xDiff = (ptI * xLength) - delta; } else { delta = length(UPrev, UCurr, lenAcc); xDiff = xLength - delta; } // Break if found point or find the new search direction. if (abs(xDiff) < tolerance) { break; } else { const scalar oldDirec(direc); direc = sgn(xDiff) * abs(oldDirec); } iter++; } } } } // * * * * * * * * * * * * * * * * Constructors * * * * * * * * * * * * * * // NURBS3DCurve::NURBS3DCurve ( const NURBSbasis& basis, const List& CPs, const List& weights, const scalarField& u, const label nPts, const word name ) : vectorField(nPts, Zero), CPs_(CPs), weights_(weights), u_(u), name_(name), basis_(basis), givenInitNrm_(Zero), nrmOrientation_(ALIGNED) { buildCurve(); } NURBS3DCurve::NURBS3DCurve ( const NURBSbasis& basis, const List& CPs, const List& weights, const label nPts, const word name ) : vectorField(nPts, Zero), CPs_(CPs), weights_(weights), u_(nPts, Zero), name_(name), basis_(basis), givenInitNrm_(Zero), nrmOrientation_(ALIGNED) { setUniformU(); buildCurve(); } NURBS3DCurve::NURBS3DCurve ( const NURBSbasis& basis, const List& CPs, const label nPts, const word name ) : vectorField(nPts, Zero), CPs_(CPs), weights_(CPs.size(), scalar(1)), u_(nPts, Zero), name_(name), basis_(basis), givenInitNrm_(Zero), nrmOrientation_(1) { setUniformU(); buildCurve(); } // * * * * * * * * * * * * * * * Member Functions * * * * * * * * * * * * * // // * * * * * * * * * * * * * * * * Set Functions * * * * * * * * * * * * * * // void NURBS3DCurve::setNrm3DOrientation ( const vector& givenNrm, const vector& givenTan ) { givenInitNrm_ = givenNrm; const vector tan(curveDerivativeU(Zero)); vector curveNrm(tan ^ givenTan); if ((givenNrm & curveNrm) >= scalar(0)) { nrmOrientation_ = ALIGNED; } else { nrmOrientation_ = OPPOSED; } Info<< "Initial nrmOrientation after comparison to NURBS u = 0 nrm : " << nrmOrientation_ << endl; } void NURBS3DCurve::setNrm2DOrientation ( const vector& givenNrm, const scalar zVal ) { givenInitNrm_ = givenNrm; const vector tan(curveDerivativeU(Zero)); vector curveNrm(Zero); curveNrm.x() = -tan.y(); curveNrm.y() = tan.x(); curveNrm.z() = zVal; if ((givenNrm & curveNrm) >= scalar(0)) { nrmOrientation_ = ALIGNED; } else { nrmOrientation_ = OPPOSED; } Info<< "Initial nrmOrientation after comparison to NURBS u = 0 nrm : " << nrmOrientation_ << endl; } void NURBS3DCurve::flipNrmOrientation() { if (nrmOrientation_ == ALIGNED) { nrmOrientation_ = OPPOSED; } else { nrmOrientation_ = ALIGNED; } } void NURBS3DCurve::setCPs(const List& CPs) { CPs_ = CPs; } void NURBS3DCurve::setWeights(const List& weights) { weights_ = weights; } void NURBS3DCurve::setName(const word& name) { name_ = name; } void NURBS3DCurve::buildCurve() { const label degree(basis_.degree()); // Iterate over the CPs of this curve. forAll(*this, ptI) { this->operator[](ptI) = vector::zero; const scalar u(u_[ptI]); // Compute denominator. scalar denom(Zero); forAll(CPs_, CPJ) { denom += basis_.basisValue(CPJ, degree, u)*weights_[CPJ]; } // Compute curve point. forAll(CPs_, CPI) { this->operator[](ptI) += CPs_[CPI] * basis_.basisValue(CPI, degree, u) * weights_[CPI]/denom; } } } void NURBS3DCurve::invert() { Info<< "Inverting NURBS curve " << name_ << endl; const label nCPs(CPs_.size()); List invertedCPs(nCPs, Zero); List invertedWeights(nCPs, Zero); for (label CPI = 0; CPIsize()-1)); vector xu(curvePoint(u)); scalar res(GREAT); do { vector dxdu(curveDerivativeU(u)); vector d2xdu2(curveDerivativeUU(u)); scalar lhs((dxdu&dxdu) + ((xu - targetPoint) & d2xdu2)); scalar rhs(-((xu - targetPoint) & dxdu)); // Update parametric coordinate and compute new point and // bound param coordinates if needed. // Compute residual. u += rhs/lhs; bound(u); xu = curvePoint(u); res = mag((xu - targetPoint) & curveDerivativeU(u)); //Info<< "dxdu" << dxdu << endl; //Info<< "d2xdu2" << d2xdu2 << endl; //Info<< "Iteration " << iter << ", Residual " << res << endl; } while ((iter++< maxIter) && (res > tolerance)); /* // debug info Info<< "Residual after " << iter << " iterations : " << res << "\nparametric coordinate " << u << "\ncurve point closest to target " << xu << "\ntarget being " << targetPoint << endl; */ // warning if method did not reach threshold if (iter > maxIter) { WarningInFunction << "Finding curve point closest to " << targetPoint << " failed." << endl; } return u; } scalar NURBS3DCurve::findClosestCurvePoint ( const vector& targetPoint, const scalar initGuess, const label maxIter, const scalar tolerance ) { // Loop through curve points to find the closest one for initialization. label iter(0); scalar u(initGuess); vector xu(curvePoint(u)); scalar res(GREAT); do { vector dxdu(curveDerivativeU(u)); vector d2xdu2(curveDerivativeUU(u)); scalar lhs((dxdu&dxdu) + ((xu - targetPoint) & d2xdu2)); scalar rhs(-((xu - targetPoint) & dxdu)); // Update parametric coordinate and compute new point and // bound param coordinates if needed. // Compute residual. u += rhs/lhs; bound(u); xu = curvePoint(u); res = mag((xu - targetPoint) & curveDerivativeU(u)); //Info<< "dxdu" << dxdu << endl; //Info<< "d2xdu2" << d2xdu2 << endl; //Info<< "bound u " << u << endl; //Info<< "u " << u << endl; //Info<< "Iteration " << iter << ", Residual " << res << endl; } while ((iter++< maxIter) && (res > tolerance)); /* // debug info Info<< "Residual after " << iter << " iterations : " << res << "\nparametric coordinate " << u << "\ncurve point closest to target " << xu << "\ntarget being " << targetPoint << endl; */ // warning if method did not reach threshold if (iter > maxIter) { WarningInFunction << "Finding curve point closest to " << targetPoint << " failed." << endl; } return u; } const vector NURBS3DCurve::nrm3D(const vector& refTan, const scalar u) const { vector curveNrm(Zero); if (nrmOrientation_ == ALIGNED) { curveNrm = curveDerivativeU(u) ^ refTan; } else { curveNrm = refTan ^ curveDerivativeU(u); } curveNrm.normalise(); return curveNrm; } const vector NURBS3DCurve::nrm2D(const scalar zVal, const scalar u) const { const vector tan(curveDerivativeU(u)); vector curveNrm(Zero); curveNrm.x() = -nrmOrientation_*tan.y(); curveNrm.y() = nrmOrientation_*tan.x(); curveNrm.z() = zVal; curveNrm /= mag(curveNrm); return curveNrm; } void NURBS3DCurve::insertKnot ( const scalarField& oldKnots, const scalar uBar, const label kInsert ) { // Get the req ref info. // Insertion into curve of non-uniform weight is not currently supported. const label degree(basis_.degree()); const label nCPs(basis_.nCPs()); List newCPs(nCPs, Zero); List newWeights(nCPs, scalar(1)); // Compute the new CPs and their weights. for (label CPI = 0; CPI < (kInsert - degree + 1); CPI++) { newCPs[CPI] = CPs_[CPI]; } for (label CPI = (kInsert - degree + 1); CPI < (kInsert + 1); CPI++) { const scalar uIOld(oldKnots[CPI]); const scalar uIDOld(oldKnots[CPI + degree]); const scalar ratio((uBar - uIOld) /(uIDOld - uIOld)); newCPs[CPI] = (ratio*CPs_[CPI] + (1 - ratio)*CPs_[CPI - 1]); } for (label CPI= (kInsert + 1); CPI