BornAgain  1.19.79
Open-source research software to simulate and fit neutron and x-ray reflectometry and grazing-incidence small-angle scattering
ComputeFluxScalar.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file Resample/Specular/ComputeFluxScalar.cpp
6 //! @brief Implements class SpecularScalarStrategy.
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 
16 #include "Base/Math/Constants.h"
17 #include "Base/Math/Functions.h"
18 #include "Base/Util/Assert.h"
24 #include <stdexcept>
25 
26 namespace {
27 
28 std::pair<complex_t, complex_t> transition(complex_t kzi, complex_t kzi1, double sigma,
29  const RoughnessModel& r_model)
30 {
31  if (r_model == RoughnessModel::NEVOT_CROCE) {
32  // Roughness is modelled by a Gaussian profile, i.e. Nevot-Croce factors for the
33  // reflection coefficients.
34  // Implementation follows A. Gibaud and G. Vignaud, in X-ray and Neutron Reflectivity,
35  // edited by J. Daillant and A. Gibaud, volume 770 of Lecture Notes in Physics (2009)
36  complex_t roughness_diff = 1;
37  complex_t roughness_sum = 1;
38  if (sigma > 0.0) {
39  roughness_diff = std::exp(-(kzi1 - kzi) * (kzi1 - kzi) * sigma * sigma / 2.);
40  roughness_sum = std::exp(-(kzi1 + kzi) * (kzi1 + kzi) * sigma * sigma / 2.);
41  }
42  const complex_t kz_ratio = kzi1 / kzi;
43 
44  const complex_t a00 = 0.5 * (1. + kz_ratio) * roughness_diff;
45  const complex_t a01 = 0.5 * (1. - kz_ratio) * roughness_sum;
46 
47  return {a00, a01};
48  }
49  // TANH model assumed
50  // Roughness is modelled by tanh profile
51  // [e.g. Bahr, Press, et al, Phys. Rev. B, vol. 47 (8), p. 4385 (1993)].
52  complex_t roughness = 1;
53  if (sigma > 0.0) {
54  const double sigeff = std::pow(M_PI_2, 1.5) * sigma;
55  roughness = std::sqrt(Math::tanhc(sigeff * kzi1) / Math::tanhc(sigeff * kzi));
56  }
57  const complex_t inv_roughness = 1.0 / roughness;
58  const complex_t kz_ratio = kzi1 / kzi * roughness;
59 
60  const complex_t a00 = 0.5 * (inv_roughness + kz_ratio);
61  const complex_t a01 = 0.5 * (inv_roughness - kz_ratio);
62 
63  return {a00, a01};
64 }
65 
66 std::vector<Spinor> computeTR(const SliceStack& slices, const std::vector<complex_t>& kz,
67  const RoughnessModel& r_model, bool top_exit)
68 {
69  const size_t N = slices.size();
70  std::vector<Spinor> TR(N, {1., 0.});
71 
72  if (N == 1) // If only one layer present, there's nothing left to calculate
73  return TR;
74 
75  // Index reversal for bottom exit
76  std::vector<size_t> X(N, 0);
77  for (size_t i = 0; i < N; ++i)
78  X[i] = top_exit ? i : N - 1 - i;
79 
80  if (kz[X[0]] == 0.0) { // If kz in layer 0 is zero, R0 = -T0 and all others equal to 0
81  TR[X[0]] = {1.0, -1.0};
82  for (size_t i = 1; i < N; ++i)
83  TR[X[i]] = Spinor(0, 0);
84  return TR;
85  }
86 
87  // Calculate transmission/refraction coefficients t_r for each layer, from bottom to top.
88  TR[X[N - 1]] = {1.0, 0.0};
89  std::vector<complex_t> factors(N - 1);
90  for (size_t i = N - 1; i > 0; i--) {
91  size_t jthis = X[i - 1];
92  size_t jlast = X[i];
93  const auto* roughness = slices.bottomRoughness(jthis); // TODO verify
94  const double sigma = roughness ? roughness->sigma() : 0.;
95 
96  const auto [mp, mm] = transition(kz[jthis], kz[jlast], sigma, r_model);
97 
98  const complex_t delta = exp_I(kz[jthis] * slices[jthis].thicknessOr0());
99 
100  complex_t S = delta / (mp + mm * TR[jlast].v);
101  factors[i - 1] = S;
102  TR[jthis].v = delta * (mm + mp * TR[jlast].v) * S;
103  }
104 
105  // Now correct all amplitudes by dividing by the remaining factors in forward direction.
106  // At some point this divison underflows; from there on all further amplitudes are set to zero.
107  complex_t dampingFactor = 1;
108  for (size_t i = 1; i < N; ++i) {
109  dampingFactor = dampingFactor * factors[i - 1];
110  TR[X[i]] = Spinor(dampingFactor, TR[X[i]].v * dampingFactor);
111  }
112 
113  return TR;
114 }
115 
116 } // namespace
117 
118 // ************************************************************************************************
119 // namespace implementation
120 // ************************************************************************************************
121 
123 {
124  const bool top_exit = k.z() <= 0; // true if source or detector pixel are at z>=0
125  std::vector<complex_t> kz = Compute::Kz::computeReducedKz(slices, k);
126  ASSERT(slices.size() == kz.size());
127 
128  const std::vector<Spinor> TR = computeTR(slices, kz, slices.roughnessModel(), top_exit);
129 
130  Fluxes result;
131  for (size_t i = 0; i < kz.size(); ++i)
132  result.emplace_back(std::make_unique<const ScalarFlux>(kz[i], TR[i]));
133  return result;
134 }
135 
137  const std::vector<complex_t>& kz)
138 {
139  const RoughnessModel& r_model = slices.roughnessModel();
140 
141  ASSERT(slices.size() == kz.size());
142  const size_t N = slices.size();
143  if (N == 1)
144  return 0.; // only one layer present, there's nothing left to calculate
145  if (kz[0] == 0.)
146  return -1.;
147 
148  complex_t R_i1 = 0.;
149 
150  for (size_t i = N - 1; i > 0; i--) {
151  double sigma = 0.0;
152  if (const auto* const roughness = slices.bottomRoughness(i - 1))
153  sigma = roughness->sigma();
154 
155  const auto [mp, mm] = transition(kz[i - 1], kz[i], sigma, r_model);
156 
157  const complex_t delta = exp_I(kz[i - 1] * slices[i - 1].thicknessOr0());
158 
159  R_i1 = pow(delta, 2) * (mm + mp * R_i1) / (mp + mm * R_i1);
160  }
161 
162  return R_i1;
163 }
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:45
Defines namespace Compute::SpecularScalar.
Defines M_PI and some more mathematical constants.
#define M_PI_2
Definition: Constants.h:45
std::vector< std::unique_ptr< const IFlux > > Fluxes
Defines namespace Math.
Declares functions in namespace ComputateKz.
Defines class LayerRoughness.
Defines class Layer.
Defines class ScalarFlux.
Defines class SliceStack.
double sigma() const
Returns rms of roughness.
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
Definition: Spinor.h:20
#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:
complex_t topLayerR(const SliceStack &slices, const std::vector< complex_t > &kz)
Computes the Fresnel R coefficient for the top layer only. Introduced in order to speed up pure refle...
Fluxes fluxes(const SliceStack &slices, const R3 &k)
Computes refraction angles and transmission/reflection coefficients for given coherent wave propagati...
complex_t tanhc(complex_t z)
Complex tanhc function: .
Definition: Functions.cpp:70