BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SpecularMagneticStrategy_v1.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sample/LegacyRT/SpecularMagneticStrategy_v1.cpp
6 //! @brief Implements class SpecularMagneticStrategy_v1.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
20 #include "Sample/Slice/Slice.h"
21 #include <Eigen/LU>
22 
23 namespace {
24 void CalculateEigenvalues(const std::vector<Slice>& slices, const kvector_t k,
25  std::vector<MatrixRTCoefficients_v1>& coeff);
26 void CalculateTransferAndBoundary(const std::vector<Slice>& slices,
27  std::vector<MatrixRTCoefficients_v1>& coeff);
28 void SetForNoTransmission(std::vector<MatrixRTCoefficients_v1>& coeff);
29 complex_t GetImExponential(complex_t exponent);
30 } // namespace
31 
33  const kvector_t& k) const
34 {
35  std::vector<MatrixRTCoefficients_v1> result(slices.size());
36  CalculateEigenvalues(slices, k, result);
37  CalculateTransferAndBoundary(slices, result);
38 
39  ISpecularStrategy::coeffs_t resultConvert;
40  for (auto& coeff : result)
41  resultConvert.push_back(std::make_unique<MatrixRTCoefficients_v1>(coeff));
42 
43  return resultConvert;
44 }
45 
47 SpecularMagneticStrategy_v1::Execute(const std::vector<Slice>&, const std::vector<complex_t>&) const
48 {
49  throw std::runtime_error("Not implemented");
50 }
51 
52 std::variant<complex_t, Eigen::Matrix2cd>
53 SpecularMagneticStrategy_v1::computeTopLayerR(const std::vector<Slice>& slices,
54  const std::vector<complex_t>& kz) const
55 {
56  throw std::runtime_error("Not implemented");
57 }
58 
59 namespace {
60 void CalculateEigenvalues(const std::vector<Slice>& slices, const kvector_t k,
61  std::vector<MatrixRTCoefficients_v1>& coeff)
62 {
63  double mag_k = k.mag();
64  double n_ref = slices[0].material().refractiveIndex(2 * M_PI / mag_k).real();
65  double sign_kz = k.z() > 0.0 ? -1.0 : 1.0;
66  for (size_t i = 0; i < coeff.size(); ++i) {
67  coeff[i].m_scatt_matrix = slices[i].polarizedReducedPotential(k, n_ref);
68  coeff[i].m_kt = mag_k * slices[i].thickness();
69  coeff[i].m_a = coeff[i].m_scatt_matrix.trace() / 2.0;
70  coeff[i].m_b_mag =
71  sqrt(coeff[i].m_a * coeff[i].m_a - coeff[i].m_scatt_matrix.determinant());
72  coeff[i].m_bz = (coeff[i].m_scatt_matrix(0, 0) - coeff[i].m_scatt_matrix(1, 1)) / 2.0;
73  complex_t rad0 = coeff[i].m_a - coeff[i].m_b_mag;
74  complex_t rad1 = coeff[i].m_a + coeff[i].m_b_mag;
75  // use small absorptive component for layers with i>0 if radicand becomes very small:
76  if (i > 0) {
77  if (std::abs(rad0) < 1e-40)
78  rad0 = I * 1e-40;
79  if (std::abs(rad1) < 1e-40)
80  rad1 = I * 1e-40;
81  }
82  coeff[i].lambda(0) = sqrt(rad0);
83  coeff[i].lambda(1) = sqrt(rad1);
84  coeff[i].kz = mag_k * coeff[i].lambda * sign_kz;
85  }
86 }
87 
88 // todo: avoid overflows (see SpecularMatrix.cpp)
89 void CalculateTransferAndBoundary(const std::vector<Slice>& slices,
90  std::vector<MatrixRTCoefficients_v1>& coeff)
91 {
92  size_t N = coeff.size();
93  if (coeff[0].lambda == Eigen::Vector2cd::Zero() && N > 1) {
94  SetForNoTransmission(coeff);
95  return;
96  }
97 
98  // First, initialize bottom layer values to have no reflection
99  coeff[N - 1].initializeBottomLayerPhiPsi();
100  if (N > 1)
101  coeff[N - 1].calculateTRMatrices();
102 
103  coeff[0].calculateTRMatrices();
104  for (int signed_index = static_cast<int>(N) - 2; signed_index > 0; --signed_index) {
105  size_t i = static_cast<size_t>(signed_index);
106  double t = slices[i].thickness();
107  coeff[i].calculateTRMatrices();
108  Eigen::Matrix4cd l = coeff[i].R1m * GetImExponential(coeff[i].kz(0) * t)
109  + coeff[i].T1m * GetImExponential(-coeff[i].kz(0) * t)
110  + coeff[i].R2m * GetImExponential(coeff[i].kz(1) * t)
111  + coeff[i].T2m * GetImExponential(-coeff[i].kz(1) * t);
112  coeff[i].phi_psi_plus = l * coeff[i + 1].phi_psi_plus;
113  coeff[i].phi_psi_min = l * coeff[i + 1].phi_psi_min;
114  }
115  // If more than one layer, impose normalization and correct correspondence
116  // for spin-z polarization in top layer
117  if (N > 1) {
118  // First layer boundary is also top layer boundary:
119  coeff[0].phi_psi_plus = coeff[1].phi_psi_plus;
120  coeff[0].phi_psi_min = coeff[1].phi_psi_min;
121  // Normalize all boundary values such that top layer has unit wave
122  // amplitude for both spin up and down (and does not contain a
123  // transmitted wave amplitude for the opposite polarization)
124  Eigen::Vector2cd T0basisA = coeff[0].T1plus() + coeff[0].T2plus();
125  Eigen::Vector2cd T0basisB = coeff[0].T1min() + coeff[0].T2min();
126  complex_t cpA, cpB, cmA, cmB;
127  cpA = T0basisB(1);
128  cpB = -T0basisA(1);
129  cmA = T0basisB(0);
130  cmB = -T0basisA(0);
131  Eigen::Vector4cd phipsitemp = cpA * coeff[0].phi_psi_plus + cpB * coeff[0].phi_psi_min;
132  coeff[0].phi_psi_min = cmA * coeff[0].phi_psi_plus + cmB * coeff[0].phi_psi_min;
133  coeff[0].phi_psi_plus = phipsitemp;
134  Eigen::Vector2cd T0plusV = coeff[0].T1plus() + coeff[0].T2plus();
135  Eigen::Vector2cd T0minV = coeff[0].T1min() + coeff[0].T2min();
136  complex_t T0plus = T0plusV(0);
137  complex_t T0min = T0minV(1);
138  coeff[0].phi_psi_min = coeff[0].phi_psi_min / T0min;
139  coeff[0].phi_psi_plus = coeff[0].phi_psi_plus / T0plus;
140  for (size_t i = 1; i < N; ++i) {
141  phipsitemp = (cpA * coeff[i].phi_psi_plus + cpB * coeff[i].phi_psi_min) / T0plus;
142  coeff[i].phi_psi_min =
143  (cmA * coeff[i].phi_psi_plus + cmB * coeff[i].phi_psi_min) / T0min;
144  coeff[i].phi_psi_plus = phipsitemp;
145  }
146  }
147 }
148 
149 void SetForNoTransmission(std::vector<MatrixRTCoefficients_v1>& coeff)
150 {
151  size_t N = coeff.size();
152  for (size_t i = 0; i < N; ++i) {
153  coeff[i].phi_psi_plus.setZero();
154  coeff[i].phi_psi_min.setZero();
155  coeff[i].T1m = Eigen::Matrix4cd::Identity() / 4.0;
156  coeff[i].R1m = coeff[i].T1m;
157  coeff[i].T2m = coeff[i].T1m;
158  coeff[i].R2m = coeff[i].T1m;
159  }
160 }
161 
162 complex_t GetImExponential(complex_t exponent)
163 {
164  if (exponent.imag() > -std::log(std::numeric_limits<double>::min()))
165  return 0.0;
166  return std::exp(I * exponent);
167 }
168 } // unnamed namespace
constexpr complex_t I
Definition: Complex.h:21
std::complex< double > complex_t
Definition: Complex.h:20
#define M_PI
Definition: Constants.h:44
Defines class LayerInterface.
Defines class Layer.
Defines class MultiLayer.
Defines class Slice.
Defines class SpecularMagneticStrategy_v1.
Defines WavevectorInfo.
double mag() const
Returns magnitude of the vector.
T z() const
Returns z-component in cartesian coordinate system.
Definition: BasicVector3D.h:67
std::vector< std::unique_ptr< const ILayerRTCoefficients > > coeffs_t
virtual std::variant< complex_t, Eigen::Matrix2cd > computeTopLayerR(const std::vector< Slice > &slices, const std::vector< complex_t > &kz) const override
ISpecularStrategy::coeffs_t Execute(const std::vector< Slice > &slices, const kvector_t &k) const
Computes refraction angle reflection/transmission coefficients for given sliced multilayer and waveve...