From b4e134b5dfa66e1fd59e7c6dcaa5e53ae8bf7213 Mon Sep 17 00:00:00 2001
From: Randolf Beerwerth <r.beerwerth@fz-juelich.de>
Date: Tue, 24 Mar 2020 12:18:27 +0100
Subject: [PATCH] Replace computational kernel in SpecularScalarStrategy

---
 Core/Multilayer/SpecularScalarStrategy.cpp | 83 +++++++++-------------
 Core/Multilayer/SpecularScalarStrategy.h   |  5 --
 2 files changed, 33 insertions(+), 55 deletions(-)

diff --git a/Core/Multilayer/SpecularScalarStrategy.cpp b/Core/Multilayer/SpecularScalarStrategy.cpp
index 2c6671f53c1..0836576f3df 100644
--- a/Core/Multilayer/SpecularScalarStrategy.cpp
+++ b/Core/Multilayer/SpecularScalarStrategy.cpp
@@ -67,32 +67,7 @@ std::vector<ScalarRTCoefficients> SpecularScalarStrategy::computeTR(const std::v
 
     // Calculate transmission/refraction coefficients t_r for each layer, from bottom to top.
     size_t start_index = N - 2;
-    if (!calculateUpFromLayer(coeff, slices, kz, start_index)) {
-        // If excessive damping leads to infinite amplitudes, then use bisection to determine
-        // the maximum number of layers for which results remain finite.
-        start_index = bisectRTcomputation(coeff, slices, kz, 0, N - 2, (N - 2) / 2);
-    }
-    setZeroBelow(coeff, start_index + 1);
-
-    // Normalize to incoming downward travelling amplitude = 1.
-    complex_t T0 = coeff[0].t_r(0);
-    // This trick is used to avoid overflows in the intermediate steps of
-    // complex division:
-    double tmax = std::max(std::abs(T0.real()), std::abs(T0.imag()));
-    if (std::isinf(tmax))
-        throw std::runtime_error("Unexpected tmax=infty");
-    for (size_t i = 0; i < N; ++i) {
-        coeff[i].t_r(0) /= tmax;
-        coeff[i].t_r(1) /= tmax;
-    }
-
-    // Now the real normalization to T_0 proceeds
-    T0 = coeff[0].t_r(0);
-    if (std::isinf(abs(T0)))
-        throw std::runtime_error("Unexpected tmax=infty");
-    for (size_t i = 0; i < N; ++i)
-        coeff[i].t_r = coeff[i].t_r / T0;
-
+    calculateUpFromLayer(coeff, slices, kz, start_index);
     return coeff;
 }
 
@@ -110,6 +85,8 @@ bool SpecularScalarStrategy::calculateUpFromLayer(std::vector<ScalarRTCoefficien
 {
     coeff[slice_index + 1].t_r(0) = 1.0;
     coeff[slice_index + 1].t_r(1) = 0.0;
+    std::vector<complex_t> factors(slice_index+2);
+    factors[slice_index + 1] = complex_t(1, 0);
     for (size_t j = 0; j <= slice_index; ++j) {
         size_t i = slice_index - j; // start from bottom
         double sigma = 0.0;
@@ -117,34 +94,40 @@ bool SpecularScalarStrategy::calculateUpFromLayer(std::vector<ScalarRTCoefficien
             sigma = roughness->getSigma();
 
         coeff[i].t_r = transition(kz[i], kz[i + 1], sigma, slices[i].thickness(), coeff[i + 1].t_r);
-        if (std::isinf(std::norm(coeff[i].t_r(0))))
-            return false; // overflow => retry calulation starting from a higher layer
+
+        // normalize the t-coefficient in each step of the computation
+        // this avoids an overflow in the backwards propagation
+        // the used factors are stored in order to correct the amplitudes
+        // in forward direction at the end of the computation
+
+        if (std::isinf(std::norm(coeff[i].t_r(0)))){
+//            throw std::runtime_error("Unexpected inf");
+            // catching this inf is not correctly covered by existing tests
+            // it is possible to pass all tests by moving the normalization before
+            // this check and hence corrupting all amplitudes
+            coeff[i].t_r(0) = 1.0;
+            coeff[i].t_r(1) = 0.0;
+
+            setZeroBelow(coeff, i);
+        }
+        factors[i] = coeff[i].t_r(0);
+        coeff[i].t_r = coeff[i].t_r / coeff[i].t_r(0);
     }
-    return true; // no overflow => result is definitive
-}
 
-//! Recursive bisection to determine the number of the deepest layer where RT computation
-//! can be started without running into overflow.
-//! Computes coeff, and returns largest possible start layer index.
-size_t SpecularScalarStrategy::bisectRTcomputation(std::vector<ScalarRTCoefficients>& coeff,
-                           const std::vector<Slice>& slices, const std::vector<complex_t>& kz,
-                           const size_t lgood, const size_t lbad, const size_t l) const
-{
-    if (calculateUpFromLayer(coeff, slices, kz, l)) {
-        // success => highest valid l must be in l..lbad-1
-        if (l + 1 == lbad)
-            return l;
-        return bisectRTcomputation(coeff, slices, kz, l, lbad, (l + lbad) / 2);
-    } else {
-        // failure => highest valid l must be in lgood..l-1
-        if (l - 1 == lgood) {
-            if (!calculateUpFromLayer(coeff, slices, kz, l - 1))
-                throw std::runtime_error(
-                    "Bisection failed: Unexpected non-monotonicity in RT computation");
-            return l - 1;
+    // now correct all amplitudes by dividing the with the remaining factors in forward direction
+    // at some point this divison underflows, which is the point when all further amplitudes are set to zero
+    auto dumpingFactor = complex_t(1, 0);
+    for (size_t j = 1; j <= slice_index + 1; ++j){
+        dumpingFactor = dumpingFactor * factors[j-1];
+        if (std::isinf(std::norm( dumpingFactor ))){
+            setZeroBelow(coeff, j-1);
+            break;
         }
-        return bisectRTcomputation(coeff, slices, kz, lgood, l, (lgood + l) / 2);
+        coeff[j].t_r = coeff[j].t_r / dumpingFactor;
     }
+
+    // this return value is meaningless now, this procedure should always succeed
+    return true;
 }
 
 namespace  {
diff --git a/Core/Multilayer/SpecularScalarStrategy.h b/Core/Multilayer/SpecularScalarStrategy.h
index e52a00fa63b..9278f04285d 100644
--- a/Core/Multilayer/SpecularScalarStrategy.h
+++ b/Core/Multilayer/SpecularScalarStrategy.h
@@ -50,11 +50,6 @@ private:
     bool calculateUpFromLayer(std::vector<ScalarRTCoefficients>& coeff,
                                      const std::vector<Slice>& slices, const std::vector<complex_t>& kz,
                                      size_t slice_index) const;
-
-    size_t bisectRTcomputation(std::vector<ScalarRTCoefficients>& coeff,
-                                      const std::vector<Slice>& slices, const std::vector<complex_t>& kz,
-                                      const size_t lgood, const size_t lbad, const size_t l) const;
-
 };
 
 #endif // SPECULARSCALARSTRATEGY_H
-- 
GitLab