BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
ComputeFluxMagnetic.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Resample/Specular/ComputeFluxMagnetic.cpp
6 //! @brief Implements class SpecularMagneticStrategy.
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 
17 #include "Base/Util/Assert.h"
24 #include <algorithm>
25 
26 namespace {
27 
28 // The factor 1e-18 is here to have unit: 1/T*nm^-2
30  / PhysConsts::h_bar / PhysConsts::h_bar / 4. / M_PI * 1e-18;
31 const auto eps = std::numeric_limits<double>::epsilon() * 10.;
32 
33 double magneticSLD(R3 B_field)
34 {
35  return magnetic_prefactor * B_field.mag();
36 }
37 
38 Spinor eigenvalues(complex_t kz, double magnetic_SLD)
39 {
40  const complex_t a = kz * kz;
41  return {std::sqrt(a - 4. * M_PI * magnetic_SLD), std::sqrt(a + 4. * M_PI * magnetic_SLD)};
42 }
43 
44 Spinor checkForUnderflow(const Spinor& eigenvs)
45 {
46  auto lambda = [](complex_t value) { return std::abs(value) < 1e-40 ? 1e-40 : value; };
47  return {lambda(eigenvs.u), lambda(eigenvs.v)};
48 }
49 
50 std::pair<SpinMatrix, SpinMatrix> computeBackwardsSubmatrices(const MatrixFlux& coeff_i,
51  const MatrixFlux& coeff_i1,
52  double sigma,
53  const RoughnessModel& r_model)
54 {
55  if (r_model == RoughnessModel::NEVOT_CROCE)
57  sigma);
58  return Compute::TransitionMagneticTanh::backwardsSubmatrices(coeff_i, coeff_i1, sigma);
59 }
60 
61 void calculateUpwards(std::vector<MatrixFlux>& coeff, const SliceStack& slices,
62  const RoughnessModel& r_model)
63 {
64  const auto N = slices.size();
65  std::vector<SpinMatrix> SMatrices(N - 1);
66  std::vector<complex_t> Normalization(N - 1);
67 
68  // bottom boundary condition
69  coeff.back().m_T = SpinMatrix::One();
70  coeff.back().m_R = SpinMatrix();
71 
72  for (size_t i = N - 1; i > 0; --i) {
73  double sigma = 0.;
74  if (const auto* const roughness = slices.bottomRoughness(i))
75  sigma = roughness->sigma();
76 
77  // compute the 2x2 submatrices in the write-up denoted as P+, P- and delta
78  const auto [mp, mm] = computeBackwardsSubmatrices(coeff[i - 1], coeff[i], sigma, r_model);
79 
80  const SpinMatrix delta = coeff[i - 1].computeDeltaMatrix(slices[i - 1].thicknessOr0());
81 
82  // compute the rotation matrix
83  SpinMatrix Si = mp + mm * coeff[i].m_R;
84  SpinMatrix S(Si.d, -Si.b, -Si.c, Si.a);
85  const complex_t norm = S.determinant();
86  S = S * delta;
87 
88  // store the rotation matrix and normalization constant in order to rotate
89  // the coefficients for all lower slices at the end of the computation
90  SMatrices[i - 1] = S;
91  Normalization[i - 1] = norm;
92 
93  // compute the reflection matrix and
94  // rotate the polarization such that we have pure incoming states (T = I)
95  S /= norm;
96 
97  // T is always equal to the identity at this point, no need to store
98  coeff[i - 1].m_R = delta * (mm + mp * coeff[i].m_R) * S;
99  }
100 
101  // now correct all amplitudes in forward direction by dividing with the remaining
102  // normalization constants. In addition rotate the polarization by the amount
103  // that was rotated above the current interface
104  // if the normalization overflows, all amplitudes below that point are set to zero
105  complex_t dumpingFactor = 1;
107  for (size_t i = 1; i < N; ++i) {
108  dumpingFactor = dumpingFactor * Normalization[i - 1];
109  S = SMatrices[i - 1] * S;
110 
111  if (std::isinf(std::norm(dumpingFactor))) {
112  std::for_each(coeff.begin() + i, coeff.end(), [](auto& coeff) {
113  coeff.m_T = SpinMatrix();
114  coeff.m_R = SpinMatrix();
115  });
116  break;
117  }
118 
119  coeff[i].m_T = S / dumpingFactor; // T * S omitted, since T is always I
120  coeff[i].m_R *= S / dumpingFactor;
121  }
122 }
123 
124 std::vector<MatrixFlux> computeFlux(const SliceStack& slices, const std::vector<complex_t>& kzs,
125  const RoughnessModel& r_model, bool forward)
126 {
127  const size_t N = slices.size();
128 
129  if (slices.size() != kzs.size())
130  throw std::runtime_error(
131  "Error in SpecularMagnetic_::execute: kz vector and slices size shall coinside.");
132  if (slices.empty())
133  return {};
134 
135  std::vector<MatrixFlux> result;
136  result.reserve(N);
137 
138  const double kz_sign = kzs.front().real() >= 0.0 ? 1.0 : -1.0; // save sign to restore it later
139 
140  auto B_0 = slices.front().bField();
141  result.emplace_back(kz_sign, eigenvalues(kzs.front(), 0.0), R3{0.0, 0.0, 0.0}, 0.0);
142  for (size_t i = 1; i < slices.size(); ++i) {
143  auto B = (forward ? +1 : -1) * (slices[i].bField() - B_0);
144  auto magnetic_SLD = magneticSLD(B);
145  result.emplace_back(kz_sign, checkForUnderflow(eigenvalues(kzs[i], magnetic_SLD)),
146  B.mag() > eps ? B / B.mag() : R3{0.0, 0.0, 0.0}, magnetic_SLD);
147  }
148 
149  if (N == 1) {
150  result[0].m_T = SpinMatrix::One();
151  result[0].m_R = SpinMatrix();
152  return result;
153  }
154 
155  if (kzs[0] == 0.) {
156  result[0].m_T = SpinMatrix::One();
157  result[0].m_R = SpinMatrix::Diag(-1, -1);
158  for (size_t i = 1; i < N; ++i) {
159  result[i].m_T = SpinMatrix();
160  result[i].m_R = SpinMatrix();
161  }
162  return result;
163  }
164 
165  calculateUpwards(result, slices, r_model);
166 
167  return result;
168 }
169 
170 } // namespace
171 
172 // ************************************************************************************************
173 // namespace implementation
174 // ************************************************************************************************
175 
176 Fluxes Compute::SpecularMagnetic::fluxes(const SliceStack& slices, const R3& k, bool forward)
177 {
178  if (slices.size() > 1 && k.z() > 0)
179  throw std::runtime_error(
180  "source or detector below horizon not yet implemented for polarized scattering");
181  std::vector<complex_t> kz = Compute::Kz::computeReducedKz(slices, k);
182  ASSERT(slices.size() == kz.size());
183 
184  Fluxes result;
185  for (auto& coeff : computeFlux(slices, kz, slices.roughnessModel(), forward))
186  result.emplace_back(std::make_unique<const MatrixFlux>(coeff));
187 
188  return result;
189 }
190 
192  const std::vector<complex_t>& kzs, bool forward)
193 {
194  const RoughnessModel& r_model = slices.roughnessModel();
195 
196  ASSERT(slices.size() == kzs.size());
197  const auto N = slices.size();
198  if (N == 1)
199  return SpinMatrix();
200  if (kzs[0] == 0.)
201  return -SpinMatrix::One();
202 
203  auto B_0 = slices.front().bField();
204  const double kz_sign = kzs.front().real() >= 0.0 ? 1.0 : -1.0; // save sign to restore it later
205 
206  auto createCoeff = [&slices, &kzs, kz_sign, B_0, forward](size_t i) {
207  const auto B = (forward ? +1 : -1) * (slices[i].bField() - B_0);
208  const auto magnetic_SLD = magneticSLD(B);
209 
210  return MatrixFlux(kz_sign, checkForUnderflow(eigenvalues(kzs[i], magnetic_SLD)),
211  B.mag() > eps ? B / B.mag() : R3{0.0, 0.0, 0.0}, magnetic_SLD);
212  };
213 
214  auto c_i1 = createCoeff(N - 1);
215 
216  // bottom boundary condition
217  c_i1.m_R = SpinMatrix();
218 
219  for (size_t i = N - 1; i > 0; --i) {
220  auto c_i = createCoeff(i - 1);
221 
222  double sigma = 0.;
223  if (const auto* const roughness = slices.bottomRoughness(i - 1))
224  sigma = roughness->sigma();
225 
226  // compute the 2x2 submatrices in the write-up denoted as P+, P- and delta
227  const auto [mp, mm] = computeBackwardsSubmatrices(c_i, c_i1, sigma, r_model);
228 
229  const SpinMatrix delta = c_i.computeDeltaMatrix(slices[i - 1].thicknessOr0());
230 
231  // compute the rotation matrix
232  SpinMatrix Si = mp + mm * c_i1.m_R;
233  SpinMatrix S(Si.d, -Si.b, -Si.c, Si.a);
234  const complex_t norm = S.determinant();
235  S = S * (delta / norm);
236 
237  c_i.m_R = delta * (mm + mp * c_i1.m_R) * S;
238  c_i1 = c_i;
239  }
240  return c_i1.m_R;
241 }
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:45
Defines namespace Compute::SpecularMagnetic.
#define M_PI
Definition: Constants.h:44
std::vector< std::unique_ptr< const IFlux > > Fluxes
Declares functions in namespace ComputateKz.
Defines class LayerRoughness.
constexpr double magnetic_prefactor
Defines class MatrixFlux.
Defines the values of physical constants (SI)
Defines class SliceStack.
Defines namespace MagneticNevotCroceTransition.
Defines namespace TransitionMagneticTanh.
Specular reflection and transmission coefficients in a layer in case of magnetic interactions between...
Definition: MatrixFlux.h:32
A stack of Slices.
Definition: SliceStack.h:38
const LayerRoughness * bottomRoughness(size_t i_slice) const
Definition: SliceStack.cpp:75
RoughnessModel roughnessModel() const
Definition: SliceStack.h:57
complex_t a
Definition: SpinMatrix.h:68
complex_t b
Definition: SpinMatrix.h:68
complex_t c
Definition: SpinMatrix.h:68
static SpinMatrix One()
Definition: SpinMatrix.cpp:36
static SpinMatrix Diag(complex_t a_, complex_t d_)
Definition: SpinMatrix.cpp:31
complex_t d
Definition: SpinMatrix.h:68
complex_t determinant() const
Definition: SpinMatrix.cpp:170
Definition: Spinor.h:20
complex_t v
Definition: Spinor.h:31
complex_t u
Definition: Spinor.h:31
#define N
Definition: mixmax.h:31
std::vector< complex_t > computeReducedKz(const SliceStack &slices, R3 k)
Computes kz values from known k vector and slices with the following assumptions:
std::pair< SpinMatrix, SpinMatrix > backwardsSubmatrices(const MatrixFlux &coeff_i, const MatrixFlux &coeff_i1, double sigma)
Fluxes fluxes(const SliceStack &slices, const R3 &k, bool forward)
Computes refraction angle reflection/transmission coefficients for given sliced sample and wavevector...
SpinMatrix topLayerR(const SliceStack &slices, const std::vector< complex_t > &kzs, bool forward)
Computes the Fresnel R coefficient for the top layer only Introduced in order to speed up pure reflec...
std::pair< SpinMatrix, SpinMatrix > backwardsSubmatrices(const MatrixFlux &coeff_i, const MatrixFlux &coeff_i1, double sigma)
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.