BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SpecularMagneticStrategy_v2.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Sample/LegacyRT/SpecularMagneticStrategy_v2.cpp
6 //! @brief Implements class SpecularMagneticStrategy_v2.
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 
18 #include "Sample/Slice/Slice.h"
19 
20 namespace {
21 kvector_t magneticImpact(kvector_t B_field);
22 Eigen::Vector2cd eigenvalues(complex_t kz, double b_mag);
23 Eigen::Vector2cd checkForUnderflow(const Eigen::Vector2cd& eigenvs);
24 complex_t GetImExponential(complex_t exponent);
25 
26 // The factor 1e-18 is here to have unit: 1/T*nm^-2
29 } // namespace
30 
32  const kvector_t& k) const
33 {
34  return Execute(slices, KzComputation::computeReducedKz(slices, k));
35 }
36 
38 SpecularMagneticStrategy_v2::Execute(const std::vector<Slice>& slices,
39  const std::vector<complex_t>& kz) const
40 {
41  if (slices.size() != kz.size())
42  throw std::runtime_error("Number of slices does not match the size of the kz-vector");
43 
45  for (auto& coeff : computeTR(slices, kz))
46  result.push_back(std::make_unique<MatrixRTCoefficients_v2>(coeff));
47 
48  return result;
49 }
50 
51 std::variant<complex_t, Eigen::Matrix2cd>
52 SpecularMagneticStrategy_v2::computeTopLayerR(const std::vector<Slice>& slices,
53  const std::vector<complex_t>& kz) const
54 {
55  throw std::runtime_error("Not implemented");
56 }
57 
58 std::vector<MatrixRTCoefficients_v2>
59 SpecularMagneticStrategy_v2::computeTR(const std::vector<Slice>& slices,
60  const std::vector<complex_t>& kzs)
61 {
62  if (kzs[0] == 0.)
63  throw std::runtime_error("Edge case k_z = 0 not implemented");
64 
65  if (slices.size() != kzs.size())
66  throw std::runtime_error(
67  "Error in SpecularMagnetic_::execute: kz vector and slices size shall coinside.");
68  if (slices.empty())
69  return {};
70 
71  std::vector<MatrixRTCoefficients_v2> result;
72  result.reserve(slices.size());
73 
74  const double kz_sign = kzs.front().real() > 0.0 ? 1.0 : -1.0; // save sign to restore it later
75  const kvector_t b_0 = magneticImpact(slices.front().bField());
76  result.emplace_back(kz_sign, eigenvalues(kzs.front(), 0.0), kvector_t{0.0, 0.0, 0.0});
77  for (size_t i = 1, size = slices.size(); i < size; ++i) {
78  kvector_t b = magneticImpact(slices[i].bField()) - b_0;
79  result.emplace_back(kz_sign, checkForUnderflow(eigenvalues(kzs[i], b.mag())), b);
80  }
81 
82  if (result.front().m_lambda == Eigen::Vector2cd::Zero()) {
83  std::for_each(result.begin(), result.end(), [](auto& coeff) { setNoTransmission(coeff); });
84  return result;
85  }
86 
87  std::for_each(result.begin(), result.end(), [](auto& coeff) { calculateTR(coeff); });
88  nullifyBottomReflection(result.back());
89  propagateBackwardsForwards(result, slices);
90 
91  return result;
92 }
93 
95 {
96  const double b = coeff.m_b.mag();
97  if (b == 0.0) {
98  calculateZeroFieldTR(coeff);
99  return;
100  }
101 
102  const double bpbz = b + coeff.m_b.z();
103  const double bmbz = b - coeff.m_b.z();
104  const complex_t bxmby = coeff.m_b.x() - I * coeff.m_b.y();
105  const complex_t bxpby = coeff.m_b.x() + I * coeff.m_b.y();
106  const complex_t l_1 = coeff.m_lambda(0);
107  const complex_t l_2 = coeff.m_lambda(1);
108 
109  auto& T1 = coeff.T1;
110  T1 << bmbz, -bxmby, -bmbz * l_1, bxmby * l_1, -bxpby, bpbz, bxpby * l_1, -bpbz * l_1,
111  -bmbz / l_1, bxmby / l_1, bmbz, -bxmby, bxpby / l_1, -bpbz / l_1, -bxpby, bpbz;
112  T1 /= 4.0 * b;
113 
114  auto& R1 = coeff.R1;
115  R1 << T1(0, 0), T1(0, 1), -T1(0, 2), -T1(0, 3), T1(1, 0), T1(1, 1), -T1(1, 2), -T1(1, 3),
116  -T1(2, 0), -T1(2, 1), T1(2, 2), T1(2, 3), -T1(3, 0), -T1(3, 1), T1(3, 2), T1(3, 3);
117 
118  auto& T2 = coeff.T2;
119  T2 << bpbz, bxmby, -bpbz * l_2, -bxmby * l_2, bxpby, bmbz, -bxpby * l_2, -bmbz * l_2,
120  -bpbz / l_2, -bxmby / l_2, bpbz, bxmby, -bxpby / l_2, -bmbz / l_2, bxpby, bmbz;
121  T2 /= 4.0 * b;
122 
123  auto& R2 = coeff.R2;
124  R2 << T2(0, 0), T2(0, 1), -T2(0, 2), -T2(0, 3), T2(1, 0), T2(1, 1), -T2(1, 2), -T2(1, 3),
125  -T2(2, 0), -T2(2, 1), T2(2, 2), T2(2, 3), -T2(3, 0), -T2(3, 1), T2(3, 2), T2(3, 3);
126 }
127 
129 {
130  coeff.T1 = Eigen::Matrix4cd::Zero();
131  coeff.R1 = Eigen::Matrix4cd::Zero();
132  coeff.T2 = Eigen::Matrix4cd::Zero();
133  coeff.R2 = Eigen::Matrix4cd::Zero();
134 
135  // lambda_1 == lambda_2, no difference which one to use
136  const complex_t eigen_value = coeff.m_lambda(0);
137 
138  Eigen::Matrix3cd Tblock;
139  Tblock << 0.5, 0.0, -0.5 * eigen_value, 0.0, 0.0, 0.0, -0.5 / eigen_value, 0.0, 0.5;
140 
141  Eigen::Matrix3cd Rblock;
142  Rblock << 0.5, 0.0, 0.5 * eigen_value, 0.0, 0.0, 0.0, 0.5 / eigen_value, 0.0, 0.5;
143 
144  coeff.T1.block<3, 3>(1, 1) = Tblock;
145  coeff.R1.block<3, 3>(1, 1) = Rblock;
146  coeff.T2.block<3, 3>(0, 0) = Tblock;
147  coeff.R2.block<3, 3>(0, 0) = Rblock;
148 }
149 
151 {
152  coeff.m_w_plus = Eigen::Vector4cd::Zero();
153  coeff.m_w_min = Eigen::Vector4cd::Zero();
154  coeff.T1 = Eigen::Matrix4cd::Identity() / 4.0;
155  coeff.R1 = coeff.T1;
156  coeff.T2 = coeff.T1;
157  coeff.R2 = coeff.T1;
158 }
159 
161 {
162  const complex_t l_1 = coeff.m_lambda(0);
163  const complex_t l_2 = coeff.m_lambda(1);
164  const double b_mag = coeff.m_b.mag();
165  const kvector_t& b = coeff.m_b;
166 
167  if (b_mag == 0.0) {
168  // both eigenvalues are the same, no difference which one to take
169  coeff.m_w_plus << -l_1, 0.0, 1.0, 0.0;
170  coeff.m_w_min << 0.0, -l_1, 0.0, 1.0;
171  return;
172  }
173 
174  // First basis vector that has no upward going wave amplitude
175  coeff.m_w_min(0) = (b.x() - I * b.y()) * (l_1 - l_2) / 2.0 / b_mag;
176  coeff.m_w_min(1) = b.z() * (l_2 - l_1) / 2.0 / b_mag - (l_1 + l_2) / 2.0;
177  coeff.m_w_min(2) = 0.0;
178  coeff.m_w_min(3) = 1.0;
179 
180  // Second basis vector that has no upward going wave amplitude
181  coeff.m_w_plus(0) = -(l_1 + l_2) / 2.0 - b.z() / (l_1 + l_2);
182  coeff.m_w_plus(1) = (b.x() + I * b.y()) * (l_1 - l_2) / 2.0 / b_mag;
183  coeff.m_w_plus(2) = 1.0;
184  coeff.m_w_plus(3) = 0.0;
185 }
186 
188  std::vector<MatrixRTCoefficients_v2>& coeff, const std::vector<Slice>& slices)
189 {
190  const int size = static_cast<int>(coeff.size());
191  std::vector<Eigen::Matrix2cd> SMatrices(coeff.size());
192  std::vector<complex_t> Normalization(coeff.size());
193 
194  for (int index = size - 2; index >= 0; --index) {
195  const size_t i = static_cast<size_t>(index);
196  const double t = slices[i].thickness();
197  const auto kz = coeff[i].getKz();
198  const Eigen::Matrix4cd l = coeff[i].R1 * GetImExponential(kz(0) * t)
199  + coeff[i].T1 * GetImExponential(-kz(0) * t)
200  + coeff[i].R2 * GetImExponential(kz(1) * t)
201  + coeff[i].T2 * GetImExponential(-kz(1) * t);
202  coeff[i].m_w_plus = l * coeff[i + 1].m_w_plus;
203  coeff[i].m_w_min = l * coeff[i + 1].m_w_min;
204 
205  // rotate and normalize polarization
206  const auto Snorm = findNormalizationCoefficients(coeff[i]);
207  auto S = Snorm.first;
208  auto norm = Snorm.second;
209 
210  SMatrices[i] = S;
211  Normalization[i] = norm;
212 
213  const complex_t a_plus = S(0, 0) / norm;
214  const complex_t b_plus = S(1, 0) / norm;
215  const complex_t a_min = S(0, 1) / norm;
216  const complex_t b_min = S(1, 1) / norm;
217 
218  const Eigen::Vector4cd w_plus = a_plus * coeff[i].m_w_plus + b_plus * coeff[i].m_w_min;
219  const Eigen::Vector4cd w_min = a_min * coeff[i].m_w_plus + b_min * coeff[i].m_w_min;
220 
221  coeff[i].m_w_plus = std::move(w_plus);
222  coeff[i].m_w_min = std::move(w_min);
223  }
224 
225  complex_t dumpingFactor = 1;
226  Eigen::Matrix2cd S = Eigen::Matrix2cd::Identity();
227  for (size_t i = 1; i < coeff.size(); ++i) {
228  dumpingFactor = dumpingFactor * Normalization[i - 1];
229  S = SMatrices[i - 1] * S;
230 
231  if (std::isinf(std::norm(dumpingFactor))) {
232  // not entirely sure, whether this is the correct edge case
233  std::for_each(coeff.begin() + i, coeff.end(),
234  [](auto& coeff) { setNoTransmission(coeff); });
235  break;
236  }
237 
238  const complex_t a_plus = S(0, 0) / dumpingFactor;
239  const complex_t b_plus = S(1, 0) / dumpingFactor;
240  const complex_t a_min = S(0, 1) / dumpingFactor;
241  const complex_t b_min = S(1, 1) / dumpingFactor;
242 
243  Eigen::Vector4cd w_plus = a_plus * coeff[i].m_w_plus + b_plus * coeff[i].m_w_min;
244  Eigen::Vector4cd w_min = a_min * coeff[i].m_w_plus + b_min * coeff[i].m_w_min;
245 
246  coeff[i].m_w_plus = std::move(w_plus);
247  coeff[i].m_w_min = std::move(w_min);
248  }
249 }
250 
251 std::pair<Eigen::Matrix2cd, complex_t>
253 {
254  const Eigen::Vector2cd Ta = coeff.T1plus() + coeff.T2plus();
255  const Eigen::Vector2cd Tb = coeff.T1min() + coeff.T2min();
256 
257  Eigen::Matrix2cd S;
258  S << Ta(0), Tb(0), Ta(1), Tb(1);
259 
260  Eigen::Matrix2cd SInverse;
261  SInverse << S(1, 1), -S(0, 1), -S(1, 0), S(0, 0);
262  const complex_t d1 = S(1, 1) - S(0, 1);
263  const complex_t d2 = S(1, 0) - S(0, 0);
264  const complex_t denominator = S(0, 0) * d1 - d2 * S(0, 1);
265 
266  return {SInverse, denominator};
267 }
268 
269 namespace {
270 kvector_t magneticImpact(kvector_t B_field)
271 {
272  return -magnetic_prefactor * B_field;
273 }
274 
275 Eigen::Vector2cd eigenvalues(complex_t kz, double b_mag)
276 {
277  const complex_t a = kz * kz;
278  return {I * std::sqrt(a + b_mag), I * std::sqrt(a - b_mag)};
279 }
280 
281 Eigen::Vector2cd checkForUnderflow(const Eigen::Vector2cd& eigenvs)
282 {
283  auto lambda = [](complex_t value) { return std::abs(value) < 1e-40 ? 1e-40 : value; };
284  return {lambda(eigenvs(0)), lambda(eigenvs(1))};
285 }
286 
287 complex_t GetImExponential(complex_t exponent)
288 {
289  if (exponent.imag() > -std::log(std::numeric_limits<double>::min()))
290  return 0.0;
291  return std::exp(I * exponent);
292 }
293 } // namespace
constexpr complex_t I
Definition: Complex.h:21
std::complex< double > complex_t
Definition: Complex.h:20
Declares functions in namespace KzComputation.
constexpr double magnetic_prefactor
Defines the values of physical constants (SI)
Defines class Slice.
Defines class SpecularMagneticStrategy_v2.
double mag() const
Returns magnitude of the vector.
T z() const
Returns z-component in cartesian coordinate system.
Definition: BasicVector3D.h:67
T y() const
Returns y-component in cartesian coordinate system.
Definition: BasicVector3D.h:65
T x() const
Returns x-component in cartesian coordinate system.
Definition: BasicVector3D.h:63
std::vector< std::unique_ptr< const ILayerRTCoefficients > > coeffs_t
Specular reflection and transmission coefficients in a layer in case of magnetic interactions between...
Eigen::Vector2cd T2plus() const override
Eigen::Vector2cd m_lambda
wave propagation direction (-1 for direct one, 1 for time reverse)
Eigen::Matrix4cd T2
matrix selecting the transmitted part of the second eigenmode
Eigen::Vector2cd T2min() const override
Eigen::Matrix4cd R1
matrix selecting the reflected part of the first eigenmode
Eigen::Vector2cd T1plus() const override
The following functions return the transmitted and reflected amplitudes for different incoming beam p...
Eigen::Vector4cd m_w_plus
boundary values for up-polarization
Eigen::Vector4cd m_w_min
boundary values for down-polarization
Eigen::Vector2cd T1min() const override
Eigen::Matrix4cd T1
matrix selecting the transmitted part of the first eigenmode
kvector_t m_b
normalized magnetic field impact (with correction for external mag. field)
Eigen::Matrix4cd R2
matrix selecting the reflected part of the second eigenmode
static void propagateBackwardsForwards(std::vector< MatrixRTCoefficients_v2 > &coeff, const std::vector< Slice > &slices)
Propagates boundary conditions from the bottom to the top of the layer stack.
static std::pair< Eigen::Matrix2cd, complex_t > findNormalizationCoefficients(const MatrixRTCoefficients_v2 &coeff)
finds linear coefficients for normalizing transmitted wave to unity.
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...
static std::vector< MatrixRTCoefficients_v2 > computeTR(const std::vector< Slice > &slices, const std::vector< complex_t > &kzs)
static void setNoTransmission(MatrixRTCoefficients_v2 &coeff)
static void calculateTR(MatrixRTCoefficients_v2 &coeff)
Computes frobenius matrices for multilayer solution.
static void calculateZeroFieldTR(MatrixRTCoefficients_v2 &coeff)
static void nullifyBottomReflection(MatrixRTCoefficients_v2 &coeff)
initializes reflectionless bottom boundary condition.
std::vector< complex_t > computeReducedKz(const std::vector< Slice > &slices, kvector_t k)
constexpr double g_factor_n
neutron g-factor
constexpr double h_bar
Reduced Plank constant, J s.
constexpr double m_n
Neutron mass, kg.
constexpr double mu_N
Nuclear magneton ( ), J/T.