From 7d5cb8fb3332e1a7f4a820324db988389b759c47 Mon Sep 17 00:00:00 2001
From: Randolf Beerwerth <r.beerwerth@fz-juelich.de>
Date: Fri, 4 Sep 2020 16:19:28 +0200
Subject: [PATCH] Provide stabilized computation of magnetic Fresnel
 computation

---
 Core/Multilayer/SpecularMagneticStrategy.cpp | 78 ++++++++++++++------
 Core/Multilayer/SpecularMagneticStrategy.h   | 18 ++---
 2 files changed, 63 insertions(+), 33 deletions(-)

diff --git a/Core/Multilayer/SpecularMagneticStrategy.cpp b/Core/Multilayer/SpecularMagneticStrategy.cpp
index f58c2bb910d..97f032ec451 100644
--- a/Core/Multilayer/SpecularMagneticStrategy.cpp
+++ b/Core/Multilayer/SpecularMagneticStrategy.cpp
@@ -77,8 +77,7 @@ SpecularMagneticStrategy::computeTR(const std::vector<Slice>& slices,
 
     std::for_each(result.begin(), result.end(), [](auto& coeff) { calculateTR(coeff); });
     nullifyBottomReflection(result.back());
-    propagateBackwards(result, slices);
-    propagateForwards(result, findNormalizationCoefficients(result.front()));
+    propagateBackwardsForwards(result, slices);
 
     return result;
 }
@@ -176,10 +175,13 @@ void SpecularMagneticStrategy::nullifyBottomReflection(MatrixRTCoefficients_v2&
     coeff.m_w_plus(3) = 0.0;
 }
 
