diff --git a/dune/gfe/hyperbolichalfspacepoint.hh b/dune/gfe/hyperbolichalfspacepoint.hh index a29907aded03c2daf911e705eee49a91e2fe2bee..96e9a5e826cd7b46792f3bccf3dc38309ede63a5 100644 --- a/dune/gfe/hyperbolichalfspacepoint.hh +++ b/dune/gfe/hyperbolichalfspacepoint.hh @@ -32,6 +32,15 @@ class HyperbolicHalfspacePoint return -2*std::acos(x) / std::sqrt(1-x*x); } + /** \brief Compute the derivative of arccosh^2 without getting unstable for x close to 1 */ + static T derivativeOfArcCosHSquared(const T& x) { + const T eps = 1e-4; + if (x > 1-eps) { // regular expression is unstable, use the series expansion instead + return 2 - 2*(x-1)/3 + 4/15*(x-1)*(x-1); + } else + return 2*std::acosh(x) / std::sqrt(x*x-1); + } + /** \brief Compute the second derivative of arccos^2 without getting unstable for x close to 1 */ static T secondDerivativeOfArcCosSquared(const T& x) { const T eps = 1e-4; @@ -97,27 +106,50 @@ public: /** \brief The exponential map */ static HyperbolicHalfspacePoint exp(const HyperbolicHalfspacePoint& p, const TangentVector& v) { - - Dune::FieldMatrix<T,N,N> frame = p.orthonormalFrame(); - - EmbeddedTangentVector ev; - frame.mtv(v,ev); - - return exp(p,ev); + + assert (N==2); + + T vNorm = v.two_norm(); + + // we compute geodesics by applying an isometry to a fixed unit-speed geodesic. + // Hence we need a unit velocity vector. + if (vNorm <= 0) + return p; + + TangentVector vUnit = v; + vUnit /= vNorm; + + // Compute the coefficients a,b,c,d of the Moebius transform that transforms + // the unit speed upward geodesic to the one through p with direction vUnit. + // We expect the Moebius transform to be an isometry, i.e. ad-bc = 1. + T cc = 1/(2*p.data_[N-1]) - vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]); + T dd = 1/(2*p.data_[N-1]) + vUnit[N-1] / (2*p.data_[N-1]*p.data_[N-1]); + T ac = vUnit[0] / (2*p.data_[N-1]) + p.data_[0]*cc; + T bd = p.data_[0] / p.data_[N-1] - ac; + + HyperbolicHalfspacePoint result; + + // vertical part + result.data_[1] = std::exp(vNorm) / (cc*std::exp(2*vNorm) + dd); + + // horizontal part + result.data_[0] = (ac*std::exp(2*vNorm) + bd) / (cc*std::exp(2*vNorm) + dd); + + return result; } - /** \brief Length of the great arc connecting the two points */ + /** \brief Hyperbolic distance between two points + * + * \f dist(a,b) = arccosh ( 1 + ||a-b||^2 / (2a_n b_n) \f + */ static T distance(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { - // Not nice: we are in a class for unit vectors, but the class is actually - // supposed to handle perturbations of unit vectors as well. Therefore - // we normalize here. - T x = a.data_ * b.data_/a.data_.two_norm()/b.data_.two_norm(); + T result(0); - // paranoia: if the argument is just eps larger than 1 acos returns NaN - x = std::min(x,1.0); + for (size_t i=0; i<N; i++) + result += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]); - return std::acos(x); + return std::acosh(1 + result / (2*a.data_[N-1]*b.data_[N-1])); } /** \brief Compute the gradient of the squared distance function keeping the first argument fixed @@ -125,17 +157,22 @@ public: Unlike the distance itself the squared distance is differentiable at zero */ static EmbeddedTangentVector derivativeOfDistanceSquaredWRTSecondArgument(const HyperbolicHalfspacePoint& a, const HyperbolicHalfspacePoint& b) { - T x = a.data_ * b.data_; - - EmbeddedTangentVector result = a.data_; - - result *= derivativeOfArcCosSquared(x); - - // Project gradient onto the tangent plane at b in order to obtain the surface gradient - result = b.projectOntoTangentSpace(result); + + TangentVector result; + + T diffNormSquared(0); + + for (size_t i=0; i<N; i++) + diffNormSquared += (a.data_[i]-b.data_[i])*(a.data_[i]-b.data_[i]); - // Gradient must be a tangent vector at b, in other words, orthogonal to it - assert( std::abs(b.data_ * result) < 1e-5); + for (size_t i=0; i<N-1; i++) + result[i] = ( b.data_[i] - a.data_[i] ) / (a.data_[N-1] * b.data_[N-1]); + + result[N-1] = - diffNormSquared / (2*a.data_[N-1]*b.data_[N-1]*b.data_[N-1]) - (a.data_[N-1] - b.data_[N-1]) / (a.data_[N-1]*b.data_[N-1]); + + T x = 1 + diffNormSquared/ (2*a.data_[N-1]*b.data_[N-1]); + + result *= derivativeOfArcCosHSquared(x); return result; }