23 Eigen::Vector2cd eigenvalues(complex_t kz,
double b_mag);
24 Eigen::Vector2cd checkForUnderflow(
const Eigen::Vector2cd& eigenvs);
25 complex_t GetImExponential(complex_t exponent);
35 return Execute(slices, KzComputation::computeReducedKz(slices, k));
38 ISpecularStrategy::coeffs_t
40 const std::vector<complex_t>& kz)
const
42 if (slices.size() != kz.size())
43 throw std::runtime_error(
"Number of slices does not match the size of the kz-vector");
45 ISpecularStrategy::coeffs_t result;
46 for (
auto& coeff : computeTR(slices, kz))
47 result.push_back(std::make_unique<MatrixRTCoefficients_v2>(coeff));
52 std::vector<MatrixRTCoefficients_v2>
53 SpecularMagneticStrategy::computeTR(
const std::vector<Slice>& slices,
54 const std::vector<complex_t>& kzs)
57 throw std::runtime_error(
"Edge case k_z = 0 not implemented");
59 if (slices.size() != kzs.size())
60 throw std::runtime_error(
61 "Error in SpecularMagnetic_::execute: kz vector and slices size shall coinside.");
65 std::vector<MatrixRTCoefficients_v2> result;
66 result.reserve(slices.size());
68 const double kz_sign = kzs.front().real() > 0.0 ? 1.0 : -1.0;
69 const kvector_t b_0 = magneticImpact(slices.front().bField());
70 result.emplace_back(kz_sign, eigenvalues(kzs.front(), 0.0),
kvector_t{0.0, 0.0, 0.0});
71 for (
size_t i = 1, size = slices.size(); i < size; ++i) {
72 kvector_t b = magneticImpact(slices[i].bField()) - b_0;
73 result.emplace_back(kz_sign, checkForUnderflow(eigenvalues(kzs[i], b.
mag())), b);
76 if (result.front().m_lambda == Eigen::Vector2cd::Zero()) {
77 std::for_each(result.begin(), result.end(), [](
auto& coeff) { setNoTransmission(coeff); });
81 std::for_each(result.begin(), result.end(), [](
auto& coeff) { calculateTR(coeff); });
82 nullifyBottomReflection(result.back());
83 propagateBackwardsForwards(result, slices);
90 const double b = coeff.m_b.
mag();
92 calculateZeroFieldTR(coeff);
96 const double bpbz = b + coeff.m_b.
z();
97 const double bmbz = b - coeff.m_b.
z();
98 const complex_t bxmby = coeff.m_b.
x() - I * coeff.m_b.
y();
99 const complex_t bxpby = coeff.m_b.
x() + I * coeff.m_b.
y();
100 const complex_t l_1 = coeff.m_lambda(0);
101 const complex_t l_2 = coeff.m_lambda(1);
104 T1 << bmbz, -bxmby, -bmbz * l_1, bxmby * l_1, -bxpby, bpbz, bxpby * l_1, -bpbz * l_1,
105 -bmbz / l_1, bxmby / l_1, bmbz, -bxmby, bxpby / l_1, -bpbz / l_1, -bxpby, bpbz;
109 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),
110 -T1(2, 0), -T1(2, 1), T1(2, 2), T1(2, 3), -T1(3, 0), -T1(3, 1), T1(3, 2), T1(3, 3);
113 T2 << bpbz, bxmby, -bpbz * l_2, -bxmby * l_2, bxpby, bmbz, -bxpby * l_2, -bmbz * l_2,
114 -bpbz / l_2, -bxmby / l_2, bpbz, bxmby, -bxpby / l_2, -bmbz / l_2, bxpby, bmbz;
118 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),
119 -T2(2, 0), -T2(2, 1), T2(2, 2), T2(2, 3), -T2(3, 0), -T2(3, 1), T2(3, 2), T2(3, 3);
124 coeff.T1 = Eigen::Matrix4cd::Zero();
125 coeff.R1 = Eigen::Matrix4cd::Zero();
126 coeff.T2 = Eigen::Matrix4cd::Zero();
127 coeff.R2 = Eigen::Matrix4cd::Zero();
130 const complex_t eigen_value = coeff.m_lambda(0);
132 Eigen::Matrix3cd Tblock;
133 Tblock << 0.5, 0.0, -0.5 * eigen_value, 0.0, 0.0, 0.0, -0.5 / eigen_value, 0.0, 0.5;
135 Eigen::Matrix3cd Rblock;
136 Rblock << 0.5, 0.0, 0.5 * eigen_value, 0.0, 0.0, 0.0, 0.5 / eigen_value, 0.0, 0.5;
138 coeff.T1.block<3, 3>(1, 1) = Tblock;
139 coeff.R1.block<3, 3>(1, 1) = Rblock;
140 coeff.T2.block<3, 3>(0, 0) = Tblock;
141 coeff.R2.block<3, 3>(0, 0) = Rblock;
146 coeff.m_w_plus = Eigen::Vector4cd::Zero();
147 coeff.m_w_min = Eigen::Vector4cd::Zero();
148 coeff.T1 = Eigen::Matrix4cd::Identity() / 4.0;
156 const complex_t l_1 = coeff.m_lambda(0);
157 const complex_t l_2 = coeff.m_lambda(1);
158 const double b_mag = coeff.m_b.
mag();
163 coeff.m_w_plus << -l_1, 0.0, 1.0, 0.0;
164 coeff.m_w_min << 0.0, -l_1, 0.0, 1.0;
169 coeff.m_w_min(0) = (b.
x() - I * b.
y()) * (l_1 - l_2) / 2.0 / b_mag;
170 coeff.m_w_min(1) = b.
z() * (l_2 - l_1) / 2.0 / b_mag - (l_1 + l_2) / 2.0;
171 coeff.m_w_min(2) = 0.0;
172 coeff.m_w_min(3) = 1.0;
175 coeff.m_w_plus(0) = -(l_1 + l_2) / 2.0 - b.
z() / (l_1 + l_2);
176 coeff.m_w_plus(1) = (b.
x() + I * b.
y()) * (l_1 - l_2) / 2.0 / b_mag;
177 coeff.m_w_plus(2) = 1.0;
178 coeff.m_w_plus(3) = 0.0;
181 void SpecularMagneticStrategy::propagateBackwardsForwards(
182 std::vector<MatrixRTCoefficients_v2>& coeff,
const std::vector<Slice>& slices)
184 const int size =
static_cast<int>(coeff.size());
185 std::vector<Eigen::Matrix2cd> SMatrices(coeff.size());
186 std::vector<complex_t> Normalization(coeff.size());
188 for (
int index = size - 2; index >= 0; --index) {
189 const size_t i =
static_cast<size_t>(index);
190 const double t = slices[i].thickness();
191 const auto kz = coeff[i].getKz();
192 const Eigen::Matrix4cd l = coeff[i].R1 * GetImExponential(kz(0) * t)
193 + coeff[i].T1 * GetImExponential(-kz(0) * t)
194 + coeff[i].R2 * GetImExponential(kz(1) * t)
195 + coeff[i].T2 * GetImExponential(-kz(1) * t);
196 coeff[i].m_w_plus = l * coeff[i + 1].m_w_plus;
197 coeff[i].m_w_min = l * coeff[i + 1].m_w_min;
200 const auto Snorm = findNormalizationCoefficients(coeff[i]);
201 auto S = Snorm.first;
202 auto norm = Snorm.second;
205 Normalization[i] = norm;
207 const complex_t a_plus = S(0, 0) / norm;
208 const complex_t b_plus = S(1, 0) / norm;
209 const complex_t a_min = S(0, 1) / norm;
210 const complex_t b_min = S(1, 1) / norm;
212 const Eigen::Vector4cd w_plus = a_plus * coeff[i].m_w_plus + b_plus * coeff[i].m_w_min;
213 const Eigen::Vector4cd w_min = a_min * coeff[i].m_w_plus + b_min * coeff[i].m_w_min;
215 coeff[i].m_w_plus = std::move(w_plus);
216 coeff[i].m_w_min = std::move(w_min);
219 complex_t dumpingFactor = 1;
220 Eigen::Matrix2cd S = Eigen::Matrix2cd::Identity();
221 for (
size_t i = 1; i < coeff.size(); ++i) {
222 dumpingFactor = dumpingFactor * Normalization[i - 1];
223 S = SMatrices[i - 1] * S;
225 if (std::isinf(std::norm(dumpingFactor))) {
227 std::for_each(coeff.begin() + i, coeff.end(),
228 [](
auto& coeff) { setNoTransmission(coeff); });
232 const complex_t a_plus = S(0, 0) / dumpingFactor;
233 const complex_t b_plus = S(1, 0) / dumpingFactor;
234 const complex_t a_min = S(0, 1) / dumpingFactor;
235 const complex_t b_min = S(1, 1) / dumpingFactor;
237 Eigen::Vector4cd w_plus = a_plus * coeff[i].m_w_plus + b_plus * coeff[i].m_w_min;
238 Eigen::Vector4cd w_min = a_min * coeff[i].m_w_plus + b_min * coeff[i].m_w_min;
240 coeff[i].m_w_plus = std::move(w_plus);
241 coeff[i].m_w_min = std::move(w_min);
245 std::pair<Eigen::Matrix2cd, complex_t>
248 const Eigen::Vector2cd Ta = coeff.
T1plus() + coeff.T2plus();
249 const Eigen::Vector2cd Tb = coeff.T1min() + coeff.T2min();
252 S << Ta(0), Tb(0), Ta(1), Tb(1);
254 Eigen::Matrix2cd SInverse;
255 SInverse << S(1, 1), -S(0, 1), -S(1, 0), S(0, 0);
256 const complex_t d1 = S(1, 1) - S(0, 1);
257 const complex_t d2 = S(1, 0) - S(0, 0);
258 const complex_t denominator = S(0, 0) * d1 - d2 * S(0, 1);
260 return {SInverse, denominator};
267 return -magnetic_prefactor * B_field;
270 Eigen::Vector2cd eigenvalues(complex_t kz,
double b_mag)
272 const complex_t a = kz * kz;
273 return {I * std::sqrt(a + b_mag), I * std::sqrt(a - b_mag)};
276 Eigen::Vector2cd checkForUnderflow(
const Eigen::Vector2cd& eigenvs)
278 auto lambda = [](complex_t value) {
return std::abs(value) < 1e-40 ? 1e-40 : value; };
279 return {lambda(eigenvs(0)), lambda(eigenvs(1))};
282 complex_t GetImExponential(complex_t exponent)
284 if (exponent.imag() > -std::log(std::numeric_limits<double>::min()))
286 return std::exp(I * exponent);
Declares functions in namespace KzComputation.
Defines the values of physical constants (SI)
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.
Defines class SpecularMagneticStrategy.
double mag() const
Returns magnitude of the vector.
T z() const
Returns z-component in cartesian coordinate system.
T y() const
Returns y-component in cartesian coordinate system.
T x() const
Returns x-component in cartesian coordinate system.
Specular reflection and transmission coefficients in a layer in case of magnetic interactions between...
Eigen::Vector2cd T1plus() const override
The following functions return the transmitted and reflected amplitudes for different incoming beam p...
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...