From 15ab014986dc8086bbc4d5d26a0c0ea34c60fb63 Mon Sep 17 00:00:00 2001
From: Oliver Sander <sander@igpm.rwth-aachen.de>
Date: Tue, 3 Sep 2013 16:30:14 +0000
Subject: [PATCH] Avoid calls to std::sqrt(x) near x=0 in method 'exp'

Because ADOL-C doesn't like calls to sqrt(0), obviously.
The use of sqrt(x) for small x can be avoided completely
using modified series expansions, because the overall
function exp is differentiable even at x=0.

[[Imported from SVN: r9426]]
---
 dune/gfe/rotation.hh | 27 +++++++++++++++++++++------
 1 file changed, 21 insertions(+), 6 deletions(-)

diff --git a/dune/gfe/rotation.hh b/dune/gfe/rotation.hh
index 28198eff..95bc5207 100644
--- a/dune/gfe/rotation.hh
+++ b/dune/gfe/rotation.hh
@@ -146,6 +146,15 @@ class Rotation<T,3> : public Quaternion<T>
         return (x < 1e-4) ? 0.5 - (x*x/48)  + (x*x*x*x)/3840 : std::sin(x/2)/x;
     }
 
+    /** \brief Computes sin(sqrt(x)/2) / sqrt(x) without getting unstable for small x
+     *
+     * Using this somewhat exotic series allows to avoid a few calls to std::sqrt near 0,
+     * which ADOL-C doesn't like.
+     */
+    static T sincHalfOfSquare(const T& x) {
+        return (x < 1e-15) ? 0.5 - (x/48)  + (x*x)/(120*32) : std::sin(std::sqrt(x)/2)/std::sqrt(x);
+    }
+
 public:
 
     /** \brief The type used for coordinates */
@@ -237,18 +246,24 @@ public:
         Rotation<T,3> q;
 
         Dune::FieldVector<T,3> vAxial = v.axial();
-        T normV = vAxial.two_norm();
+        T normV2 = vAxial.two_norm2();
 
-        // Stabilization for small |v| due to Grassia
-        T sin = sincHalf(normV);
+        // Stabilization for small |v|
+        T sin = sincHalfOfSquare(normV2);
 
-        // if normV == 0 then q = (0,0,0,1)
-        assert(!isnan(sin));
+        assert(!std::isnan(sin));
 
         q[0] = sin * vAxial[0];
         q[1] = sin * vAxial[1];
         q[2] = sin * vAxial[2];
-        q[3] = std::cos(normV/2);
+
+        // The series expansion of cos(x) at x=0 is
+        // 1 - x*x/2 + x*x*x*x/24 - ...
+        // hence the series of cos(x/2) is
+        // 1 - x*x/8 + x*x*x*x/384 - ...
+        q[3] = (normV2 < 1e-4)
+          ? 1 - normV2/8 + normV2*normV2 / 384
+          : std::cos(std::sqrt(normV2)/2);
 
         return q;
     }
-- 
GitLab