From 48d53f26791bccc88aa4bdb0faf1fabca0ca7be6 Mon Sep 17 00:00:00 2001 From: Elias Pipping <elias.pipping@fu-berlin.de> Date: Fri, 6 Jul 2012 11:08:52 +0200 Subject: [PATCH] Handle 0 * 1/x for x close to 0 in addHessian Since 0 * nan = nan --- dune/tectonic/localnonlinearity.hh | 74 ++++++++++++++++++++---------- 1 file changed, 51 insertions(+), 23 deletions(-) diff --git a/dune/tectonic/localnonlinearity.hh b/dune/tectonic/localnonlinearity.hh index 6fcefe4c..ee3d8164 100644 --- a/dune/tectonic/localnonlinearity.hh +++ b/dune/tectonic/localnonlinearity.hh @@ -54,39 +54,67 @@ template <int dimension> class LocalNonlinearity { } } - /* - H''(|x|) x \otimes x / |x|^2 - + H'(|x|) [|x|^2 id - x \otimes x] / |x|^3 - - For i == j, this is (writing k and using the notation from below) - - h2 * x[k] * x[k] / normX2 + h1 [normX2 - x[k]^2]/normX^3 - = h2 * x[k] * x[k] / normX2 + h1ox - h1 x[k]^2/normX^3 - = (h2-h1ox) * x[k] * x[k] / normX2 + h1ox - - For i != j, this is (again, using the notation from below) - - h2 * x[i] * x[j] / normX2 - h1 * x[i] * x[j]/normX^3 - = (h2 - h1ox) * x[i] * x[j] / normX2 + /** Formula for the derivative: + + \f{align*}{ + \frac {d^2}{dz^2} H(|z|) + &= \frac d{dz} \left( H'(|z|) \otimes \frac z{|z|} \right)\\ + &= H''(|z|) \frac z{|z|} \otimes \frac z{|z|} + + H'(|z|) \left( \frac {|z| \operatorname{id} - z \otimes z/|z|}{|z|^2} + \right)\\ + &= \frac {H''(|z|)}{|z|^2} z \otimes z + + \frac {H'(|z|)}{|z|} \operatorname{id} + - \frac {H'(|z|)}{|z|^3} z \otimes z\\ + &= \left( \frac {H''(|z|)}{|z|^2} - \frac {H'(|z|)}{|z|^3} \right) z + \otimes z + + \frac {H'(|z|)}{|z|} \operatorname{id} + \f} */ void addHessian(VectorType const &x, MatrixType &A) const { - if (x == VectorType(0)) - return; - double const normX2 = x.two_norm2(); - double const normX = sqrt(normX2); - double const h1ox = func->rightDifferential(normX) / normX; - double const h2 = func->second_deriv(normX); + double const normX = std::sqrt(normX2); + double const normX3 = normX * normX2; + + double const H1 = func->rightDifferential(normX); + double const H2 = func->second_deriv(normX); + + // TODO: potential optimisation: factor out (H1 / normX), get rid of normX3 + double const weight1 = H2 / normX2; + double const weight2 = -H1 / normX3; + double const weight3 = H1 / normX; + + // {{{ In what follows, we handle the case 0 * (1/x) = 0 with x + // close to 0. + double tensorweight = 0; + + if (std::isnan(weight1)) + assert(H2 == 0); + else + tensorweight += weight1; + + if (std::isnan(weight2)) + assert(H1 == 0); + else + tensorweight += weight2; + + double idweight = 0; + if (std::isnan(weight3)) + assert(H1 == 0); + else + idweight += weight3; + // }}} for (int i = 0; i < dimension; ++i) for (int j = 0; j < i; ++j) { - double const entry = (h2 - h1ox) * x[i] * x[j] / normX2; + double const entry = tensorweight * x[i] * x[j]; A[i][j] += entry; A[j][i] += entry; } - for (int k = 0; k < dimension; ++k) - A[k][k] += (h2 - h1ox) * x[k] * x[k] / normX2 + h1ox; + for (int k = 0; k < dimension; ++k) { + double const entry = tensorweight * x[k] * x[k]; + A[k][k] += entry + idweight; + } } void addGradient(VectorType const &x, VectorType &y) const { -- GitLab