-void SpecularMagneticStrategy::propagateBackwards(std::vector<MatrixRTCoefficients_v2>& coeff,
-                                                  const std::vector<Slice>& slices)
+void SpecularMagneticStrategy::propagateBackwardsForwards(
+    std::vector<MatrixRTCoefficients_v2>& coeff, const std::vector<Slice>& slices)
 {
     const int size = static_cast<int>(coeff.size());
+    std::vector<Eigen::Matrix2cd> SMatrices(coeff.size());
+    std::vector<complex_t> Normalization(coeff.size());
+
     for (int index = size - 2; index >= 0; --index) {
         const size_t i = static_cast<size_t>(index);
         const double t = slices[i].thickness();
@@ -190,10 +192,54 @@ void SpecularMagneticStrategy::propagateBackwards(std::vector<MatrixRTCoefficien
                              + coeff[i].T2 * GetImExponential(-kz(1) * t);
         coeff[i].m_w_plus = l * coeff[i + 1].m_w_plus;
         coeff[i].m_w_min = l * coeff[i + 1].m_w_min;
+
+        // rotate and normalize polarization
+        auto r = findNormalizationCoefficients(coeff[i]);
+        auto S = std::get<0>(r);
+        auto norm = std::get<1>(r);
+
+        SMatrices[i] = S;
+        Normalization[i] = norm;
+
+        const complex_t a_plus = S(0, 0) / norm;
+        const complex_t b_plus = S(1, 0) / norm;
+        const complex_t a_min = S(0, 1) / norm;
+        const complex_t b_min = S(1, 1) / norm;
+
+        Eigen::Vector4cd w_plus = a_plus * coeff[i].m_w_plus + b_plus * coeff[i].m_w_min;
+        Eigen::Vector4cd w_min = a_min * coeff[i].m_w_plus + b_min * coeff[i].m_w_min;
+
+        coeff[i].m_w_plus = std::move(w_plus);
+        coeff[i].m_w_min = std::move(w_min);
+    }
+
+    auto dumpingFactor = complex_t(1, 0);
+    Eigen::Matrix2cd S = Eigen::Matrix2cd::Identity();
+    for (size_t i = 1; i < coeff.size(); ++i) {
+        dumpingFactor = dumpingFactor * Normalization[i - 1];
+        S = SMatrices[i - 1] * S;
+
+        if (std::isinf(std::norm(dumpingFactor))) {
+            // not entirely sure, whether this is the correct edge case
+            std::for_each(coeff.begin() + i, coeff.end(),
+                          [](auto& coeff) { setNoTransmission(coeff); });
+            break;
+        }
+
+        const complex_t a_plus = S(0, 0) / dumpingFactor;
+        const complex_t b_plus = S(1, 0) / dumpingFactor;
+        const complex_t a_min = S(0, 1) / dumpingFactor;
+        const complex_t b_min = S(1, 1) / dumpingFactor;
+
+        Eigen::Vector4cd w_plus = a_plus * coeff[i].m_w_plus + b_plus * coeff[i].m_w_min;
+        Eigen::Vector4cd w_min = a_min * coeff[i].m_w_plus + b_min * coeff[i].m_w_min;
+
+        coeff[i].m_w_plus = std::move(w_plus);
+        coeff[i].m_w_min = std::move(w_min);
     }
 }
 
-Eigen::Matrix2cd
+std::pair<Eigen::Matrix2cd, complex_t>
 SpecularMagneticStrategy::findNormalizationCoefficients(const MatrixRTCoefficients_v2& coeff)
 {
     const Eigen::Vector2cd Ta = coeff.T1plus() + coeff.T2plus();
@@ -204,25 +250,11 @@ SpecularMagneticStrategy::findNormalizationCoefficients(const MatrixRTCoefficien
 
     Eigen::Matrix2cd result;
     result << S(1, 1), -S(0, 1), -S(1, 0), S(0, 0);
-    result /= S(0, 0) * S(1, 1) - S(1, 0) * S(0, 1);
-
-    return result;
-}
+    auto d1 = S(1, 1) - S(0, 1);
+    auto d2 = S(1, 0) - S(0, 0);
+    auto denom = S(0, 0) * d1 - d2 * S(0, 1);
 
-void SpecularMagneticStrategy::propagateForwards(std::vector<MatrixRTCoefficients_v2>& coeff,
-                                                 const Eigen::Matrix2cd& weights)
-{
-    const complex_t a_plus = weights(0, 0);
-    const complex_t b_plus = weights(1, 0);
-    const complex_t a_min = weights(0, 1);
-    const complex_t b_min = weights(1, 1);
-
-    for (auto& term : coeff) {
-        Eigen::Vector4cd w_plus = a_plus * term.m_w_plus + b_plus * term.m_w_min;
-        Eigen::Vector4cd w_min = a_min * term.m_w_plus + b_min * term.m_w_min;
-        term.m_w_plus = std::move(w_plus);
-        term.m_w_min = std::move(w_min);
-    }
+    return {result, denom};
 }
 
 namespace
diff --git a/Core/Multilayer/SpecularMagneticStrategy.h b/Core/Multilayer/SpecularMagneticStrategy.h
index 2ae617a4fe0..c93828d19c5 100644
--- a/Core/Multilayer/SpecularMagneticStrategy.h
+++ b/Core/Multilayer/SpecularMagneticStrategy.h
@@ -33,9 +33,9 @@ class Slice;
 class BA_CORE_API_ SpecularMagneticStrategy : public ISpecularStrategy
 {
 public:
-    using coefficient_type         = MatrixRTCoefficients_v2;
+    using coefficient_type = MatrixRTCoefficients_v2;
     using coefficient_pointer_type = std::unique_ptr<const coefficient_type>;
-    using coeffs_t                 = std::vector<coefficient_pointer_type>;
+    using coeffs_t = std::vector<coefficient_pointer_type>;
 
     //! Computes refraction angle reflection/transmission coefficients
     //! for given sliced multilayer and wavevector k
@@ -61,18 +61,16 @@ private:
 
     //! Propagates boundary conditions from the bottom to the top of the layer stack.
     //! Used to compute boundary conditions from the bottom one (with nullified reflection)
-    static void propagateBackwards(std::vector<MatrixRTCoefficients_v2>& coeff,
-                                   const std::vector<Slice>& slices);
+    //! simultaneously propagates amplitudes forward again
+    //! Due to the use of temporary objects this is combined into one function now
+    static void propagateBackwardsForwards(std::vector<MatrixRTCoefficients_v2>& coeff,
+                                           const std::vector<Slice>& slices);
 
     //! finds linear coefficients for normalizing transmitted wave to unity.
     //! The left column of the returned matrix corresponds to the coefficients for pure spin-up
     //! wave, while the right column - to the coefficients for the spin-down one.
-    static Eigen::Matrix2cd findNormalizationCoefficients(const MatrixRTCoefficients_v2& coeff);
-
-    //! makes a linear combination of boundary conditions with using the given weights for each
-    //! coefficient in the vector.
-    static void propagateForwards(std::vector<MatrixRTCoefficients_v2>& coeff,
-                                  const Eigen::Matrix2cd& weights);
+    static std::pair<Eigen::Matrix2cd, complex_t>
+    findNormalizationCoefficients(const MatrixRTCoefficients_v2& coeff);
 };
 
 #endif // BORNAGAIN_CORE_MULTILAYER_SPECULARMAGNETICSTRATEGY_H
-- 
GitLab