diff --git a/Geo/MElement.cpp b/Geo/MElement.cpp index e50141e44f52de1ca24223d9954e61eb15d40e16..72e235675bc94f5bf7bc4ecdd1063a689e5461f9 100644 --- a/Geo/MElement.cpp +++ b/Geo/MElement.cpp @@ -202,12 +202,12 @@ double MElement::rhoShapeMeasure() double MElement::metricShapeMeasure() { - return MetricBasis::minRCorner(this); + return MetricBasis::getMinRCorner(this); } double MElement::metricShapeMeasure2() { - return MetricBasis::boundMinR(this); + return MetricBasis::getMinR(this); } double MElement::maxDistToStraight() const diff --git a/Numeric/ElementType.cpp b/Numeric/ElementType.cpp index 85154f66fbf576a3394401bd70c2c87a47bbf0e1..2310ac62994a37ea9b7652b82bde28d9c7afcd8a 100644 --- a/Numeric/ElementType.cpp +++ b/Numeric/ElementType.cpp @@ -562,3 +562,7 @@ int ElementType::getTag(int parentTag, int order, bool serendip) default : Msg::Warning("unknown element type %i, returning 0", parentTag); return 0; } } + +int ElementType::getPrimaryTag(int tag) { + return getTag(ParentTypeFromTag(tag), 1); +} diff --git a/Numeric/ElementType.h b/Numeric/ElementType.h index c8e2ef955a89328d162460f52359975aaff6a9e9..137be60453e5b8340dd28266c6b79e0822f9d66a 100644 --- a/Numeric/ElementType.h +++ b/Numeric/ElementType.h @@ -21,6 +21,9 @@ namespace ElementType // Give element tag from type, order & serendip int getTag(int parentTag, int order, bool serendip = false); + + // Give first order element tag + int getPrimaryTag(int tag); } #endif diff --git a/Numeric/MetricBasis.cpp b/Numeric/MetricBasis.cpp index e651597fc568a9cece0df5410811039bc8f6839b..a7b26661a5a4b82b43a32284aad20a79465c0561 100644 --- a/Numeric/MetricBasis.cpp +++ b/Numeric/MetricBasis.cpp @@ -6,22 +6,40 @@ #include "MElement.h" #include "AnalyseCurvedMesh.h" #include "MetricBasis.h" +#include "bezierBasis.h" +#include "JacobianBasis.h" #include "BasisFactory.h" #include "pointsGenerators.h" +#include "MHexahedron.h" #include "OS.h" #include <queue> #include <sstream> #include <limits> +#include <iomanip> +#include <cmath> double MetricBasis::_tol = 1e-3; +//TODO Renommer fonctions plus explicitement (minmaxA,...) et rendre statique certaines fonctions + namespace { double cubicCardanoRoot(double p, double q) { + // solve the equation t^3 + p*t + q = 0 double A = q*q/4 + p*p*p/27; if (A > 0) { double sq = std::sqrt(A); - return std::pow(-q/2+sq, 1/3.) + std::pow(-q/2-sq, 1/3.); + double t1 = -q/2+sq, t2 = -q/2-sq;; + + if (t1 < 0) + t1 = -std::pow(-t1, 1/3.); + else + t1 = std::pow(t1, 1/3.); + if (t2 < 0) + t2 = -std::pow(-t2, 1/3.); + else + t2 = std::pow(t2, 1/3.); + return t1 + t2; } else { double module = std::sqrt(-p*p*p/27); @@ -58,6 +76,7 @@ MetricBasis::MetricBasis(int tag) : _jacobian(NULL), _type(ElementType::ParentTypeFromTag(tag)), _dim(ElementType::DimensionFromTag(tag)) { + //Msg::Fatal("Verifie si cette fonction est ok 1"); const bool serendip = false; const int metOrder = metricOrder(tag); const int jacOrder = 3*metOrder/2; @@ -69,7 +88,6 @@ MetricBasis::MetricBasis(int tag) : else metricSpace = new FuncSpaceData(true, tag, false, metOrder+2, metOrder, &serendip); - _gradients = BasisFactory::getGradientBasis(*metricSpace); _bezier = BasisFactory::getBezierBasis(*metricSpace); delete metricSpace; @@ -80,10 +98,15 @@ MetricBasis::MetricBasis(int tag) : jacSpace = new FuncSpaceData(true, tag, jacOrder, &serendip); } else if (_type == TYPE_PYR) { + // The square of the jacobian must be in the same space than the cube of + // the metric, so +3 jacSpace = new FuncSpaceData(true, tag, false, jacOrder+3, jacOrder, &serendip); } - else if (_type != TYPE_TRI && _type != TYPE_QUA) + else if (_type == TYPE_TRI || _type == TYPE_QUA) { + // jacobian not needed + } + else Msg::Fatal("metric not implemented for element tag %d", tag); if (jacSpace) { @@ -94,59 +117,125 @@ MetricBasis::MetricBasis(int tag) : _fillInequalities(metOrder); } -double MetricBasis::boundMinR(MElement *el) +double MetricBasis::getMinR(MElement *el) { + /*static int a = 0; + if (++a == 1) { + double pinf = 1/.0; + double minf = -1/.0; + double mynan = .0/.0; + Msg::Info("pinf=%g minf=%g mynan=%g", pinf, minf, mynan); + Msg::Info(". == . => %d %d %d", pinf == pinf, minf == minf, mynan == mynan); + Msg::Info("./. => %g %g %g", pinf/pinf, minf/minf, mynan/mynan); + Msg::Info("1. < . => %d %d %d", 1. < pinf, 1. < minf, 1. < mynan); + Msg::Info("1. > . => %d %d %d", 1. > pinf, 1. > minf, 1. > mynan); + Msg::Info("max => %g %g %g", std::max(1., pinf), std::max(1., minf), std::max(1., mynan)); + Msg::Info("min => %g %g %g", std::min(1., pinf), std::min(1., minf), std::min(1., mynan)); + Msg::Info("inv => %g %g %g", 1./pinf, 1./minf, 1./mynan); + Msg::Info("pinf>. => %d %d %d", pinf > pinf, pinf > minf, pinf > mynan); + Msg::Info(" "); + double giant = std::numeric_limits<double>::max(); + double infinity = std::numeric_limits<double>::infinity(); + Msg::Info("giant=%g, giant*10=%g infinity=%g", giant, giant*10, infinity); + Msg::Info("a=pinf, (a - 1) / (a + 1) => %g/%g => %g", pinf - 1, pinf + 1, (pinf - 1) / (pinf + 1)); + Msg::Info("a=giant, (a - 1) / (a + 1) => %g/%g => %g", giant - 1, giant + 1, (giant - 1) / (giant + 1)); + Msg::Info("giant*10 == infinity => %d", giant*10 == std::numeric_limits<double>::infinity()); + Msg::Info("sqrt(pinf) => %g", std::sqrt(pinf)); + Msg::Info("!(mynan >= 1) => %d, (mynan < 1) => %d", !(mynan >= 1), mynan < 1); + }*/ + MetricBasis *metric = (MetricBasis*)BasisFactory::getMetricBasis(el->getTypeForMSH()); - return metric->getBoundMinR(el); + double m = metric->computeMinR(el); + //Msg::Info("returning %g", m); + return m; } -double MetricBasis::minRCorner(MElement *el) +double MetricBasis::computeMinR(MElement *el) const { - int tag = el->getTypeForMSH(); - int order = 1; - if (el->getType() == TYPE_TRI || el->getType() == TYPE_TET) order = 0; - - const GradientBasis *gradients; - const JacobianBasis *jacobian; - if (el->getType() != TYPE_PYR) { - gradients = BasisFactory::getGradientBasis(tag, order); - jacobian = BasisFactory::getJacobianBasis(tag, order); - } - else { - const bool serendip = false; - FuncSpaceData fsd(true, tag, false, 1, 0, &serendip); - gradients = BasisFactory::getGradientBasis(fsd); - jacobian = BasisFactory::getJacobianBasis(fsd); - } - - int nSampPnts = jacobian->getNumJacNodes(); - if (el->getType() == TYPE_PYR) nSampPnts = 4; - int nMapping = gradients->getNumMapNodes(); + int nSampPnts = _gradients->getNumSamplingPoints(); + int nMapping = _gradients->getNumMapNodes(); // Nodes fullMatrix<double> nodes(nMapping, 3); el->getNodesCoord(nodes); // Jacobian coefficients - fullVector<double> jacLag(jacobian->getNumJacNodes()); - jacobian->getSignedJacobian(nodes, jacLag); + fullVector<double> *jac = NULL; + if (_jacobian) { + fullVector<double> jacLag(_jacobian->getNumJacNodes()); + jac = new fullVector<double>(_jacobian->getNumJacNodes()); + _jacobian->getSignedIdealJacobian(nodes, jacLag); + _jacobian->lag2Bez(jacLag, *jac); + } // Metric coefficients fullMatrix<double> metCoeffLag; - _fillCoeff<false>(el->getDim(), gradients, nodes, metCoeffLag); + _fillCoeff<true>(el->getDim(), _gradients, nodes, metCoeffLag); + fullMatrix<double> *metCoeff; + metCoeff = new fullMatrix<double>(nSampPnts, metCoeffLag.size2()); + _bezier->matrixLag2Bez.mult(metCoeffLag, *metCoeff); - // Compute min_corner(R) - return _computeMinlagR(jacLag, metCoeffLag, nSampPnts); + /*static double inf = std::numeric_limits<double>::infinity(); + double minq = inf, maxq = -inf; + for (int k = 0; k < nSampPnts; ++k) { + minq = std::min(minq, (*metCoeff)(k, 0)); + maxq = std::max(maxq, (*metCoeff)(k, 0)); + } + double minp = 0, maxp = 0; + for (int i = 1; i < 7; ++i) { + double min = inf, max = -inf; + for (int k = 0; k < nSampPnts; ++k) { + min = std::min(min, (*metCoeff)(k, i)); + max = std::max(max, (*metCoeff)(k, i)); + } + if (min < 0 && max < 0) { + double tmp = min; + min = -max; + max = -tmp; + } + minp += min*min; + maxp += max*max; + } + minp = std::sqrt(minp); + maxp = std::sqrt(maxp); + double mina = minq/maxp; + double maxa = maxq/minp; + if (maxa-mina < _tol) { + return getMinSampledR(el, 0); + }*/ + + // Compute min(R, _tol) + double RminLag, RminBez; + //Msg::Info("element %d", el->getNum()); + _computeBoundsRmin(*metCoeff, *jac, RminLag, RminBez); + + //statsForMatlab(el, 20, NULL); + + //Msg::Info("%.10g %.10g", RminBez, RminLag); + + if (RminLag-RminBez < _tol) { + delete jac; + delete metCoeff; + return RminBez; + } + else { + //Msg::Info("Subdivision (%g vs %g)", RminLag, RminBez); + MetricData *md2 = new MetricData(metCoeff, jac, RminBez, 0, 0); + double minBez = _subdivideForRmin(md2, RminLag, _tol, el); + //Msg::Info("bez lag %.10g %.10g", minBez, RminLag); + return minBez; + } } -double MetricBasis::minSampledR(MElement *el, int order) +double MetricBasis::getMinSampledR(MElement *el, int order) { MetricBasis *metric = (MetricBasis*)BasisFactory::getMetricBasis(el->getTypeForMSH()); - return metric->getMinSampledR(el, order); + return metric->computeMinSampledR(el, order); } -double MetricBasis::getMinSampledR(MElement *el, int deg) const +double MetricBasis::computeMinSampledR(MElement *el, int deg) const { + //Msg::Fatal("Verifie si cette fonction est ok b"); fullMatrix<double> samplingPoints; bool serendip = false; gmshGeneratePoints(FuncSpaceData(el, deg, &serendip), samplingPoints); @@ -170,49 +259,118 @@ double MetricBasis::getMinSampledR(MElement *el, int deg) const return min; } -double MetricBasis::getBoundMinR(MElement *el) const +double MetricBasis::getMaxSampledR(MElement *el, int order) { - int nSampPnts = _gradients->getNumSamplingPoints(); - int nMapping = _gradients->getNumMapNodes(); + MetricBasis *metric = (MetricBasis*)BasisFactory::getMetricBasis(el->getTypeForMSH()); + return metric->computeMaxSampledR(el, order); +} + +double MetricBasis::computeMaxSampledR(MElement *el, int deg) const +{ + fullMatrix<double> samplingPoints; + bool serendip = false; + gmshGeneratePoints(FuncSpaceData(el, deg, &serendip), samplingPoints); + + MetricData *md; + _getMetricData(el, md); + + fullMatrix<double> R; + interpolate(el, md, samplingPoints, R); + + if (R.size1() < 1) { + delete md; + return -1; + } + + double max = R(0, 1); + for (int i = 1; i < R.size1(); ++i) + max = std::max(max, R(i, 1)); + + delete md; + return max; +} + +void MetricBasis::lag2Bez(const fullMatrix<double> &metCoeffLag, + fullMatrix<double> &metCoeffBez) const +{ + _bezier->matrixLag2Bez.mult(metCoeffLag, metCoeffBez); +} + +double MetricBasis::getMinSampledGlobalRatio(MElement *el, int order) +{ + MetricBasis *metric = (MetricBasis*)BasisFactory::getMetricBasis(el->getTypeForMSH()); + return metric->computeMinSampledGlobalRatio(el, order); +} + +double MetricBasis::computeMinSampledGlobalRatio(MElement *el, int deg) const +{ + Msg::Fatal("Verifie si cette fonction est ok d"); + fullMatrix<double> samplingPoints; + bool serendip = false; + gmshGeneratePoints(FuncSpaceData(el, deg, &serendip), samplingPoints); + + MetricData *md; + _getMetricData(el, md); + + fullMatrix<double> R; + interpolate(el, md, samplingPoints, R); + + double min = R(0, 2); + double max = R(0, 3); + for (int i = 1; i < R.size1(); ++i) { + min = std::min(min, R(i, 2)); + max = std::max(max, R(i, 3)); + } + + //Msg::Info("%g %g", min, max); + + delete md; + return std::sqrt(min/max); +} + +double MetricBasis::getMinRCorner(MElement *el) +{ + Msg::Fatal("Verifie si cette fonction est ok e"); + int tag = el->getTypeForMSH(); + int order = 1; + if (el->getType() == TYPE_TRI || el->getType() == TYPE_TET) order = 0; + + const GradientBasis *gradients; + const JacobianBasis *jacobian; + if (el->getType() != TYPE_PYR) { + gradients = BasisFactory::getGradientBasis(tag, order); + jacobian = BasisFactory::getJacobianBasis(tag, order); + } + else { + const bool serendip = false; + FuncSpaceData fsd(true, tag, false, 1, 0, &serendip); + gradients = BasisFactory::getGradientBasis(fsd); + jacobian = BasisFactory::getJacobianBasis(fsd); + } + + int nSampPnts = jacobian->getNumJacNodes(); + if (el->getType() == TYPE_PYR) nSampPnts = 4; + int nMapping = gradients->getNumMapNodes(); // Nodes fullMatrix<double> nodes(nMapping, 3); el->getNodesCoord(nodes); // Jacobian coefficients - fullVector<double> *jac = NULL; - if (_jacobian) { - fullVector<double> jacLag(_jacobian->getNumJacNodes()); - jac = new fullVector<double>(_jacobian->getNumJacNodes()); - _jacobian->getSignedIdealJacobian(nodes, jacLag); - _jacobian->lag2Bez(jacLag, *jac); - } + fullVector<double> jacLag(jacobian->getNumJacNodes()); + jacobian->getSignedJacobian(nodes, jacLag); // Metric coefficients fullMatrix<double> metCoeffLag; - _fillCoeff<true>(el->getDim(), _gradients, nodes, metCoeffLag); - fullMatrix<double> *metCoeff; - metCoeff = new fullMatrix<double>(nSampPnts, metCoeffLag.size2()); - _bezier->matrixLag2Bez.mult(metCoeffLag, *metCoeff); - - // Compute min(R, _tol) - double RminLag, RminBez; - _computeRmin(*metCoeff, *jac, RminLag, RminBez); + _fillCoeff<false>(el->getDim(), gradients, nodes, metCoeffLag); - if (RminLag-RminBez < MetricBasis::_tol) { - delete jac; - delete metCoeff; - return RminBez; - } - else { - MetricData *md2 = new MetricData(metCoeff, jac, RminBez, 0, 0); - double tt = _subdivideForRmin(md2, RminLag, MetricBasis::_tol); - return tt; - } + // Compute min_corner(R) + return _computeMinlagR(jacLag, metCoeffLag, nSampPnts); } void MetricBasis::_fillInequalities(int metricOrder) { + //Msg::Fatal("Verifie si cette fonction est ok"); if (_type == TYPE_PYR) { _fillInequalitiesPyr(metricOrder); return; @@ -301,10 +459,7 @@ void MetricBasis::_fillInequalities(int metricOrder) for (int l = 0; l < dim; l++) { hash += (exp(i, l)+exp(j, l)+exp(k, l)) * pow_int(3*metricOrder+1, l); } - if (j == k && j != i) - _ineqP3[hash].push_back(IneqData(num/den, k, j, i)); - else - _ineqP3[hash].push_back(IneqData(num/den, i, j, k)); + _ineqP3[hash].push_back(IneqData(num/den, i, j, k)); } } } @@ -361,6 +516,7 @@ void MetricBasis::_fillInequalities(int metricOrder) void MetricBasis::_fillInequalitiesPyr(int metricOrder) { + //Msg::Fatal("Verifie si cette fonction est ok"); fullMatrix<int> exp(_bezier->_exponents.size1(), _bezier->_exponents.size2()); for (int i = 0; i < _bezier->_exponents.size1(); ++i) { for (int j = 0; j < _bezier->_exponents.size2(); ++j) { @@ -424,10 +580,7 @@ void MetricBasis::_fillInequalitiesPyr(int metricOrder) for (int l = 0; l < 3; l++) { hash += (exp(i, l)+exp(j, l)+exp(k, l)) * pow_int(3*metricOrder+1, l); } - if (j == k && j != i) - _ineqP3[hash].push_back(IneqData(num/den, k, j, i)); - else - _ineqP3[hash].push_back(IneqData(num/den, i, j, k)); + _ineqP3[hash].push_back(IneqData(num/den, i, j, k)); } } } @@ -471,6 +624,7 @@ void MetricBasis::_fillInequalitiesPyr(int metricOrder) void MetricBasis::_lightenInequalities(int &countj, int &countp, int &counta) { + //Msg::Fatal("Verifie si cette fonction est ok"); double tol = .0; std::map<int, std::vector<IneqData> >::iterator it, itbeg[3], itend[3]; @@ -506,10 +660,74 @@ void MetricBasis::_lightenInequalities(int &countj, int &countp, int &counta) bool MetricBasis::validateBezierForMetricAndJacobian() { + Msg::Fatal("Verifie si cette fonction est ok f"); Msg::Info("Testing Bezier interpolation and subdivision " "for jacobien and metric on all element types..."); int numError = 0; + // Parameters + const int numElem = 10000000; //////// + + MElement **elements; + elements = new MElement*[numElem]; + + for (int tag = 1; tag <= MSH_NUM_TYPE; ++tag) { + if (tag != 9) continue; //////// + const int numPrimNode = 3; //////// + + const int order = ElementType::OrderFromTag(tag); + const int dim = ElementType::DimensionFromTag(tag); + const int primTag = ElementType::getPrimaryTag(tag); + + Msg::Info("... testing element tag %d", tag); + + // Get reference nodes + const nodalBasis *mapBasis = BasisFactory::getNodalBasis(tag); + fullMatrix<double> nodes; + mapBasis->getReferenceNodes(nodes); + + // Create 'numElem' elements more and more randomized + for (int iel = 0; iel < numElem; ++iel) { + const double range = static_cast<double>(iel) / (numElem-1) / order; + const double rangePrim = static_cast<double>(iel/2000) / ((numElem/2000)-1) / order; //////// + const double rangeSub = static_cast<double>(iel%2000) / 1999 / order; //////// + if (!(iel%200)) Msg::Info("%g %g", rangePrim, rangeSub); + std::vector<MVertex*> vertices(nodes.size1()); + for (int i = 0; i < numPrimNode; ++i) { + vertices[i] = new MVertex(nodes(i, 0) + symRand(rangePrim), + nodes(i, 1) + symRand(rangePrim), + nodes(i, 2) + symRand(rangePrim)); + } + MElement *primEl = MElement::createElement(primTag, vertices); + + for (int i = numPrimNode; i < nodes.size1(); ++i) { + SPoint3 p; + primEl->pnt(nodes(i, 0), nodes(i, 1), nodes(i, 2), p); + vertices[i] = new MVertex(p.x() + symRand(rangeSub), + p.y() + symRand(rangeSub), + p.z() + symRand(rangeSub)); + } + MElement *el = MElement::createElement(tag, vertices); + if (!el) { + Msg::Error("MElement was unable to create element for tag %d", tag); + ++numError; + } + + elements[iel] = el; + } + GMSH_AnalyseCurvedMeshPlugin plugin = GMSH_AnalyseCurvedMeshPlugin(); + plugin.test(elements, numElem, dim); + } + + + //================= + return 987; + //================= + + /*Msg::Info("Testing Bezier interpolation and subdivision " + "for jacobien and metric on all element types..."); + int numError = 0; + // Parameters const int numType = 6; const int acceptedTypes[numType] = {TYPE_TRI, TYPE_QUA, TYPE_TET, @@ -566,7 +784,7 @@ bool MetricBasis::validateBezierForMetricAndJacobian() dim > 2 ? nodes(i, 2) + symRand(range) : 0); } MElement *el = MElement::createElement(tag, vertices); - if (el != NULL) { + if (!el) { Msg::Error("MElement was unable to create element for tag %d", tag); ++numError; } @@ -578,7 +796,7 @@ bool MetricBasis::validateBezierForMetricAndJacobian() if (numError) Msg::Error("Validation of Bezier terminated with %d errors!", numError); else Msg::Info("Validation of Bezier terminated without errors"); - return numError; + return numError;*/ } int MetricBasis::validateBezierForMetricAndJacobian(MElement *el, @@ -587,6 +805,7 @@ int MetricBasis::validateBezierForMetricAndJacobian(MElement *el, double toleranceTensor, double tolerance) { + Msg::Fatal("Verifie si cette fonction est ok g"); const int tag = el->getTypeForMSH(); const MetricBasis *metricBasis = BasisFactory::getMetricBasis(tag); @@ -638,12 +857,13 @@ void MetricBasis::interpolate(const MElement *el, const fullMatrix<double> &nodes, fullMatrix<double> &R) const { + //Msg::Fatal("Verifie si cette fonction est ok h"); fullMatrix<double> &metcoeffs = *md->_metcoeffs, *metric = new fullMatrix<double>; fullVector<double> &jaccoeffs = *md->_jaccoeffs, *jac = new fullVector<double>; _bezier->interpolate(metcoeffs, nodes, *metric); - R.resize(nodes.size1(), 2); + R.resize(nodes.size1(), 4); switch (metcoeffs.size2()) { case 1: @@ -652,6 +872,8 @@ void MetricBasis::interpolate(const MElement *el, break; case 3: + ((MetricBasis*)this)->file << "q p myR eigR" << std::endl; + ((MetricBasis*)this)->file << _bezier->getNumLagCoeff() << std::endl; for (int i = 0; i < R.size1(); ++i) { // Compute from q, p double p = pow((*metric)(i, 1), 2); @@ -667,10 +889,18 @@ void MetricBasis::interpolate(const MElement *el, fullMatrix<double> vecLeft(2, 2), vecRight(2, 2); metricTensor.eig(valReal, valImag, vecLeft, vecRight, true); R(i, 1) = std::sqrt(valReal(0) / valReal(1)); + R(i, 2) = valReal(0); + R(i, 3) = valReal(1); + ((MetricBasis*)this)->file << (*metric)(i, 0) << " "; + ((MetricBasis*)this)->file << p << " "; + ((MetricBasis*)this)->file << R(i, 0) << " "; + ((MetricBasis*)this)->file << R(i, 1) << std::endl; } break; case 7: + ((MetricBasis*)this)->file << "a K myR eigR" << std::endl; + ((MetricBasis*)this)->file << _bezier->getNumLagCoeff() << std::endl; _jacobian->getBezier()->interpolate(jaccoeffs, nodes, *jac); for (int i = 0; i < R.size1(); ++i) { // Compute from q, p, J @@ -692,6 +922,12 @@ void MetricBasis::interpolate(const MElement *el, fullMatrix<double> vecLeft(3, 3), vecRight(3, 3); metricTensor.eig(valReal, valImag, vecLeft, vecRight, true); R(i, 1) = std::sqrt(valReal(0) / valReal(2)); + R(i, 2) = valReal(0); + R(i, 3) = valReal(2); + ((MetricBasis*)this)->file << (*metric)(i, 0)/p << " "; + ((MetricBasis*)this)->file << (*jac)(i)*(*jac)(i)/p/p/p << " "; + ((MetricBasis*)this)->file << R(i, 0) << " "; + ((MetricBasis*)this)->file << R(i, 1) << std::endl; } break; @@ -710,6 +946,7 @@ void MetricBasis::interpolateAfterNSubdivisions( fullMatrix<double> &uvw, fullMatrix<double> &metric) const { + Msg::Fatal("Verifie si cette fonction est ok i"); // Interpolate metric after 'numSub' random subdivision at // 'numPnt' random points. // Return: isub, the subdomain tag of each subdivision, @@ -798,6 +1035,8 @@ void MetricBasis::interpolateAfterNSubdivisions( void MetricBasis::statsForMatlab(MElement *el, int deg, MetricData *md) const { + //return; + //Msg::Fatal("Verifie si cette fonction est ok h"); fullMatrix<double> samplingPoints; bool serendip = false; gmshGeneratePoints(FuncSpaceData(el, deg, &serendip), samplingPoints); @@ -805,7 +1044,7 @@ void MetricBasis::statsForMatlab(MElement *el, int deg, MetricData *md) const if (!md) _getMetricData(el, md); static unsigned int aa = 0; - if (md->_num < 100 && ++aa < 200) { + if (md->_num < 100000 && ++aa < 1000) { std::stringstream name; name << "metricStat_" << el->getNum() << "_"; name << (md->_num % 10); @@ -817,42 +1056,47 @@ void MetricBasis::statsForMatlab(MElement *el, int deg, MetricData *md) const ((MetricBasis*)this)->file.open(name.str().c_str(), std::fstream::out); { - int dim = el->getDim(); + ((MetricBasis*)this)->file << "mina minKfast minKsharp cmin beta_m beta_M beta c_m" << std::endl; + double mina, maxa, minKs, minKf, c, beta_m, beta_M, beta, c_m; + fullMatrix<double> *coeff = md->_metcoeffs; fullVector<double> *jac = md->_jaccoeffs; - - double mina, maxa, minK; _minMaxA(*coeff, mina, maxa); - double dRda, term1, phip, beta; - double maxaPos, maxaNeg; - double maxKfast, maxKsharp; - if (dim == 3) { - _minK(*coeff, *jac, minK); - double tmpa = mina, tmpK = minK; - _computeTermBeta(tmpa, tmpK, dRda, term1, phip); - beta = -3 * mina*mina * term1 / dRda / 6; - - _maxAstKneg(*coeff, *jac, minK, beta, maxaPos); - _maxAstKpos(*coeff, *jac, minK, beta, maxaNeg); - - _maxKstAfast(*coeff, *jac, mina, beta, maxKfast); - _maxKstAsharp(*coeff, *jac, mina, beta, maxKsharp); + if (el->getDim() == 3) { + _minKfast(*coeff, *jac, minKf); + _minKsharp(*coeff, *jac, minKs); + double minK = minKf; + _moveInsideDomain(mina, minK, true); + double p_dRda, p_dRdK; + _computePseudoDerivatives(mina, minK, p_dRda, p_dRdK); + double slope = -p_dRda/p_dRdK; + c = .5*(3*minK/mina - slope); + _computeBoundingCurve(*coeff, *jac, beta_m, c, true); + _computeBoundingCurve(*coeff, *jac, beta_M, c, false); + beta = -p_dRda/(p_dRdK*3*mina*mina); + if (beta < 0) + _computeLowerBoundC(*coeff, *jac, beta, c_m); + else + c_m = -1; } else { - minK = dRda = term1 = phip = beta = maxaPos = maxaNeg = maxKfast = maxKsharp = -1; + minKs = minKf = c = beta_m = beta_M = beta = c_m = -1; } - ((MetricBasis*)this)->file << mina << " " << maxa << " " << minK << " "; - ((MetricBasis*)this)->file << dRda << " " << term1 << " " << phip << " "; - ((MetricBasis*)this)->file << beta << " "; - ((MetricBasis*)this)->file << maxaPos << " " << maxaNeg << " "; - ((MetricBasis*)this)->file << maxKfast << " " << maxKsharp << std::endl; + ((MetricBasis*)this)->file << std::setprecision(15); + ((MetricBasis*)this)->file << mina << " " << minKf << " " << minKs << " "; + ((MetricBasis*)this)->file << c << " " << beta_m << " " << beta_M << " "; + ((MetricBasis*)this)->file << beta << " " << c_m << std::endl; + ((MetricBasis*)this)->file << std::setprecision(8); } } fullMatrix<double> R; interpolate(el, md, samplingPoints, R); + ((MetricBasis*)this)->file.close(); + + return; if (R.size1() < 1) { ((MetricBasis*)this)->file << -1 << " "; @@ -893,139 +1137,124 @@ int MetricBasis::metricOrder(int tag) } } -void MetricBasis::_computeRmin( +void MetricBasis::_computeBoundsRmin( const fullMatrix<double> &coeff, const fullVector<double> &jac, - double &RminLag, double &RminBez) const + double &sqrtRminLag, double &sqrtRminBez) const { - RminLag = _computeMinlagR(jac, coeff, _bezier->getNumLagCoeff()); - if (RminLag == 0) { - RminBez = 0; + sqrtRminLag = _computeMinlagR(jac, coeff, _bezier->getNumLagCoeff()); + if (sqrtRminLag <= _tol) { + sqrtRminBez = .0; return; } if (coeff.size2() == 3) { // 2d element double mina, dummy; _minMaxA(coeff, mina, dummy); - RminBez = std::sqrt(_R2Dsafe(mina)); - return; - } - - double minK; - _minK(coeff, jac, minK); - if (minK < 1e-10) { - RminBez = 0; + sqrtRminBez = std::sqrt(_R2Dsafe(mina)); return; } - double mina, dummy; + double minK, mina, dummy; + _minKfast(coeff, jac, minK); _minMaxA(coeff, mina, dummy); - double term1, dRda, phip; - _computeTermBeta(mina, minK, dRda, term1, phip); + _moveInsideDomain(mina, minK, true); - if (dRda < 0) { - // TODO : better am ? - double amApprox, da; + double p_dRda, p_dRdK; + _computePseudoDerivatives(mina, minK, p_dRda, p_dRdK); + + if (p_dRda < 0) { + //Msg::Info("case 1 & 2"); + // cases 1 & 2 => compute a lower bounding curve K = beta * a^3 + c * a + // with c such that the curve that pass through (minK, mina) has the + // slope equal to -dRda/dRdK + double slope = -p_dRda/p_dRdK; + double c = .5*(3*minK/mina - slope); + double beta; + _computeBoundingCurve(coeff, jac, beta, c, true); { - const double p = -3; - double q = -minK - 2; - const double a1 = cubicCardanoRoot(p, q); - const double phim = std::acos(-1/a1) - M_PI/3; - q = -minK + 2*std::cos(3*phim); - amApprox = cubicCardanoRoot(p, q); - if (minK < 10) - da = -.3; - else if (minK < 20) - da = -.25; - else if (minK < 35) - da = -.2; - else if (minK < 70) - da = -.15; - else if (minK < 175) - da = -.1; - else - da = -.05; + double beta = (minK/mina-c)/mina/mina; + if (std::abs(p_dRda + p_dRdK * (3*beta*mina*mina+c)) > 1e-12) { + Msg::Error("Derivative along curve not zero %g", p_dRda + p_dRdK * (3*beta*mina*mina+c)); + } } - double beta = -3 * mina*mina * term1 / dRda / 6; - double maxa; - if (beta*minK-mina*mina*mina < 0) - _maxAstKneg(coeff, jac, minK, beta, maxa); + double a_int = mina, K_int = minK; + bool onVertical = _intersectionCurveLeftCorner(beta, c, a_int, K_int); + if (onVertical) { + // then the minimum is on the curve, we prefer to return this + sqrtRminBez = std::sqrt(_R3Dsafe(mina, minK)); + //Msg::Info("onvertical"); + return; + } + /*if (a_int - mina < ???) { + sqrtRminBez = std::sqrt(_R3Dsafe(a_int, minK)); + return; + }*/ + + bool haveToCompute_a0; + if (_moveInsideDomain(a_int, K_int, true)) { + haveToCompute_a0 = true; + } else { - _maxAstKpos(coeff, jac, minK, beta, maxa); - if (maxa < amApprox && beta*minK-maxa*maxa*maxa < 0) - _maxAstKneg(coeff, jac, minK, beta, maxa); - } - - maxa = std::max(mina, maxa); - if (amApprox*amApprox*amApprox+da < maxa*maxa*maxa) { - // compute better am - // - double am0 = std::pow(amApprox*amApprox*amApprox+da, 1/3.); - double am1 = std::pow(amApprox*amApprox*amApprox+da+.05, 1/3.); - //double am0S = am0, am1S = am1; - double am = (am0 + am1)/2; - double R0 = _R3Dsafe(am0, minK); - double R1 = _R3Dsafe(am1, minK); - double Rnew = _R3Dsafe(am, minK); - - int cnt = 0; - while (std::abs(R0-Rnew) > _tol*.01 || std::abs(R1-Rnew) > _tol*.01) { - ++cnt; - if (R0 > R1) { - am0 = am; - R0 = Rnew; - } - else { - am1 = am; - R1 = Rnew; - } - am = (am0 + am1)/2; - Rnew = _R3Dsafe(am, minK); - } - - if (am < maxa) { - RminBez = _R3Dsafe(am, minK); - RminBez = std::sqrt(RminBez); - return; + double p_dRda, p_dRdK; + _computePseudoDerivatives(a_int, K_int, p_dRda, p_dRdK); + if (p_dRda + p_dRdK * (3*beta*a_int*a_int+c) < -1e-5) { + Msg::Error("Derivative along curve not positive or zero %g", p_dRda + p_dRdK * (3*beta*a_int*a_int+c)); + Msg::Info("(%g, %g) vs (%g, %g), diff (%g, %g)", mina, minK, a_int, K_int, a_int-mina, K_int-minK); + double beta2 = (minK/mina-c)/mina/mina; + Msg::Info("beta %g - %g = %g", beta, beta2, beta-beta2); } + haveToCompute_a0 = p_dRda > 0; } - RminBez = _R3Dsafe(maxa, minK); - RminBez = std::sqrt(RminBez); + if (haveToCompute_a0) { + double a0 = _computeAbscissaMinR(mina, minK); + sqrtRminBez = std::sqrt(_R3Dsafe(a0, minK)); + } + else { + sqrtRminBez = std::sqrt(_R3Dsafe(a_int, K_int)); + } return; } - else if (term1 < 0) { - double maxK; - double beta = -3 * mina*mina * term1 / dRda / 6; - if (beta*minK-mina*mina*mina > 0) Msg::Fatal("Arf pas prevu"); - //_maxKstAsharp(coeff, jac, mina, beta, maxK); - _maxKstAfast(coeff, jac, mina, beta, maxK); - const double x = .5*(maxK-mina*mina*mina+3*mina); - const double phimin = std::acos(-1/mina) - M_PI/3; - double myphi; - if (std::abs(x) > 1) { - myphi = phimin; + else if (p_dRdK < 0) { + //Msg::Info("case 4 & 5"); + double slope = -p_dRda/p_dRdK; + double c = .5*(3*minK/mina - slope); + double beta; + _computeBoundingCurve(coeff, jac, beta, c, false); + double a_int = mina, K_int = minK; + + if (_intersectionCurveLeftCorner(beta, c, a_int, K_int)) { + /*if (std::abs(a_int - mina) < _tol / 100 && + std::abs(K_int - minK) < _tol / 100) { + sqrtRminBez = std::sqrt(_R3Dsafe(mina, minK)); + return; + }*/ + // We compute K0 and return _R3Dsafe(mina, min(K0,K_int)) + double K0 = 2*std::cos(3*std::acos(-1/mina)-M_PI) + (mina*mina-3) * mina; + sqrtRminBez = std::sqrt(_R3Dsafe(mina, std::min(K0, K_int))); } else { - const double phimaxK = std::acos(x)/3; - myphi = std::max(phimin, phimaxK); + if (_moveInsideDomain(a_int, K_int, false)) + sqrtRminBez = std::sqrt(_R3Dsafe(a_int, K_int)); + else + sqrtRminBez = std::sqrt(_computeMinRAlongCurve(beta, c, a_int, -1)); } - RminBez = (mina+2*std::cos(myphi+2*M_PI/3))/(mina+2*std::cos(myphi)); - RminBez = std::sqrt(RminBez); return; } else { - RminBez = (mina+2*std::cos(phip+M_PI/3))/(mina+2*std::cos(phip-M_PI/3)); - RminBez = std::sqrt(RminBez); + //Msg::Info("case 3"); + sqrtRminBez = std::sqrt(_R3Dsafe(mina, minK)); return; } } -void MetricBasis::_computeRmax( +void MetricBasis::_computeBoundsRmax( const fullMatrix<double> &coeff, const fullVector<double> &jac, double &RmaxLag) const { + Msg::Fatal("Verifie si cette fonction est ok k"); RmaxLag = 0.; for (int i = 0; i < _bezier->getNumLagCoeff(); ++i) { @@ -1046,8 +1275,9 @@ void MetricBasis::_computeRmax( } } -double MetricBasis::_subdivideForRmin(MetricData *md, double RminLag, double tol, MElement *el) const +double MetricBasis::_subdivideForRmin(MetricData *md, double &RminLag, double tol, MElement *el) const { + //Msg::Fatal("Verifie si cette fonction est ok l"); std::priority_queue<MetricData*, std::vector<MetricData*>, lessMinB> subdomains; const bool for3d = md->_jaccoeffs; const int numCoeff = md->_metcoeffs->size2(); @@ -1058,6 +1288,7 @@ double MetricBasis::_subdivideForRmin(MetricData *md, double RminLag, double tol std::vector<fullVector<double>*> trash; + int k = 0; while (RminLag - subdomains.top()->_RminBez > tol && subdomains.size() < 25000) { fullMatrix<double> *subcoeffs, *coeff; fullVector<double> *subjac, *jac = NULL; @@ -1082,17 +1313,22 @@ double MetricBasis::_subdivideForRmin(MetricData *md, double RminLag, double tol jac->setAsProxy(*subjac, i * numJacPnts, numJacPnts); } double minLag, minBez; - _computeRmin(*coeff, *jac, minLag, minBez); + _computeBoundsRmin(*coeff, *jac, minLag, minBez); RminLag = std::min(RminLag, minLag); int newNum = num + (i+1) * pow_int(10, depth); + if (depth > 8) newNum = 999999999; MetricData *metData = new MetricData(coeff, jac, minBez, depth+1, newNum); + //statsForMatlab(el, 15, metData); //if (el) statsForMatlab(el, 20, metData); subdomains.push(metData); } if (for3d) trash.push_back(subjac); delete subcoeffs; + ++k; } + //Msg::Info("num subdiv %d", k); + double ans = subdomains.top()->_RminBez; while (subdomains.size()) { md = subdomains.top(); @@ -1138,6 +1374,7 @@ template<bool ideal> void MetricBasis::_fillCoeff(int dim, const GradientBasis *gradients, const fullMatrix<double> &nodes, fullMatrix<double> &coeff) { + //Msg::Fatal("Verifie si cette fonction est ok o"); const int nSampPnts = gradients->getNumSamplingPoints(); switch (dim) { @@ -1159,15 +1396,11 @@ void MetricBasis::_fillCoeff(int dim, const GradientBasis *gradients, for (int i = 0; i < nSampPnts; i++) { const double &dxdX = dxydX(i,0), &dydX = dxydX(i,1); const double &dxdY = dxydY(i,0), &dydY = dxydY(i,1); - double dzdX, dzdY; + double dzdX = 0, dzdY = 0; if (nodes.size2() > 2) { dzdX = dxydX(i,2); dzdY = dxydY(i,2); } - else { - dzdX = 0; - dzdY = 0; - } const double dvxdX = dxdX*dxdX + dydX*dydX + dzdX*dzdX; const double dvxdY = dxdY*dxdY + dydY*dydY + dzdY*dzdY; coeff(i, 0) = (dvxdX + dvxdY) / 2; @@ -1218,46 +1451,43 @@ double MetricBasis::_computeMinlagR(const fullVector<double> &jac, for (int i = 0; i < num; ++i) { if (jac(i) <= 0.) return 0; - const double q = coeff(i, 0); + const double &q = coeff(i, 0); double p = 0; for (int k = 1; k < 7; ++k) { p += pow_int(coeff(i, k), 2); } p = std::sqrt(p); - const double a = q/p; - if (a > 1e4) { // TODO: from _tol ? - Rmin = std::min(Rmin, std::sqrt((a - std::sqrt(3.)) / (a + std::sqrt(3.)))); - } - else { - const double tmpR = _R3Dsafe(a, jac(i)/p/p*jac(i)/p); - Rmin = std::min(Rmin, std::sqrt(tmpR)); - } + + Rmin = std::min(Rmin, _R3Dsafe(q, p, jac(i))); } - return Rmin; + break; case 3: for (int i = 0; i < num; ++i) { const double &q = coeff(i, 0); - const double p = pow_int(coeff(i, 1), 2) + pow_int(coeff(i, 2), 2); - const double tmpR = _R2Dsafe(q, std::sqrt(p)); - Rmin = std::min(Rmin, std::sqrt(tmpR)); + double p = pow_int(coeff(i, 1), 2) + pow_int(coeff(i, 2), 2); + p = std::sqrt(p); + + Rmin = std::min(Rmin, _R2Dsafe(q, p)); } - return Rmin; + break; default: Msg::Error("coeff have not right number of column"); return -1; } + + return std::sqrt(Rmin); } -void MetricBasis::_minMaxA( - const fullMatrix<double> &coeff, double &min, double &max) const +void MetricBasis::_minMaxA(const fullMatrix<double> &coeff, + double &mina, double &max) const { - min = std::numeric_limits<double>::max(); - min = 1e10; - //max = 1; // max is not used actually - double minLowBound = -min; + // TODO calculer le max ? + double upperBound = std::numeric_limits<double>::infinity(); + double lowerBound = -upperBound; std::map<int, std::vector<IneqData> >::const_iterator it = _ineqA.begin(); + while (it != _ineqA.end()) { double num = 0; double den = 0; @@ -1271,36 +1501,34 @@ void MetricBasis::_minMaxA( den += it->second[k].val * tmp; num += it->second[k].val * coeff(i, 0) * coeff(j, 0); } - double val = num/den; - if (num < 0) { - if (den > 0) { - _minA(coeff, min); + + // mina has to satisfy: mina^2 * den <= num + if (den == 0) { + if (num >= 0) {} // ok, nothing to do + else {// impossible to satisfy. + _minAfast(coeff, mina); return; } - minLowBound = std::max(val, minLowBound); - } - else if (den > 0) { - min = std::min(val, min); - //max = std::max(val, max); } - ++it; - } + else if (den > 0) + upperBound = std::min(upperBound, num/den); + else + lowerBound = std::max(lowerBound, num/den); - if (min < minLowBound) { - _minA(coeff, min); - return; + ++it; } - min = min > 1 ? std::sqrt(min) : 1; - //max = std::sqrt(max); + if (lowerBound > upperBound) + _minAfast(coeff, mina); + else + mina = upperBound > 1 ? std::sqrt(upperBound) : 1; } -void MetricBasis::_minA(const fullMatrix<double> &coeff, double &mina) const +void MetricBasis::_minAfast(const fullMatrix<double> &coeff, double &mina) const { double minq = coeff(0, 0); - for (int i = 1; i < coeff.size1(); ++i) { - if (minq > coeff(i, 0)) minq = coeff(i, 0); - } + for (int i = 1; i < coeff.size1(); ++i) + minq = std::min(minq, coeff(i, 0)); double maxp = 0; for (int i = 0; i < coeff.size1(); ++i) { @@ -1311,65 +1539,146 @@ void MetricBasis::_minA(const fullMatrix<double> &coeff, double &mina) const maxp = std::max(maxp, tmp); } - mina = minq/maxp; - if (mina < 1) mina = 1; + mina = std::max(1., minq/maxp); // accept +inf as answer } -void MetricBasis::_minK(const fullMatrix<double> &coeff, - const fullVector<double> &jac, double &min) const +void MetricBasis::_minKfast(const fullMatrix<double> &coeff, + const fullVector<double> &jac, double &minK) const { - fullVector<double> r(coeff.size1()); + fullVector<double> P(coeff.size1()); for (int i = 0; i < coeff.size1(); ++i) { - r(i) = 0; + P(i) = 0; for (int l = 1; l < 7; ++l) { - r(i) += coeff(i, l) * coeff(i, l); + P(i) += coeff(i, l) * coeff(i, l); } - r(i) = std::sqrt(r(i)); + P(i) = std::sqrt(P(i)); } - min = 1e10; + double upperBound = std::numeric_limits<double>::infinity(); + double lowerBound = -upperBound; + std::map<int, std::vector<IneqData> >::const_iterator itJ, itP; itJ = _ineqJ2.begin(); itP = _ineqP3.begin(); - if (_ineqP3.size() != _ineqJ2.size()) Msg::Fatal("sizes P3 %d, J2 %d", _ineqP3.size(), _ineqJ2.size()); - //Msg::Warning("sizes %d %d", _ineqJ2.size(), _ineqP3.size()); - int count = 0; - while (itJ != _ineqJ2.end() && itP != _ineqP3.end()) { - if (count >= (int)_ineqJ2.size()) Msg::Fatal("aaargh"); - if (itJ->first != itP->first) Msg::Fatal("not same hash %d %d", itJ->first, itP->first); - + // _ineqJ2 and _ineqP3 have the same size + while (itJ != _ineqJ2.end()) { + //if (itJ->first != itP->first) Msg::Fatal("not same hash %d %d", itJ->first, itP->first); double num = 0; - //Msg::Info("sizej %d", itJ->second.size()); - for (unsigned int l = 0; l < itJ->second.size(); ++l) { - const int i = itJ->second[l].i; - const int j = itJ->second[l].j; - num += itJ->second[l].val * jac(i) * jac(j); + for (unsigned int k = 0; k < itJ->second.size(); ++k) { + const int i = itJ->second[k].i; + const int j = itJ->second[k].j; + num += itJ->second[k].val * jac(i) * jac(j); } double den = 0; - //Msg::Info("sizep %d", itP->second.size()); for (unsigned int l = 0; l < itP->second.size(); ++l) { const int i = itP->second[l].i; const int j = itP->second[l].j; const int k = itP->second[l].k; - //Msg::Info("i%d j%d k%d", i, j, k); - if (l>=itP->second.size()) Msg::Error("l %d/%d", l, itP->second.size()); - if (i>=r.size() || j>=r.size()||k>=r.size() ) Msg::Fatal("i%d j%d k%d /%d (%dl%d)", i, j, k, r.size(), count, l); - den += itP->second[l].val * r(i) * r(j) * r(k); + den += itP->second[l].val * P(i) * P(j) * P(k); } - //Msg::Info("%g/%g = %g", num, den, num/den); - min = std::min(min, num/den); + + // minK has to satisfy: minK * den <= num + if (den == 0) { + if (num >= 0) { + ++itJ; + ++itP; + continue; + } + // otherwise, it's impossible to satisfy. + minK = 0;//TODO c'est mieux de retourner _minKfast(coeff, jac, minK); ? + return; + } + else if (den > 0) + upperBound = std::min(upperBound, num/den); + else + lowerBound = std::max(lowerBound, num/den); + + ++itJ; + ++itP; + } + + if (lowerBound > upperBound) + minK = 0;//TODO c'est mieux de retourner _minKfast(coeff, jac, minK); ? + else + minK = std::max(.0, upperBound); +} + +void MetricBasis::_minKsharp(const fullMatrix<double> &coeff, + const fullVector<double> &jac, double &minK) const +{ + fullVector<double> P(coeff.size1()); + fullMatrix<double> Q(coeff.size1(), coeff.size1()); + for (int i = 0; i < coeff.size1(); ++i) { + P(i) = 0; + for (int l = 1; l < 7; ++l) { + P(i) += coeff(i, l) * coeff(i, l); + } + P(i) = std::sqrt(P(i)); + // fill only the half + for (int j = i; j < coeff.size1(); ++j) { + Q(i, j) = 0; + for (int l = 1; l < 7; ++l) { + Q(i, j) += coeff(i, l) * coeff(j, l); + } + } + } + + double upperBound = std::numeric_limits<double>::infinity(); + double lowerBound = -upperBound; + + std::map<int, std::vector<IneqData> >::const_iterator itJ, itP; + itJ = _ineqJ2.begin(); + itP = _ineqP3.begin(); + + // _ineqJ2 and _ineqP3 have the same size + while (itJ != _ineqJ2.end()) { + double num = 0; + for (unsigned int k = 0; k < itJ->second.size(); ++k) { + const int i = itJ->second[k].i; + const int j = itJ->second[k].j; + num += itJ->second[k].val * jac(i) * jac(j); + } + + double den = 0; + for (unsigned int l = 0; l < itP->second.size(); ++l) { + const int i = itP->second[l].i; + const int j = itP->second[l].j; + const int k = itP->second[l].k; + den += itP->second[l].val * 1/3*(Q(i,j)*P(k)+Q(i,k)*P(j)+Q(j,k)*P(i)); + } + + // minK has to satisfy: minK * den <= num + if (den == 0) { + if (num >= 0) { + ++itJ; + ++itP; + continue; + } + // otherwise, it's impossible to satisfy. + minK = 0;//TODO c'est mieux de retourner _minKfast(coeff, jac, minK); ? + return; + } + else if (den > 0) + upperBound = std::min(upperBound, num/den); + else + lowerBound = std::max(lowerBound, num/den); + ++itJ; ++itP; - ++count; } - min = std::max(min, 0.); + + if (lowerBound > upperBound) + minK = 0;//TODO c'est mieux de retourner _minKfast(coeff, jac, minK); ? + else + minK = std::max(.0, upperBound); } void MetricBasis::_maxAstKpos(const fullMatrix<double> &coeff, const fullVector<double> &jac, double minK, double beta, double &maxa) const { + Msg::Fatal("Verifie si cette fonction est ok"); fullVector<double> P(coeff.size1()); for (int i = 0; i < coeff.size1(); ++i) { P(i) = 0; @@ -1411,6 +1720,7 @@ void MetricBasis::_maxAstKpos(const fullMatrix<double> &coeff, void MetricBasis::_maxAstKneg(const fullMatrix<double> &coeff, const fullVector<double> &jac, double minK, double beta, double &maxa) const { + Msg::Fatal("Verifie si cette fonction est ok"); fullVector<double> P(coeff.size1()); fullMatrix<double> Q(coeff.size1(), coeff.size1()); for (int i = 0; i < coeff.size1(); ++i) { @@ -1462,6 +1772,7 @@ void MetricBasis::_maxAstKneg(const fullMatrix<double> &coeff, void MetricBasis::_maxKstAfast(const fullMatrix<double> &coeff, const fullVector<double> &jac, double mina, double beta, double &maxK) const { + Msg::Fatal("Verifie si cette fonction est ok"); fullVector<double> r(coeff.size1()); for (int i = 0; i < coeff.size1(); ++i) { r(i) = 0; @@ -1503,6 +1814,7 @@ void MetricBasis::_maxKstAfast(const fullMatrix<double> &coeff, void MetricBasis::_maxKstAsharp(const fullMatrix<double> &coeff, const fullVector<double> &jac, double mina, double beta, double &maxK) const { + Msg::Fatal("Verifie si cette fonction est ok"); fullVector<double> P(coeff.size1()); fullMatrix<double> Q(coeff.size1(), coeff.size1()); for (int i = 0; i < coeff.size1(); ++i) { @@ -1511,7 +1823,8 @@ void MetricBasis::_maxKstAsharp(const fullMatrix<double> &coeff, P(i) += coeff(i, l) * coeff(i, l); } P(i) = std::sqrt(P(i)); - for (int j = 0; j < coeff.size1(); ++j) { + // fill only the half + for (int j = i; j < coeff.size1(); ++j) { Q(i, j) = 0; for (int l = 1; l < 7; ++l) { Q(i, j) += coeff(i, l) * coeff(j, l); @@ -1538,10 +1851,10 @@ void MetricBasis::_maxKstAsharp(const fullMatrix<double> &coeff, const int j = itP->second[l].j; const int k = itP->second[l].k; num += itP->second[l].val * coeff(i, 0) * coeff(j, 0) * coeff(k, 0); - if (j == k) + if (j == k) // TODO verif ok den += itP->second[l].val * Q(i,i) * P(i); else - den += itP->second[l].val * 1/3*(Q(i,j)*P(k)+Q(i,k)*P(j)+Q(k,j)*P(i)); + den += itP->second[l].val * 1/3*(Q(i,j)*P(k)+Q(i,k)*P(j)+Q(j,k)*P(i)); } min = std::min(min, num/den); ++itJ; @@ -1551,88 +1864,564 @@ void MetricBasis::_maxKstAsharp(const fullMatrix<double> &coeff, maxK = 1/beta*(mina*mina*mina-min); } -void MetricBasis::_computeTermBeta(double &a, double &K, - double &dRda, double &term1, - double &phip) const +void MetricBasis::_computeBoundBeta(const fullMatrix<double> &coeff, + const fullVector<double> &jac, + double &beta, bool compLowBound) const { - double x0 = .5 * (K - a*a*a + 3*a); - double sin, sqrt; - if (x0 > 1) { - const double p = -3; - double q = -K + 2; - a = cubicCardanoRoot(p, q); - - x0 = 1; - phip = M_PI / 3; - term1 = 1 + .5 * a; - sin = std::sqrt(3.) / 2; - sqrt = 0; - } - else if (x0 < -1) { - K = -2 + a*a*a - 3*a; - - x0 = -1; - phip = 2 * M_PI / 3; - term1 = 1 - .5 * a; - sin = std::sqrt(3.) / 2; - sqrt = 0; + // compute a lower/upper bound for function beta = K/a^3 + + double upperBound = std::numeric_limits<double>::infinity(); + double lowerBound = -upperBound; + + // value in case of a failure + if (compLowBound) beta = 0; + else beta = 1; + + std::map<int, std::vector<IneqData> >::const_iterator itJ, itP; + itJ = _ineqJ2.begin(); + itP = _ineqP3.begin(); + + // _ineqJ2 and _ineqP3 have the same size + while (itJ != _ineqJ2.end()) { + double num = 0; + for (unsigned int k = 0; k < itJ->second.size(); ++k) { + const int i = itJ->second[k].i; + const int j = itJ->second[k].j; + num += itJ->second[k].val * jac(i) * jac(j); + } + + double den = 0; + for (unsigned int l = 0; l < itP->second.size(); ++l) { + const int i = itP->second[l].i; + const int j = itP->second[l].j; + const int k = itP->second[l].k; + den += itP->second[l].val * coeff(i, 0) * coeff(j, 0) * coeff(k, 0); + } + + // beta has to satisfy beta * den <= num if compLowBound = true + // or beta * den >= num if compLowBound = false + if (den == 0) { + if (compLowBound) { + if (num >= 0) { + ++itJ; + ++itP; + continue; + } + else return; + //TODO c'est mieux de retourner min(J^2)/max(q^3) ?? + } + else { + if (num <= 0) { + ++itJ; + ++itP; + continue; + } + else return; + //TODO c'est mieux de retourner max(J^2)/min(q^3) ?? + } + } + if (den > 0) { + if (compLowBound) + upperBound = std::min(upperBound, num/den); + else + lowerBound = std::max(lowerBound, num/den); + } + else { + if (compLowBound) + lowerBound = std::max(lowerBound, num/den); + else + upperBound = std::min(upperBound, num/den); + } + + ++itJ; + ++itP; } - else { - phip = (std::acos(x0) + M_PI) / 3; - term1 = 1 + a * std::cos(phip); - sin = std::sin(phip); - sqrt = std::sqrt(1-x0*x0); + + if (lowerBound <= upperBound) { + if (compLowBound) + beta = std::max(.0, upperBound); + else + beta = std::min(1., lowerBound); } - dRda = sin * sqrt + .5 * term1 * (1-a*a); + //TODO sinon, c'est mieux de retourner m..(J^2)/m..(q^3) ?? } -double MetricBasis::_R3Dsafe(double q, double p, double J) +void MetricBasis::_computeBoundingCurve(const fullMatrix<double> &coeff, + const fullVector<double> &jac, + double &beta, double c, + bool compLowBound) const { - if (q > 1e5*p) { - const double m = p*std::sqrt(3.)/q; - return (1-m) / (1+m); + // compute a lower/upper bounding curve K = beta * a^3 + c * a + // with c fixed. + + //Msg::Info("BEGINNING %g %g", std::numeric_limits<double>::min(), std::numeric_limits<double>::denorm_min()); + + fullMatrix<double> Q(coeff.size1(), coeff.size1()); // (half filled!) + for (int i = 0; i < coeff.size1(); ++i) { + for (int j = i; j < coeff.size1(); ++j) { + Q(i, j) = 0; + for (int l = 1; l < 7; ++l) + Q(i, j) += coeff(i, l) * coeff(j, l); + } + } + + double upperBound = std::numeric_limits<double>::infinity(); + double lowerBound = -upperBound; + + // values in case of a failure + if (compLowBound) beta = upperBound; + else beta = lowerBound; + + std::map<int, std::vector<IneqData> >::const_iterator itJ, itP; + itJ = _ineqJ2.begin(); + itP = _ineqP3.begin(); + + // _ineqJ2 and _ineqP3 have the same size + int kk = 0; + while (itJ != _ineqJ2.end()) {// && kk++ < 2) {// FIXME making tests + long double J2 = 0; + for (unsigned int k = 0; k < itJ->second.size(); ++k) { + const int i = itJ->second[k].i; + const int j = itJ->second[k].j; + J2 += itJ->second[k].val * jac(i) * jac(j); + //Msg::Info("(%d, %d) = (%g | %g, %g) -> J2:%Lg", i, j, itJ->second[k].val, jac(i), jac(j), J2); + } + + long double q3 = 0, qp2 = 0; + for (unsigned int l = 0; l < itP->second.size(); ++l) { + const int i = itP->second[l].i; + const int j = itP->second[l].j; + const int k = itP->second[l].k; + q3 += itP->second[l].val * coeff(i, 0) * coeff(j, 0) * coeff(k, 0); + qp2 += itP->second[l].val * 1/3*(coeff(i, 0) * Q(j, k) + + coeff(j, 0) * Q(i, k) + + coeff(k, 0) * Q(i, j)); + //Msg::Info("%d %d %d => %Lg %Lg", i, j, k, q3, qp2); + //Msg::Info("(%d %d %d) -> q3:%Lg qp2:%Lg", i, j, k, q3, qp2); + } + + //Msg::Info("a%Lg K%Lg %Lg %Lg", std::sqrt(q3/qp2), std::sqrt(J2*J2*q3/qp2/qp2/qp2), std::sqrt(q3/qp2), std::sqrt(J2*J2*q3/qp2/qp2/qp2)); + + const long double num = J2 - ((long double)c)*qp2; + const long double den = q3; + // beta has to satisfy beta * den <= num if compLowBound = true + // or beta * den >= num if compLowBound = false + + if (den == 0) { + if (compLowBound) { + if (num >= 0) {} // ok, nothing to do + else return; + //TODO c'est mieux de retourner min(..)/max(..) ?? + } + else { + if (num <= 0) {} // ok, nothing to do + else return; + //TODO c'est mieux de retourner max(..)/min(..) ?? + } + } + else if (den > 0) { + if (compLowBound) + upperBound = std::min(upperBound, (double)(num/den)); + else + lowerBound = std::max(lowerBound, (double)(num/den)); + } + else { + if (compLowBound) + lowerBound = std::max(lowerBound, (double)(num/den)); + else + upperBound = std::min(upperBound, (double)(num/den)); + } + + //Msg::Info("in [%.15g, %.15g]", lowerBound, upperBound); + + ++itJ; + ++itP; + } + + if (lowerBound <= upperBound) { + if (compLowBound) + beta = upperBound; + else + beta = lowerBound; } - const double a = q/p; - const double K = J*J/p/p/p; - return _R3Dsafe(a, K); + + //Msg::Info("ENDING"); + //TODO sinon, c'est mieux de retourner m..(..)/m..(..) ?? } -double MetricBasis::_R3Dsafe(double a, double K) +void MetricBasis::_computeLowerBoundC(const fullMatrix<double> &coeff, + const fullVector<double> &jac, + double beta, double &c) const +{ + // compute a lower/upper bound of function C = K-beta*a^3 + + double upperBound = std::numeric_limits<double>::infinity(); + double lowerBound = -upperBound; + + // value in case of a failure + c = 0; + + fullVector<double> P(coeff.size1()); + for (int i = 0; i < coeff.size1(); ++i) { + P(i) = 0; + for (int l = 1; l < 7; ++l) { + P(i) += coeff(i, l) * coeff(i, l); + } + P(i) = std::sqrt(P(i)); + } + + std::map<int, std::vector<IneqData> >::const_iterator itJ, itP; + itJ = _ineqJ2.begin(); + itP = _ineqP3.begin(); + + // _ineqJ2 and _ineqP3 have the same size + while (itJ != _ineqJ2.end()) { + double num = 0, den = 0; + for (unsigned int l = 0; l < itP->second.size(); ++l) { + const int i = itP->second[l].i; + const int j = itP->second[l].j; + const int k = itP->second[l].k; + num -= itP->second[l].val * coeff(i, 0) * coeff(j, 0) * coeff(k, 0); + den += itP->second[l].val * P(i) * P(j) * P(k); + } + + num = num * beta; + for (unsigned int l = 0; l < itJ->second.size(); ++l) { + const int i = itJ->second[l].i; + const int j = itJ->second[l].j; + num += itJ->second[l].val * jac(i) * jac(j); + } + + // c has to satisfy c * den <= num + if (den == 0) { + if (num >= 0) { + ++itJ; + ++itP; + continue; + } + else return; + //TODO c'est mieux de retourner min(J^2-beta q^3)/max(p^3) ? + } + if (den > 0) + upperBound = std::min(upperBound, num/den); + else { + lowerBound = std::max(lowerBound, num/den); + } + + ++itJ; + ++itP; + } + + if (lowerBound <= upperBound) { + c = upperBound; + } + //TODO sinon, c'est mieux de retourner min(J^2-beta q^3)/max(p^3) ? +} + +void MetricBasis::_computePseudoDerivatives(double a, double K, + double &dRda, double &dRdK) +{ + // Return derivative without positive (but non-constant) coefficients + // Useful when only the sign of dRda and dRdK or the ratio dRda/dRdK + // are needed. + + double w = _wSafe(a, K); + if (std::abs(w) > 1) { + Msg::Error("Cannot compute pseudo derivatives of R " + "outside the domain (w = %g)", w); + return; + } + + const double phip = (std::acos(w) + M_PI) / 3; + const double C = 1 + a * std::cos(phip); + + dRdK = C / 3; + dRda = 2 * std::sqrt(1-w*w) * std::sin(phip) + (1-a*a) * C; +} + +void MetricBasis::_computeDerivatives(double a, double K, + double &dRda, double &dRdK, + double &dRdaa, double &dRdaK, double &dRdKK) +{ + const double w = _wSafe(a, K); + if (std::abs(w) > 1) { + Msg::Error("Cannot compute derivatives of R outside the domain"); + return; + } + + const double phi = (std::acos(w)) / 3; + const double phip = phi + M_PI / 3; + const double S = 1./std::sqrt(1-w*w); + const double A = (1-a*a)*S; + const double sin = std::sin(phi); + const double sinp = std::sin(phip); + const double cosp = std::cos(phip); + const double C = 1 + a*cosp; + const double D = 1./(a+2*std::cos(phi)); + static const double sq3 = std::sqrt(3); + const double coeff = sq3*D*D/18; + + dRdK = coeff*6 * (C*S); + dRda = coeff*18 * (2*sinp + A*C); + dRdKK = coeff*S*S * (a*sinp - 4*C*sin*D + 3*w*C*S); + dRdaK = coeff*S*3 * (a*A*sinp + 3*w*A*C*S + 2*cosp - 4*C*(1+A*sin)*D); + dRdaa = coeff*9 * (3*w*A*A*C*S - 4*a*C*S + a*A*A*sinp - 4*(1+A*sin)*D*(2*sinp + A*C)); +} + +bool MetricBasis::_moveInsideDomain(double &a, double &K, bool bottomleftCorner) +{ + // Note: For N-R (when moving 'a'), we compute the root of + // f(a) = .5 * (K - a^3 + 3*a) - 'w' where 'w' is -1 or 1. + + double w = _w(a, K); + if (w > 1.) { + if (bottomleftCorner) { + // make sure to compute the right root (a>1) and + // avoid slopes that are too small: + a = std::max(a, 1.1); + + const double tolerance = _tol/100; + // Note: N-R approaches the root from the right, we + while (w > 1 || w < 1-tolerance) { + double df = w - 1; + double slope = 1.5*(1-a*a); + a -= df/slope; + w = _w(a, K); + } + } + else { + K -= 2*w - 2; + } + return true; + } + else if (w < -1.) { + if (bottomleftCorner) { + K -= 2*w + 2; + } + else { + a = std::max(a, 2.); + + const double tolerance = _tol/100; + while (std::abs(w+1) > tolerance) { + double df = w + 1; + double slope = 1.5*(1-a*a); + a -= df/slope; + w = _w(a, K); + } + } + return true; + } + return false; +} + +double MetricBasis::_computeMinRAlongCurve(double beta, double c, + double mina, double maxa) { - const double x = .5 * (K + (3 - a*a)*a); - if (x > 1+1e-7 || x < -1-1e-7) { - Msg::Warning("x = %g (a,K) = (%g,%g)", x, a, K); + // Compute the minimum of R on the curve K = beta * a^3 + c + // where beta can be zero. + // 'mina' and 'maxa' stand for the limits of the curve. There must be at + // least 1 limit. To indicate that 'mina' or 'maxa' is not a limit, it is + // set to a negative value. + + bool towardsPositivea; + double a, K, R; + if (mina < 0) { + a = maxa; + K = (beta*a*a + c)*a; + R = _R3Dsafe(a, K); + towardsPositivea = false; + } + else if (maxa < 0) { + a = mina; + K = (beta*a*a + c)*a; + R = _R3Dsafe(a, K); + towardsPositivea = true; + } + else { + // choose the more promising limit as starting point + double K1 = beta*mina*mina*mina + c; + double K2 = beta*maxa*maxa*maxa + c; + double R1 = _R3Dsafe(mina, K1); + double R2 = _R3Dsafe(maxa, K2); + if (R1 < R2) { + a = mina; + K = K1; + R = R1; + towardsPositivea = true; + } + else { + a = maxa; + K = K2; + R = R2; + towardsPositivea = false; + } + } + + double da = 1, dK, dKK; + double dRda, dRdK, dRdaa, dRdaK, dRdKK; + double dRdC, dRdCC; + + _computeDerivatives(a, K, dRda, dRdK, dRdaa, dRdaK, dRdKK); + dK = 3*beta*a*a+c; + dKK = 6*beta*a; + dRdC = dRda*da + dRdK*dK; + dRdCC = dRdaa*da*da + 2*dRdaK*da*dK + dRdKK*dK*dK + dRdK*dKK; + + if ((towardsPositivea && dRdC > 0) || (!towardsPositivea && dRdC < 0)) { + Msg::Warning("Already the minimum"); + return R; + } + + while (.25*dRdC*dRdC/dRdCC > .01*_toleranceOnR(R)) { + a = a - dRdC / dRdCC; + K = (beta*a*a + c)*a; + R = _R3Dsafe(a, K); + + _computeDerivatives(a, K, dRda, dRdK, dRdaa, dRdaK, dRdKK); + dK = 3*beta*a*a+c; + dKK = 6*beta*a; + dRdC = dRda*da + dRdK*dK; + dRdCC = dRdaa*da*da + 2*dRdaK*da*dK + dRdKK*dK*dK + dRdK*dKK; + } + + /*Msg::Warning("Checking Newton Raphson solution... (%g, %g)", a, K); + double min = 1; + for (double aa = std::max(1., .5*a); aa < 2*a; aa += .001*a) { + double KK = (beta*aa*aa + c)*aa; + double w = _wSafe(aa, KK); + if (std::abs(w) <= 1) min = std::min(min, _R3Dsafe(aa, KK)); } + if (std::abs(min-R) < .01*_toleranceOnR(R)) + Msg::Info("... aaand, it is good!"); + else + Msg::Error("... and, it's a fail! %g vs %g (tol %g => %g)", R, min, _toleranceOnR(R), R-.01*_toleranceOnR(R));*/ + + return R; +} + +bool MetricBasis::_intersectionCurveLeftCorner(double beta, double c, + double &a, double &K) +{ + // on input, a and K are the bottom left corner + // on output, a and K are the intersection + // return true if the intersection is on the vertical + // We assume the intersection exists + // (i.e. the bounding curve is sufficiently sharp) + if (3*beta*a*a + c < 0) { + Msg::Error("The slope is negative, cannot compute the intersection"); + return -1; + } + + const double minK = K; + const double mina = a; //TODO remove (not needed) + + K = (beta*a*a + c)*a; + if (K >= minK) return true; + + // Find the root by Newton-Raphson + double dK = K-minK; + + static double precision = std::numeric_limits<double>::epsilon() * 1e3; + while (std::abs(dK) > minK * precision) { //TODO meilleur approx + const double slope = 3*beta*a*a + c; + a -= dK / slope; + dK = (beta*a*a + c)*a-minK; + } + K = minK; + //Msg::Info("in %d it (%g, %g) -> (%g, %g) => diff(%g, %g)", k, mina, minK, a, K, a-mina, K-minK); + return false; +} + +double MetricBasis::_computeAbscissaMinR(double aStart, double K) +{ + double dRda, dRdK, dRdaa, dRdaK, dRdKK; + _computeDerivatives(aStart, K, dRda, dRdK, dRdaa, dRdaK, dRdKK); + double a = aStart; + while (dRda > MetricBasis::_tol*1e-3) { + double da = - dRda / dRdaa; + a += da; + _computeDerivatives(a, K, dRda, dRdK, dRdaa, dRdaK, dRdKK); + } + return a; +} - double ans; - if (x >= 1) ans = (a - 1) / (a + 2); - else if (x <= -1) ans = (a - 2) / (a + 1); +double MetricBasis::_R3Dsafe(double q, double p, double J) +{ + if (q >= p && p >= 0 && J == J && + p < std::numeric_limits<double>::infinity()) { + if (q == 0) return 0; + if (p == 0) return 1; + if (q == std::numeric_limits<double>::infinity()) { + Msg::Warning("q had infinite value in computation of 3D metric"); + return 1; + } + const double a = q/p; + const double K = J*J/p/p/p; + return _R3Dsafe(a, K); + } else { - const double phi = std::acos(x) / 3; - ans = (a + 2*std::cos(phi + 2*M_PI/3)) / (a + 2*std::cos(phi)); + Msg::Error("Wrong argument for 3d metric (%g, %g, %g)", q, p, J); + return -1; } +} - if (ans < 0 || ans > 1) { - if (ans < 0) return 0; - else return 1; +double MetricBasis::_R3Dsafe(double a, double K) +{ + if (a >= 1 && K >= 0) { + // TODO speedup in function of _tol ? + // if (2/a < _tol) return 1.; + // if (8/K < _tol*_tol*_tol) return 1.; + + if (a == std::numeric_limits<double>::infinity()) + return 1; + + const double w = _wSafe(a, K); + if (std::abs(w) > 1-_tol/10) { + if (w > 0) return (a - 1) / (a + 2); + else return (a - 2) / (a + 1); + } + + const double phi = std::acos(w) / 3; + const double R = (a + 2*std::cos(phi + 2*M_PI/3)) / (a + 2*std::cos(phi)); + return std::max(.0, std::min(1., R)); + } + else { + Msg::Error("Wrong argument for 3d metric (%g, %g)", a, K); + return -1; } - return ans; } double MetricBasis::_R2Dsafe(double q, double p) { - if (q < 0 || p < 0 || p > q) - Msg::Error("wrong argument for 2d metric (%g, %g)", q, p); - return (q-p) / (q+p); + if (q >= p && p >= 0 && p < std::numeric_limits<double>::infinity()) { + if (q == 0) return 0; + if (q == std::numeric_limits<double>::infinity()) { + Msg::Warning("q had infinite value in computation of 2D metric"); + return 1; + } + return (q-p) / (q+p); + } + else { + Msg::Error("Wrong argument for 2d metric (%g, %g)", q, p); + return -1; + } } double MetricBasis::_R2Dsafe(double a) { - if (a < 1 -#if !defined(_MSC_VER) - || !std::isfinite(a) -#endif - ) - Msg::Error("wrong argument for 2d metric (%g)", a); - return (a - 1) / (a + 1); + if (a >= 1) { + if (a == std::numeric_limits<double>::infinity()) + return 1; + return (a - 1) / (a + 1); + } + else { + Msg::Error("Wrong argument for 2d metric (%g)", a); + return -1; + } +} + +double MetricBasis::_toleranceOnR(double R) +{ + const double sqRmax = std::max(.0, std::sqrt(R) - _tol); + return R - sqRmax*sqRmax; } diff --git a/Numeric/MetricBasis.h b/Numeric/MetricBasis.h index 0764f76811178fadeceb90835672609d35a48eb0..e6d49b5f6b77a2c8a5220537dd2068b8e7586b15 100644 --- a/Numeric/MetricBasis.h +++ b/Numeric/MetricBasis.h @@ -6,11 +6,13 @@ #ifndef _METRIC_BASIS_H_ #define _METRIC_BASIS_H_ -#include "JacobianBasis.h" -#include "fullMatrix.h" #include <fstream> #include <cmath> +#include "fullMatrix.h" class MElement; +class bezierBasis; +class JacobianBasis; +class GradientBasis; class MetricBasis { friend class MetricCoefficient; @@ -58,29 +60,46 @@ private: public: MetricBasis(int elementTag); + // Get & Set methods static void setTol(double tol) {_tol = tol;} - const JacobianBasis* getJacobianForMetric() const {return _jacobian;} const bezierBasis* getBezier() const {return _bezier;} - int getNumMetricNodes() const {return _gradients->getNumSamplingPoints();} + //int getNumMetricNodes() const {return _gradients->getNumSamplingPoints();} + + // Compute min(R) with given tolerance + static double getMinR(MElement *el); + double computeMinR(MElement*) const; + + // Sample R and return min + static double getMinSampledR(MElement *el, int order); + double computeMinSampledR(MElement*, int order) const; + + // Compute R on corners and return min + static double getMinRCorner(MElement *el); + + // TEST : sample min(r_min) and max(r_max) and return ratio of the two + static double getMinSampledGlobalRatio(MElement *el, int order); + double computeMinSampledGlobalRatio(MElement*, int order) const; - static double boundMinR(MElement *el); - static double minSampledR(MElement *el, int order); - double getBoundMinR(MElement*) const; - double getMinSampledR(MElement*, int order) const; + // TEST : sample R and return max + static double getMaxSampledR(MElement *el, int order); + double computeMaxSampledR(MElement*, int order) const; - static double minRCorner(MElement *el); + // template<bool ideal> void getMetricCoeff(const fullMatrix<double> &nodes, fullMatrix<double> &coeff) const { _fillCoeff<ideal>(_dim, _gradients, nodes, coeff); } +// void lag2Bez(const fullMatrix<double> &metCoeffLag, +// fullMatrix<double> &metCoeffBez) const { +// _bezier->matrixLag2Bez.mult(metCoeffLag, metCoeffBez); +// } void lag2Bez(const fullMatrix<double> &metCoeffLag, - fullMatrix<double> &metCoeffBez) const { - _bezier->matrixLag2Bez.mult(metCoeffLag, metCoeffBez); - } + fullMatrix<double> &metCoeffBez) const; + // Metric order static int metricOrder(int tag); public: @@ -107,13 +126,13 @@ private: void _fillInequalitiesPyr(int order); void _lightenInequalities(int&, int&, int&); //TODO change - void _computeRmin(const fullMatrix<double>&, const fullVector<double>&, + void _computeBoundsRmin(const fullMatrix<double>&, const fullVector<double>&, double &RminLag, double &RminBez) const; - void _computeRmax(const fullMatrix<double>&, const fullVector<double>&, + void _computeBoundsRmax(const fullMatrix<double>&, const fullVector<double>&, double &RmaxLag) const; void _getMetricData(const MElement*, MetricData*&) const; - double _subdivideForRmin(MetricData*, double RminLag, double tol, MElement *el=NULL) const; + double _subdivideForRmin(MetricData*, double &RminLag, double tol, MElement *el=NULL) const; template<bool ideal> static void _fillCoeff(int dim, const GradientBasis*, const fullMatrix<double> &nodes, fullMatrix<double> &coeff); @@ -121,10 +140,9 @@ private: const fullMatrix<double> &coeff, int num); void _minMaxA(const fullMatrix<double>&, double &min, double &max) const; - void _minA(const fullMatrix<double>&, double &min) const; - void _minK(const fullMatrix<double>&, const fullVector<double>&, double &min) const; - void _computeTermBeta(double &a, double &K, double &dRda, - double &term1, double &phip) const; + void _minAfast(const fullMatrix<double>&, double &min) const; + void _minKfast(const fullMatrix<double>&, const fullVector<double>&, double &min) const; + void _minKsharp(const fullMatrix<double>&, const fullVector<double>&, double &min) const; void _maxAstKpos(const fullMatrix<double>&, const fullVector<double>&, double minK, double beta, double &maxa) const; void _maxAstKneg(const fullMatrix<double>&, const fullVector<double>&, @@ -133,11 +151,49 @@ private: double mina, double beta, double &maxK) const; void _maxKstAsharp(const fullMatrix<double>&, const fullVector<double>&, double mina, double beta, double &maxK) const; + void _computeBoundBeta(const fullMatrix<double>&, + const fullVector<double>&, + double &beta, bool lowerBound) const; + void _computeBoundingCurve(const fullMatrix<double>&, + const fullVector<double>&, + double &beta, double c, bool lowerBound) const; + void _computeLowerBoundC(const fullMatrix<double>&, + const fullVector<double>&, + double beta, double &c) const; + + static bool _moveInsideDomain(double &a, double &K, bool bottomleftCorner); + static void _computePseudoDerivatives(double a, double K, + double &dRda, double &dRdK); + static void _computeDerivatives(double a, double K, + double &dRda, double &dRdK, + double &dRdaa, double &dRdaK, double &dRdKK); + static double _computeMinRAlongCurve(double beta, double c, + double mina, double maxa); + static bool _intersectionCurveLeftCorner(double beta, double c, + double &mina, double &minK); + static double _computeAbscissaMinR(double aStart, double K); static double _R3Dsafe(double a, double K); static double _R3Dsafe(double q, double p, double J); static double _R2Dsafe(double a); static double _R2Dsafe(double q, double p); + static inline double _w(double a, double K) { + return .5 * (K + (3-a*a)*a); + } + static inline double _wSafe(double a, double K) { + const double w = _w(a, K); + if (w > 1) { + if (w < 1+_tol/10) return 1; + else Msg::Error("outside the domain w(%g, %g) = %g", a, K, w); + } + else if (w < -1) { + if (w > -1-_tol/10) return -1; + else Msg::Error("outside the domain w(%g, %g) = %g", a, K, w); + } + return w; + } + + static double _toleranceOnR(double R); private: class gterIneq {