diff --git a/Numeric/MetricBasis.cpp b/Numeric/MetricBasis.cpp index 1bc1c72f5c103b842a062c9eef7f247c79325c22..9d293c50908cdbc6eefcb61eeceecb1c5fbe9337 100644 --- a/Numeric/MetricBasis.cpp +++ b/Numeric/MetricBasis.cpp @@ -3,6 +3,7 @@ // See the LICENSE.txt file for license information. Please report all // bugs and problems to the public mailing list <gmsh@geuz.org>. +#include "AnalyseCurvedMesh.h" #include "MetricBasis.h" #include "BasisFactory.h" #include "pointsGenerators.h" @@ -53,9 +54,9 @@ MetricBasis::MetricBasis(int tag) const int metOrder = metricOrder(tag); if (type == TYPE_HEX || type == TYPE_PRI) { int order = ElementType::OrderFromTag(tag); - _jacobian = new JacobianBasis(tag, 3*order); + _jacobian = BasisFactory::getJacobianBasis(tag, 3*order); } - else if (type == TYPE_TET) + else if (type == TYPE_TET || type == TYPE_TRI || type == TYPE_QUA) _jacobian = BasisFactory::getJacobianBasis(tag); else Msg::Fatal("metric not implemented for element tag %d", tag); @@ -82,19 +83,37 @@ double MetricBasis::minRCorner(MElement *el) int nSampPnts = jacobian->getNumJacNodes(); if (el->getType() == TYPE_PYR) nSampPnts = 4; - int nMapping = gradients->getNumMapNodes(); + + // Nodes fullMatrix<double> nodes(nMapping, 3); el->getNodesCoord(nodes); + fullMatrix<double> nodesMetric; + switch (el->getDim()) { + case 1: + return -1; - // Metric coefficients - fullMatrix<double> metCoeffLag; - _fillCoeff(gradients, nodes, metCoeffLag); + case 2: + if (_paramOnPlane(nodes, nodesMetric)) return -1; + break; + + case 3: + nodesMetric.setAsProxy(nodes); + break; + + default: + Msg::Error("no metric for element of dimension %d (tag=%d)", + el->getDim(), el->getTypeForMSH()); + } // Jacobian coefficients fullVector<double> jacLag(jacobian->getNumJacNodes()); jacobian->getSignedJacobian(nodes, jacLag); + // Metric coefficients + fullMatrix<double> metCoeffLag; + _fillCoeff(gradients, nodesMetric, metCoeffLag); + // Compute min_corner(R) return _computeMinlagR(jacLag, metCoeffLag, nSampPnts); } @@ -166,15 +185,27 @@ double MetricBasis::getBoundMinR(MElement *el) { int nSampPnts = _gradients->getNumSamplingPoints(); int nMapping = _gradients->getNumMapNodes(); + + // Nodes fullMatrix<double> nodes(nMapping, 3); el->getNodesCoord(nodes); + fullMatrix<double> nodesMetric; + switch (el->getDim()) { + case 1: + return -1; - // Metric coefficients - fullMatrix<double> metCoeffLag; - _fillCoeff(_gradients, nodes, metCoeffLag); - fullMatrix<double> *metCoeff; - metCoeff = new fullMatrix<double>(nSampPnts, metCoeffLag.size2()); - _bezier->matrixLag2Bez.mult(metCoeffLag, *metCoeff); + case 2: + if (_paramOnPlane(nodes, nodesMetric)) return -1; + break; + + case 3: + nodesMetric.setAsProxy(nodes); + break; + + default: + Msg::Error("no metric for element of dimension %d (tag=%d)", + el->getDim(), el->getTypeForMSH()); + } // Jacobian coefficients fullVector<double> jacLag(_jacobian->getNumJacNodes()); @@ -182,9 +213,16 @@ double MetricBasis::getBoundMinR(MElement *el) _jacobian->getSignedJacobian(nodes, jacLag); _jacobian->lag2Bez(jacLag, *jac); - // + // Metric coefficients + fullMatrix<double> metCoeffLag; + _fillCoeff(_gradients, nodesMetric, 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, 0); + _computeRmin(*metCoeff, *jac, RminLag, RminBez); if (RminLag-RminBez < MetricBasis::_tol) { return RminBez; @@ -288,45 +326,47 @@ void MetricBasis::_fillInequalities(int metricOrder) } } - exp.resize(_jacobian->getBezier()->_exponents.size1(), - _jacobian->getBezier()->_exponents.size2()); - for (int i = 0; i < _jacobian->getBezier()->_exponents.size1(); ++i) { - for (int j = 0; j < _jacobian->getBezier()->_exponents.size2(); ++j) { - exp(i, j) = static_cast<int>(_jacobian->getBezier()->_exponents(i, j) + .5); + if (_jacobian) { + exp.resize(_jacobian->getBezier()->_exponents.size1(), + _jacobian->getBezier()->_exponents.size2()); + for (int i = 0; i < _jacobian->getBezier()->_exponents.size1(); ++i) { + for (int j = 0; j < _jacobian->getBezier()->_exponents.size2(); ++j) { + exp(i, j) = static_cast<int>(_jacobian->getBezier()->_exponents(i, j) + .5); + } } - } - int njac = _jacobian->getNumJacNodes(); - for (int i = 0; i < njac; i++) { - for (int j = i; j < njac; j++) { - int order = metricOrder/2*3; - double num = 1, den = 1; - { - int compl1 = order; - int compl2 = order; - int compltot = 2*order; - for (int k = 0; k < dimSimplex; k++) { - num *= nChoosek(compl1, exp(i, k)) - * nChoosek(compl2, exp(j, k)); - den *= nChoosek(compltot, exp(i, k) + exp(j, k)); - compl1 -= exp(i, k); - compl2 -= exp(j, k); - compltot -= exp(i, k) + exp(j, k); + int njac = _jacobian->getNumJacNodes(); + for (int i = 0; i < njac; i++) { + for (int j = i; j < njac; j++) { + int order = metricOrder/2*3; + double num = 1, den = 1; + { + int compl1 = order; + int compl2 = order; + int compltot = 2*order; + for (int k = 0; k < dimSimplex; k++) { + num *= nChoosek(compl1, exp(i, k)) + * nChoosek(compl2, exp(j, k)); + den *= nChoosek(compltot, exp(i, k) + exp(j, k)); + compl1 -= exp(i, k); + compl2 -= exp(j, k); + compltot -= exp(i, k) + exp(j, k); + } + } + for (int k = dimSimplex; k < dim; k++) { + num *= nChoosek(order, exp(i, k)) + * nChoosek(order, exp(j, k)); + den *= nChoosek(2*order, exp(i, k) + exp(j, k)); } - } - for (int k = dimSimplex; k < dim; k++) { - num *= nChoosek(order, exp(i, k)) - * nChoosek(order, exp(j, k)); - den *= nChoosek(2*order, exp(i, k) + exp(j, k)); - } - if (i != j) num *= 2; + if (i != j) num *= 2; - ++countJ2; - int hash = 0; - for (int k = 0; k < dim; k++) { - hash += (exp(i, k)+exp(j, k)) * pow_int(2*order+1, k); + ++countJ2; + int hash = 0; + for (int k = 0; k < dim; k++) { + hash += (exp(i, k)+exp(j, k)) * pow_int(2*order+1, k); + } + _ineqJ2[hash].push_back(IneqData(num/den, i, j)); } - _ineqJ2[hash].push_back(IneqData(num/den, i, j)); } } @@ -531,8 +571,7 @@ int MetricBasis::metricOrder(int tag) void MetricBasis::_computeRmin( const fullMatrix<double> &coeff, const fullVector<double> &jac, - double &RminLag, double &RminBez, - int depth, bool debug) const + double &RminLag, double &RminBez) const { RminLag = _computeMinlagR(jac, coeff, _bezier->getNumLagCoeff()); if (RminLag == 0) { @@ -540,6 +579,13 @@ void MetricBasis::_computeRmin( 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) { @@ -595,9 +641,9 @@ void MetricBasis::_computeRmin( double am1 = std::pow(amApprox*amApprox*amApprox+da+.05, 1/3.); //double am0S = am0, am1S = am1; double am = (am0 + am1)/2; - double R0 = _Rsafe(am0, minK); - double R1 = _Rsafe(am1, minK); - double Rnew = _Rsafe(am, minK); + 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) { @@ -611,17 +657,17 @@ void MetricBasis::_computeRmin( R1 = Rnew; } am = (am0 + am1)/2; - Rnew = _Rsafe(am, minK); + Rnew = _R3Dsafe(am, minK); } if (am < maxa) { - RminBez = _Rsafe(am, minK); + RminBez = _R3Dsafe(am, minK); RminBez = std::sqrt(RminBez); return; } } - RminBez = _Rsafe(maxa, minK); + RminBez = _R3Dsafe(maxa, minK); RminBez = std::sqrt(RminBez); return; } @@ -670,7 +716,7 @@ void MetricBasis::_computeRmax( RmaxLag = std::max(RmaxLag, std::sqrt((a - std::sqrt(3)) / (a + std::sqrt(3)))); } else { - const double tmpR = _Rsafe(a, jac(i)/p/p*jac(i)/p); + const double tmpR = _R3Dsafe(a, jac(i)/p/p*jac(i)/p); RmaxLag = std::max(RmaxLag, std::sqrt(tmpR)); } } @@ -707,7 +753,7 @@ double MetricBasis::_subdivideForRmin(MetricData *md, double RminLag, double tol jac = new fullVector<double>; jac->setAsProxy(*subjac, i * numJacPnts, numJacPnts); double minLag, minBez; - _computeRmin(*coeff, *jac, minLag, minBez, depth+1); + _computeRmin(*coeff, *jac, minLag, minBez); RminLag = std::min(RminLag, minLag); int newNum = num + (i+1) * pow_int(10, depth); MetricData *metData = new MetricData(coeff, jac, minBez, depth+1, newNum); @@ -771,15 +817,31 @@ void MetricBasis::_getMetricData(MElement *el, MetricData *&md) const { int nSampPnts = _gradients->getNumSamplingPoints(); int nMapping = _gradients->getNumMapNodes(); + + // Nodes fullMatrix<double> nodes(nMapping, 3); el->getNodesCoord(nodes); + fullMatrix<double> nodesMetric; + switch (el->getDim()) { + case 1: + md = NULL; + return; - // Metric coefficients - fullMatrix<double> metCoeffLag; - _fillCoeff(_gradients, nodes, metCoeffLag); - fullMatrix<double> *metCoeff; - metCoeff = new fullMatrix<double>(nSampPnts, metCoeffLag.size2()); - _bezier->matrixLag2Bez.mult(metCoeffLag, *metCoeff); + case 2: + if (_paramOnPlane(nodes, nodesMetric)) { + md = NULL; + return; + } + break; + + case 3: + nodesMetric.setAsProxy(nodes); + break; + + default: + Msg::Error("no metric for element of dimension %d (tag=%d)", + el->getDim(), el->getTypeForMSH()); + } // Jacobian coefficients fullVector<double> jacLag(_jacobian->getNumJacNodes()); @@ -787,6 +849,13 @@ void MetricBasis::_getMetricData(MElement *el, MetricData *&md) const _jacobian->getSignedJacobian(nodes, jacLag); _jacobian->lag2Bez(jacLag, *jac); + // Metric coefficients + fullMatrix<double> metCoeffLag; + _fillCoeff(_gradients, nodesMetric, metCoeffLag); + fullMatrix<double> *metCoeff; + metCoeff = new fullMatrix<double>(nSampPnts, metCoeffLag.size2()); + _bezier->matrixLag2Bez.mult(metCoeffLag, *metCoeff); + md = new MetricData(metCoeff, jac, -1, 0, 0); } @@ -799,10 +868,27 @@ void MetricBasis::_fillCoeff(const GradientBasis *gradients, case 0 : return; case 1 : - case 2 : Msg::Fatal("not implemented"); break; + case 2 : + { + fullMatrix<double> dxydX(nSampPnts,2), dxydY(nSampPnts,2); + gradients->getGradientsFromNodes(nodes, &dxydX, &dxydY, NULL); + + coeff.resize(nSampPnts, 3); + 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); + const double dvxdX = dxdX*dxdX + dydX*dydX; + const double dvxdY = dxdY*dxdY + dydY*dydY; + coeff(i, 0) = (dvxdX + dvxdY) / 2; + coeff(i, 1) = dvxdX - coeff(i, 0); + coeff(i, 2) = (dxdX*dxdY + dydX*dydY); + } + } + break; + case 3 : { fullMatrix<double> dxyzdX(nSampPnts,3), dxyzdY(nSampPnts,3), dxyzdZ(nSampPnts,3); @@ -834,28 +920,112 @@ double MetricBasis::_computeMinlagR(const fullVector<double> &jac, const fullMatrix<double> &coeff, int num) { double Rmin = 1.; - for (int i = 0; i < num; ++i) { - if (jac(i) <= 0.) { - return 0; - } - else { - double q = coeff(i, 0); + + switch (coeff.size2()) { + case 7: + for (int i = 0; i < num; ++i) { + if (jac(i) <= 0.) return 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/6); const double a = q/p; - if (a > 1e4) { + if (a > 1e4) { // TODO: from _tol ? Rmin = std::min(Rmin, std::sqrt((a - std::sqrt(3)) / (a + std::sqrt(3)))); } else { - const double tmpR = _Rsafe(a, jac(i)/p/p*jac(i)/p); + const double tmpR = _R3Dsafe(a, jac(i)/p/p*jac(i)/p); Rmin = std::min(Rmin, std::sqrt(tmpR)); } } + return Rmin; + + case 3: + for (int i = 0; i < num; ++i) { + if (jac(i) <= 0.) return 0; + + const double q = coeff(i, 0); + const double p = pow_int(coeff(i, 1), 2) + pow_int(coeff(i, 2), 2); + const double a = q/std::sqrt(p); + const double tmpR = _R2Dsafe(a); + Rmin = std::min(Rmin, std::sqrt(tmpR)); + } + return Rmin; + + default: + Msg::Error("coeff have not right number of column"); + return -1; + } +} + +int MetricBasis::_paramOnPlane(const fullMatrix<double> &nodes3d, + fullMatrix<double> &nodes2d) +{ + // check if in xy-plane + int i = 0; + while (i < nodes3d.size1() && nodes3d(i,2) == 0) ++i; + if (i == nodes3d.size1()) { + nodes2d.setAsProxy(const_cast<double*>(nodes3d.getDataPtr()), + nodes3d.size1(), 2); + Msg::Info("Was on xy-plane"); + return 0; + } + + // check if coplanar and reparameterize + SVector3 vx(nodes3d(1, 0) - nodes3d(0, 0), + nodes3d(1, 1) - nodes3d(0, 1), + nodes3d(1, 2) - nodes3d(0, 2)); + SVector3 vy(nodes3d(2, 0) - nodes3d(0, 0), + nodes3d(2, 1) - nodes3d(0, 1), + nodes3d(2, 2) - nodes3d(0, 2)); + + double lx = vx.norm(), L = vy.norm() + lx; + if (norm(crossprod(vx, vy)) / L < 1e-10) { + nodes2d.resize(nodes3d.size1(), 2, true); + return 0; } - return Rmin; + + nodes2d.resize(nodes3d.size1(), 2, false); + nodes2d(0, 0) = 0; + nodes2d(0, 1) = 0; + + SVector3 ux = vx; + ux *= 1/lx; + nodes2d(1, 0) = lx; + nodes2d(1, 1) = 0; + + double p_vy_x = dot(vy, ux); + SVector3 uy = ux; + uy *= -p_vy_x; + uy += vy; + double n = uy.norm(); + uy.normalize(); + nodes2d(2, 0) = p_vy_x; + nodes2d(2, 1) = n; + + for (int i = 3; i < nodes3d.size1(); ++i) { + SVector3 v(nodes3d(i, 0) - nodes3d(0, 0), + nodes3d(i, 1) - nodes3d(0, 1), + nodes3d(i, 2) - nodes3d(0, 2)); + double p_v_x = dot(v, ux); + double p_v_y = dot(v, uy); + SVector3 px = ux, py = uy; + px *= -p_v_x; + py *= -p_v_y; + v -= px; + v -= py; + if (norm(v) / L < 1e-10) { + //Msg::Info("%g / %g = %g <= 1e-10", norm(v), L, norm(v)/L); + return 1; + } + nodes2d(i, 0) = p_v_x; + nodes2d(i, 1) = p_v_y; + } + + return 0; } void MetricBasis::_minMaxA( @@ -871,7 +1041,7 @@ void MetricBasis::_minMaxA( const int i = it->second[k].i; const int j = it->second[k].j; double tmp = 0; - for (int l = 1; l < 7; ++l) { + for (int l = 1; l < coeff.size2(); ++l) { tmp += coeff(i, l) * coeff(j, l); } den += it->second[k].val * tmp; @@ -882,8 +1052,10 @@ void MetricBasis::_minMaxA( max = std::max(val, max); ++it; } - min *= 6; - max *= 6; + if (coeff.size2() == 7) { + min *= 6; + max *= 6; + } min = min > 1 ? std::sqrt(min) : 1; max = std::sqrt(max); diff --git a/Numeric/MetricBasis.h b/Numeric/MetricBasis.h index 8bbe4c4aa049a0b6e29e47476649282ca7b78c67..c126aeb3e806de58059ca4675b81c1dac6013950 100644 --- a/Numeric/MetricBasis.h +++ b/Numeric/MetricBasis.h @@ -70,7 +70,7 @@ private: void _lightenInequalities(int&, int&, int&); //TODO change void _computeRmin(const fullMatrix<double>&, const fullVector<double>&, - double &RminLag, double &RminBez, int depth, bool debug = false) const; + double &RminLag, double &RminBez) const; void _computeRmax(const fullMatrix<double>&, const fullVector<double>&, double &RmaxLag) const; void _computeTermBeta(double &a, double &K, double &dRda, @@ -83,6 +83,9 @@ private: static double _computeMinlagR(const fullVector<double> &jac, const fullMatrix<double> &coeff, int num); + static int _paramOnPlane(const fullMatrix<double> &nodes3d, + fullMatrix<double> &nodes2d); + void _minMaxA(const fullMatrix<double>&, double &min, double &max) const; void _minK(const fullMatrix<double>&, const fullVector<double>&, double &min) const; void _maxAstKpos(const fullMatrix<double>&, const fullVector<double>&, @@ -94,7 +97,7 @@ private: void _maxKstAsharp(const fullMatrix<double>&, const fullVector<double>&, double mina, double beta, double &maxK) const; - static double _Rsafe(double a, double K) { + static double _R3Dsafe(double a, double K) { const double x = .5 * (K - a*a*a + 3*a); if (x > 1+1e-13 || x < -1-1e-13) { Msg::Warning("x = %g (|1+%g|)", x, std::abs(x)-1); @@ -115,6 +118,10 @@ private: } return ans; } + static double _R2Dsafe(double a) { + if (a < 1) Msg::Warning("R2d = %g", (a - 1) / (a + 1)); + return (a - 1) / (a + 1); + } private: class gterIneq {