BornAgain  1.18.0
Simulate and fit neutron and x-ray scattering at grazing incidence
SpecularMagneticNewStrategy.cpp
Go to the documentation of this file.
1 // ************************************************************************** //
2 //
3 // BornAgain: simulate and fit scattering at grazing incidence
4 //
5 //! @file Sample/Specular/SpecularMagneticNewStrategy.cpp
6 //! @brief Implements class SpecularMagneticNewStrategy.
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2020
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************** //
14 
19 #include "Sample/Slice/Slice.h"
20 
21 namespace
22 {
23 double magneticSLD(kvector_t B_field);
24 Eigen::Vector2cd eigenvalues(complex_t kz, double b_mag);
25 Eigen::Vector2cd checkForUnderflow(const Eigen::Vector2cd& eigenvs);
26 
27 // The factor 1e-18 is here to have unit: 1/T*nm^-2
29  / PhysConsts::h_bar / PhysConsts::h_bar / 4. / M_PI * 1e-18;
30 const auto eps = std::numeric_limits<double>::epsilon() * 10.;
31 const LayerRoughness* GetBottomRoughness(const std::vector<Slice>& slices,
32  const size_t slice_index);
33 } // namespace
34 
36  const kvector_t& k) const
37 {
38  return Execute(slices, KzComputation::computeReducedKz(slices, k));
39 }
40 
42 SpecularMagneticNewStrategy::Execute(const std::vector<Slice>& slices,
43  const std::vector<complex_t>& kz) const
44 {
45  if (slices.size() != kz.size())
46  throw std::runtime_error("Number of slices does not match the size of the kz-vector");
47 
49  for (auto& coeff : computeTR(slices, kz))
50  result.push_back(std::make_unique<MatrixRTCoefficients_v3>(coeff));
51 
52  return result;
53 }
54 
55 std::vector<MatrixRTCoefficients_v3>
56 SpecularMagneticNewStrategy::computeTR(const std::vector<Slice>& slices,
57  const std::vector<complex_t>& kzs) const
58 {
59  const size_t N = slices.size();
60 
61  if (slices.size() != kzs.size())
62  throw std::runtime_error(
63  "Error in SpecularMagnetic_::execute: kz vector and slices size shall coinside.");
64  if (slices.empty())
65  return {};
66 
67  std::vector<MatrixRTCoefficients_v3> result;
68  result.reserve(N);
69 
70  const double kz_sign = kzs.front().real() >= 0.0 ? 1.0 : -1.0; // save sign to restore it later
71 
72  auto B_0 = slices.front().bField();
73  result.emplace_back(kz_sign, eigenvalues(kzs.front(), 0.0), kvector_t{0.0, 0.0, 0.0}, 0.0);
74  for (size_t i = 1, size = slices.size(); i < size; ++i) {
75  auto B = slices[i].bField() - B_0;
76  auto magnetic_SLD = magneticSLD(B);
77  result.emplace_back(kz_sign, checkForUnderflow(eigenvalues(kzs[i], magnetic_SLD)),
78  B.mag() > eps ? B / B.mag() : kvector_t{0.0, 0.0, 0.0}, magnetic_SLD);
79  }
80 
81  if (N == 1) {
82  result[0].m_T = Eigen::Matrix2cd::Identity();
83  result[0].m_R = Eigen::Matrix2cd::Zero();
84  return result;
85 
86  } else if (kzs[0] == 0.) {
87  result[0].m_T = Eigen::Matrix2cd::Identity();
88  result[0].m_R = -Eigen::Matrix2cd::Identity();
89  for (size_t i = 1; i < N; ++i) {
90  result[i].m_T.setZero();
91  result[i].m_R.setZero();
92  }
93  return result;
94  }
95 
96  calculateUpwards(result, slices);
97 
98  return result;
99 }
100 
101 void SpecularMagneticNewStrategy::calculateUpwards(std::vector<MatrixRTCoefficients_v3>& coeff,
102  const std::vector<Slice>& slices) const
103 {
104  const auto N = slices.size();
105  std::vector<Eigen::Matrix2cd> SMatrices(N - 1);
106  std::vector<complex_t> Normalization(N - 1);
107 
108  // bottom boundary condition
109  coeff.back().m_T = Eigen::Matrix2cd::Identity();
110  coeff.back().m_R = Eigen::Matrix2cd::Zero();
111 
112  for (int i = N - 2; i >= 0; --i) {
113  double sigma = 0.;
114  if (const auto roughness = GetBottomRoughness(slices, i))
115  sigma = roughness->getSigma();
116 
117  // compute the 2x2 submatrices in the write-up denoted as P+, P- and delta
118  const auto mpmm = computeBackwardsSubmatrices(coeff[i], coeff[i + 1], sigma);
119  const Eigen::Matrix2cd mp = mpmm.first;
120  const Eigen::Matrix2cd mm = mpmm.second;
121 
122  const Eigen::Matrix2cd delta = coeff[i].computeDeltaMatrix(slices[i].thickness());
123 
124  // compute the rotation matrix
125  Eigen::Matrix2cd S, Si;
126  Si = mp + mm * coeff[i + 1].m_R;
127  S << Si(1, 1), -Si(0, 1), -Si(1, 0), Si(0, 0);
128  const complex_t norm = S(0, 0) * S(1, 1) - S(0, 1) * S(1, 0);
129  S = S * delta;
130 
131  // store the rotation matrix and normalization constant in order to rotate
132  // the coefficients for all lower slices at the end of the computation
133  SMatrices[i] = S;
134  Normalization[i] = norm;
135 
136  // compute the reflection matrix and
137  // rotate the polarization such that we have pure incoming states (T = I)
138  S /= norm;
139 
140  // T is always equal to the identity at this point, no need to store
141  coeff[i].m_R = delta * (mm + mp * coeff[i + 1].m_R) * S;
142  }
143 
144  // now correct all amplitudes in forward direction by dividing with the remaining
145  // normalization constants. In addition rotate the polarization by the amount
146  // that was rotated above the current interface
147  // if the normalization overflows, all amplitudes below that point are set to zero
148  complex_t dumpingFactor = 1;
149  Eigen::Matrix2cd S = Eigen::Matrix2cd::Identity();
150  for (size_t i = 1; i < N; ++i) {
151  dumpingFactor = dumpingFactor * Normalization[i - 1];
152  S = SMatrices[i - 1] * S;
153 
154  if (std::isinf(std::norm(dumpingFactor))) {
155  std::for_each(coeff.begin() + i, coeff.end(), [](auto& coeff) {
156  coeff.m_T = Eigen::Matrix2cd::Zero();
157  coeff.m_R = Eigen::Matrix2cd::Zero();
158  });
159  break;
160  }
161 
162  coeff[i].m_T = S / dumpingFactor; // T * S omitted, since T is always I
163  coeff[i].m_R *= S / dumpingFactor;
164  }
165 }
166 
167 namespace
168 {
169 double magneticSLD(kvector_t B_field)
170 {
171  return magnetic_prefactor * B_field.mag();
172 }
173 
174 Eigen::Vector2cd eigenvalues(complex_t kz, double magnetic_SLD)
175 {
176  const complex_t a = kz * kz;
177  return {std::sqrt(a - 4. * M_PI * magnetic_SLD), std::sqrt(a + 4. * M_PI * magnetic_SLD)};
178 }
179 
180 Eigen::Vector2cd checkForUnderflow(const Eigen::Vector2cd& eigenvs)
181 {
182  auto lambda = [](complex_t value) { return std::abs(value) < 1e-40 ? 1e-40 : value; };
183  return {lambda(eigenvs(0)), lambda(eigenvs(1))};
184 }
185 
186 const LayerRoughness* GetBottomRoughness(const std::vector<Slice>& slices, const size_t slice_index)
187 {
188  if (slice_index + 1 < slices.size())
189  return slices[slice_index + 1].topRoughness();
190  return nullptr;
191 }
192 } // namespace
std::complex< double > complex_t
Definition: Complex.h:20
Declares functions in namespace KzComputation.
Defines class LayerRoughness.
constexpr double magnetic_prefactor
#define M_PI
Definition: MathConstants.h:39
Defines the values of physical constants (SI)
Defines class Slice.
Defines class SpecularMagneticNewStrategy.
double mag() const
Returns magnitude of the vector.
std::vector< std::unique_ptr< const ILayerRTCoefficients > > coeffs_t
A roughness of interface between two layers.
virtual std::pair< Eigen::Matrix2cd, Eigen::Matrix2cd > computeBackwardsSubmatrices(const MatrixRTCoefficients_v3 &coeff_i, const MatrixRTCoefficients_v3 &coeff_i1, double sigma) const =0
void calculateUpwards(std::vector< MatrixRTCoefficients_v3 > &coeff, const std::vector< Slice > &slices) const
std::vector< MatrixRTCoefficients_v3 > computeTR(const std::vector< Slice > &slices, const std::vector< complex_t > &kzs) const
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...
std::vector< complex_t > computeReducedKz(const std::vector< Slice > &slices, kvector_t k)
double Si(double x)
Sine integral function: .
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.
Eigen::Vector2cd eigenvalues(complex_t kz, double b_mag)
const LayerRoughness * GetBottomRoughness(const std::vector< Slice > &slices, const size_t slice_index)
Eigen::Vector2cd checkForUnderflow(const Eigen::Vector2cd &eigenvs)