File: KzComputation.cpp

package info (click to toggle)
bornagain 23.0-4
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid
  • size: 103,936 kB
  • sloc: cpp: 423,131; python: 40,997; javascript: 11,167; awk: 630; sh: 318; ruby: 173; xml: 130; makefile: 51; ansic: 24
file content (99 lines) | stat: -rw-r--r-- 3,418 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//  ************************************************************************************************
//
//  BornAgain: simulate and fit reflection and scattering
//
//! @file      Resample/Slice/KzComputation.cpp
//! @brief     Implements functions in namespace Compute::Kz.
//!
//! @homepage  http://www.bornagainproject.org
//! @license   GNU General Public License v3 or higher (see COPYING)
//! @copyright Forschungszentrum Jülich GmbH 2018
//! @authors   Scientific Computing Group at MLZ (see CITATION, AUTHORS)
//
//  ************************************************************************************************

#include "Resample/Slice/KzComputation.h"
#include "Base/Const/Units.h"
#include "Base/Util/Assert.h"
#include "Resample/Slice/SliceStack.h"
#include <numbers>

using std::numbers::pi;

namespace {

// Returns normalized SLD in nm^-2
complex_t normalizedSLD(const Material& material)
{
    ASSERT(material.typeID() == MATERIAL_TYPES::MaterialBySLD);

    complex_t sld =
        std::conj(material.refractiveIndex_or_SLD()) / (Units::angstrom * Units::angstrom);
    sld *= 4.0 * pi;
    return sld;
}

complex_t checkForUnderflow(complex_t val)
{
    return std::norm(val) < 1e-80 ? complex_t(0.0, 1e-40) : val;
}

} // namespace

//  ************************************************************************************************
// namespace KzComputation
//  ************************************************************************************************

std::vector<complex_t> Compute::Kz::computeReducedKz(const SliceStack& slices, R3 k)
{
    const size_t N = slices.size();

    const size_t i_ref = k.z() > 0. ? N - 1 : 0;
    const double n_ref = slices[i_ref].material().refractiveIndex((2 * pi) / k.mag()).real();
    const double k_base = k.mag() * (k.z() > 0.0 ? -1 : 1);

    std::vector<complex_t> result(N);
    // Calculate refraction angle, expressed as k_z, for each layer.
    for (size_t i = 0; i < N; ++i) {
        complex_t rad = slices[i].scalarReducedPotential(k, n_ref);
        if (i != i_ref)
            rad = checkForUnderflow(rad);
        result[i] = k_base * std::sqrt(rad);
    }
    return result;
}

std::vector<complex_t> Compute::Kz::computeKzFromSLDs(const SliceStack& slices, double kz)
{
    const size_t N = slices.size();
    const double k_sign = kz > 0.0 ? -1 : 1;
    complex_t kz2_base = kz * kz + normalizedSLD(slices[0].material());

    std::vector<complex_t> result(N);
    result[0] = -kz;
    // Calculate refraction angle, expressed as k_z, for each layer.
    for (size_t i = 1; i < N; ++i) {
        complex_t kz2 = checkForUnderflow(kz2_base - normalizedSLD(slices[i].material()));
        result[i] = k_sign * std::sqrt(kz2);
    }
    return result;
}

std::vector<complex_t> Compute::Kz::computeKzFromRefIndices(const SliceStack& slices, R3 k)
{
    const size_t N = slices.size();
    const double kz = k.z();
    const double k_sign = kz > 0.0 ? -1 : 1;
    const double k2 = k.mag2();
    const double kz2 = kz * kz;
    const double wl = (2 * pi) / std::sqrt(k2);
    const complex_t n2_ref = slices[0].material().refractiveIndex2(wl);

    std::vector<complex_t> result(N);
    result[0] = -kz;
    for (size_t i = 1; i < N; ++i) {
        const complex_t n2_norm = slices[i].material().refractiveIndex2(wl) - n2_ref;
        result[i] = k_sign * std::sqrt(checkForUnderflow(k2 * n2_norm + kz2));
    }
    return result;
}