reflectorch 1.3.0__py3-none-any.whl → 1.5.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of reflectorch might be problematic. Click here for more details.
- reflectorch/__init__.py +17 -17
- reflectorch/data_generation/__init__.py +128 -126
- reflectorch/data_generation/dataset.py +210 -210
- reflectorch/data_generation/likelihoods.py +80 -80
- reflectorch/data_generation/noise.py +470 -470
- reflectorch/data_generation/priors/__init__.py +60 -60
- reflectorch/data_generation/priors/base.py +55 -55
- reflectorch/data_generation/priors/exp_subprior_sampler.py +298 -298
- reflectorch/data_generation/priors/independent_priors.py +195 -195
- reflectorch/data_generation/priors/multilayer_models.py +311 -311
- reflectorch/data_generation/priors/multilayer_structures.py +104 -104
- reflectorch/data_generation/priors/no_constraints.py +206 -206
- reflectorch/data_generation/priors/parametric_models.py +841 -841
- reflectorch/data_generation/priors/parametric_subpriors.py +369 -369
- reflectorch/data_generation/priors/params.py +252 -252
- reflectorch/data_generation/priors/sampler_strategies.py +369 -369
- reflectorch/data_generation/priors/scaler_mixin.py +65 -65
- reflectorch/data_generation/priors/subprior_sampler.py +371 -371
- reflectorch/data_generation/priors/utils.py +118 -118
- reflectorch/data_generation/process_data.py +41 -41
- reflectorch/data_generation/q_generator.py +280 -246
- reflectorch/data_generation/reflectivity/__init__.py +102 -102
- reflectorch/data_generation/reflectivity/abeles.py +97 -97
- reflectorch/data_generation/reflectivity/kinematical.py +70 -70
- reflectorch/data_generation/reflectivity/memory_eff.py +105 -105
- reflectorch/data_generation/reflectivity/numpy_implementations.py +120 -120
- reflectorch/data_generation/reflectivity/smearing.py +138 -138
- reflectorch/data_generation/reflectivity/smearing_pointwise.py +109 -109
- reflectorch/data_generation/scale_curves.py +112 -112
- reflectorch/data_generation/smearing.py +98 -98
- reflectorch/data_generation/utils.py +223 -222
- reflectorch/extensions/jupyter/__init__.py +11 -6
- reflectorch/extensions/jupyter/api.py +85 -0
- reflectorch/extensions/jupyter/callbacks.py +34 -34
- reflectorch/extensions/jupyter/components.py +758 -0
- reflectorch/extensions/jupyter/custom_select.py +268 -0
- reflectorch/extensions/jupyter/log_widget.py +241 -0
- reflectorch/extensions/jupyter/model_selection.py +495 -0
- reflectorch/extensions/jupyter/plotly_plot_manager.py +329 -0
- reflectorch/extensions/jupyter/widget.py +625 -0
- reflectorch/extensions/matplotlib/__init__.py +5 -5
- reflectorch/extensions/matplotlib/losses.py +32 -32
- reflectorch/extensions/refnx/refnx_conversion.py +76 -76
- reflectorch/inference/__init__.py +28 -24
- reflectorch/inference/inference_model.py +847 -851
- reflectorch/inference/input_interface.py +239 -0
- reflectorch/inference/loading_data.py +37 -0
- reflectorch/inference/multilayer_fitter.py +171 -171
- reflectorch/inference/multilayer_inference_model.py +193 -193
- reflectorch/inference/plotting.py +524 -98
- reflectorch/inference/preprocess_exp/__init__.py +6 -6
- reflectorch/inference/preprocess_exp/attenuation.py +36 -36
- reflectorch/inference/preprocess_exp/cut_with_q_ratio.py +31 -31
- reflectorch/inference/preprocess_exp/footprint.py +81 -81
- reflectorch/inference/preprocess_exp/interpolation.py +19 -16
- reflectorch/inference/preprocess_exp/normalize.py +21 -21
- reflectorch/inference/preprocess_exp/preprocess.py +121 -121
- reflectorch/inference/query_matcher.py +81 -81
- reflectorch/inference/record_time.py +43 -43
- reflectorch/inference/sampler_solution.py +56 -56
- reflectorch/inference/scipy_fitter.py +272 -248
- reflectorch/inference/torch_fitter.py +87 -87
- reflectorch/ml/__init__.py +32 -32
- reflectorch/ml/basic_trainer.py +292 -292
- reflectorch/ml/callbacks.py +80 -80
- reflectorch/ml/dataloaders.py +26 -26
- reflectorch/ml/loggers.py +55 -55
- reflectorch/ml/schedulers.py +355 -355
- reflectorch/ml/trainers.py +200 -191
- reflectorch/ml/utils.py +2 -2
- reflectorch/models/__init__.py +15 -14
- reflectorch/models/activations.py +50 -50
- reflectorch/models/encoders/__init__.py +19 -17
- reflectorch/models/encoders/conv_encoder.py +218 -218
- reflectorch/models/encoders/conv_res_net.py +115 -115
- reflectorch/models/encoders/fno.py +133 -133
- reflectorch/models/encoders/integral_kernel_embedding.py +390 -0
- reflectorch/models/networks/__init__.py +14 -14
- reflectorch/models/networks/mlp_networks.py +434 -428
- reflectorch/models/networks/residual_net.py +156 -156
- reflectorch/paths.py +29 -27
- reflectorch/runs/__init__.py +31 -31
- reflectorch/runs/config.py +25 -25
- reflectorch/runs/slurm_utils.py +93 -93
- reflectorch/runs/train.py +78 -78
- reflectorch/runs/utils.py +404 -401
- reflectorch/test_config.py +4 -4
- reflectorch/train.py +4 -4
- reflectorch/train_on_cluster.py +4 -4
- reflectorch/utils.py +98 -68
- {reflectorch-1.3.0.dist-info → reflectorch-1.5.0.dist-info}/METADATA +129 -125
- reflectorch-1.5.0.dist-info/RECORD +96 -0
- {reflectorch-1.3.0.dist-info → reflectorch-1.5.0.dist-info}/WHEEL +1 -1
- {reflectorch-1.3.0.dist-info → reflectorch-1.5.0.dist-info}/licenses/LICENSE.txt +20 -20
- reflectorch-1.3.0.dist-info/RECORD +0 -86
- {reflectorch-1.3.0.dist-info → reflectorch-1.5.0.dist-info}/top_level.txt +0 -0
|
@@ -1,105 +1,105 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
from math import pi
|
|
4
|
-
|
|
5
|
-
import torch
|
|
6
|
-
from torch import Tensor
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
def abeles_memory_eff(
|
|
10
|
-
q: Tensor,
|
|
11
|
-
thickness: Tensor,
|
|
12
|
-
roughness: Tensor,
|
|
13
|
-
sld: Tensor,
|
|
14
|
-
):
|
|
15
|
-
"""Simulates reflectivity curves for SLD profiles with box model parameterization using a memory-efficient implementation the Abeles matrix method.
|
|
16
|
-
It is computationally slower compared to the implementation in the 'abeles' function.
|
|
17
|
-
|
|
18
|
-
Args:
|
|
19
|
-
q (Tensor): tensor of momentum transfer (q) values with shape [batch_size, n_points] or [n_points]
|
|
20
|
-
thickness (Tensor): tensor containing the layer thicknesses (ordered from top to bottom) with shape [batch_size, n_layers]
|
|
21
|
-
roughness (Tensor): tensor containing the interlayer roughnesses (ordered from top to bottom) with shape [batch_size, n_layers + 1]
|
|
22
|
-
sld (Tensor): tensor containing the layer SLDs (real or complex; ordered from top to bottom) with shape [batch_size, n_layers + 1].
|
|
23
|
-
It includes the substrate but excludes the ambient medium which is assumed to have an SLD of 0.
|
|
24
|
-
|
|
25
|
-
Returns:
|
|
26
|
-
Tensor: tensor containing the simulated reflectivity curves with shape [batch_size, n_points]
|
|
27
|
-
"""
|
|
28
|
-
c_dtype = torch.complex128 if q.dtype is torch.float64 else torch.complex64
|
|
29
|
-
|
|
30
|
-
batch_size, num_layers = thickness.shape
|
|
31
|
-
|
|
32
|
-
sld = sld * 1e-6 + 1e-30j
|
|
33
|
-
|
|
34
|
-
num_interfaces = num_layers + 1
|
|
35
|
-
|
|
36
|
-
k_z0 = (q / 2).to(c_dtype)
|
|
37
|
-
|
|
38
|
-
if len(k_z0.shape) == 1:
|
|
39
|
-
k_z0.unsqueeze_(0)
|
|
40
|
-
|
|
41
|
-
thickness_prev_layer = 1. # ambient
|
|
42
|
-
|
|
43
|
-
for interface_num in range(num_interfaces):
|
|
44
|
-
|
|
45
|
-
prev_layer_idx = interface_num - 1
|
|
46
|
-
next_layer_idx = interface_num
|
|
47
|
-
|
|
48
|
-
if interface_num == 0:
|
|
49
|
-
k_z_previous_layer = _get_relative_k_z(k_z0, torch.zeros(batch_size, 1).to(sld))
|
|
50
|
-
else:
|
|
51
|
-
thickness_prev_layer = thickness[:, prev_layer_idx].unsqueeze(1)
|
|
52
|
-
k_z_previous_layer = _get_relative_k_z(k_z0, sld[:, prev_layer_idx].unsqueeze(1))
|
|
53
|
-
|
|
54
|
-
k_z_next_layer = _get_relative_k_z(k_z0, sld[:, next_layer_idx].unsqueeze(1)) # (batch_num, q_num)
|
|
55
|
-
|
|
56
|
-
reflection_matrix = _make_reflection_matrix(
|
|
57
|
-
k_z_previous_layer, k_z_next_layer, roughness[:, interface_num].unsqueeze(1)
|
|
58
|
-
)
|
|
59
|
-
|
|
60
|
-
if interface_num == 0:
|
|
61
|
-
total_reflectivity_matrix = reflection_matrix
|
|
62
|
-
else:
|
|
63
|
-
translation_matrix = _make_translation_matrix(k_z_previous_layer, thickness_prev_layer)
|
|
64
|
-
|
|
65
|
-
total_reflectivity_matrix = torch.einsum(
|
|
66
|
-
'bnmr, bmlr, bljr -> bnjr', total_reflectivity_matrix, translation_matrix, reflection_matrix
|
|
67
|
-
)
|
|
68
|
-
|
|
69
|
-
r = total_reflectivity_matrix[:, 0, 1] / total_reflectivity_matrix[:, 1, 1]
|
|
70
|
-
|
|
71
|
-
reflectivity = torch.clamp_max_(torch.abs(r) ** 2, 1.).flatten(1)
|
|
72
|
-
|
|
73
|
-
return reflectivity
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
def _get_relative_k_z(k_z0, scattering_length_density):
|
|
77
|
-
return torch.sqrt(k_z0 ** 2 - 4 * pi * scattering_length_density)
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
def _make_reflection_matrix(k_z_previous_layer, k_z_next_layer, interface_roughness):
|
|
81
|
-
p = _safe_div((k_z_previous_layer + k_z_next_layer), (2 * k_z_previous_layer)) * \
|
|
82
|
-
torch.exp(-(k_z_previous_layer - k_z_next_layer) ** 2 * 0.5 * interface_roughness ** 2)
|
|
83
|
-
|
|
84
|
-
m = _safe_div((k_z_previous_layer - k_z_next_layer), (2 * k_z_previous_layer)) * \
|
|
85
|
-
torch.exp(-(k_z_previous_layer + k_z_next_layer) ** 2 * 0.5 * interface_roughness ** 2)
|
|
86
|
-
|
|
87
|
-
return _stack_mtx(p, m, m, p)
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
def _stack_mtx(a11, a12, a21, a22):
|
|
91
|
-
return torch.stack([
|
|
92
|
-
torch.stack([a11, a12], dim=1),
|
|
93
|
-
torch.stack([a21, a22], dim=1),
|
|
94
|
-
], dim=1)
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
def _make_translation_matrix(k_z, thickness):
|
|
98
|
-
return _stack_mtx(
|
|
99
|
-
torch.exp(-1j * k_z * thickness), torch.zeros_like(k_z),
|
|
100
|
-
torch.zeros_like(k_z), torch.exp(1j * k_z * thickness)
|
|
101
|
-
)
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def _safe_div(numerator, denominator):
|
|
105
|
-
return torch.where(denominator == 0, numerator, torch.divide(numerator, denominator))
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
from math import pi
|
|
4
|
+
|
|
5
|
+
import torch
|
|
6
|
+
from torch import Tensor
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def abeles_memory_eff(
|
|
10
|
+
q: Tensor,
|
|
11
|
+
thickness: Tensor,
|
|
12
|
+
roughness: Tensor,
|
|
13
|
+
sld: Tensor,
|
|
14
|
+
):
|
|
15
|
+
"""Simulates reflectivity curves for SLD profiles with box model parameterization using a memory-efficient implementation the Abeles matrix method.
|
|
16
|
+
It is computationally slower compared to the implementation in the 'abeles' function.
|
|
17
|
+
|
|
18
|
+
Args:
|
|
19
|
+
q (Tensor): tensor of momentum transfer (q) values with shape [batch_size, n_points] or [n_points]
|
|
20
|
+
thickness (Tensor): tensor containing the layer thicknesses (ordered from top to bottom) with shape [batch_size, n_layers]
|
|
21
|
+
roughness (Tensor): tensor containing the interlayer roughnesses (ordered from top to bottom) with shape [batch_size, n_layers + 1]
|
|
22
|
+
sld (Tensor): tensor containing the layer SLDs (real or complex; ordered from top to bottom) with shape [batch_size, n_layers + 1].
|
|
23
|
+
It includes the substrate but excludes the ambient medium which is assumed to have an SLD of 0.
|
|
24
|
+
|
|
25
|
+
Returns:
|
|
26
|
+
Tensor: tensor containing the simulated reflectivity curves with shape [batch_size, n_points]
|
|
27
|
+
"""
|
|
28
|
+
c_dtype = torch.complex128 if q.dtype is torch.float64 else torch.complex64
|
|
29
|
+
|
|
30
|
+
batch_size, num_layers = thickness.shape
|
|
31
|
+
|
|
32
|
+
sld = sld * 1e-6 + 1e-30j
|
|
33
|
+
|
|
34
|
+
num_interfaces = num_layers + 1
|
|
35
|
+
|
|
36
|
+
k_z0 = (q / 2).to(c_dtype)
|
|
37
|
+
|
|
38
|
+
if len(k_z0.shape) == 1:
|
|
39
|
+
k_z0.unsqueeze_(0)
|
|
40
|
+
|
|
41
|
+
thickness_prev_layer = 1. # ambient
|
|
42
|
+
|
|
43
|
+
for interface_num in range(num_interfaces):
|
|
44
|
+
|
|
45
|
+
prev_layer_idx = interface_num - 1
|
|
46
|
+
next_layer_idx = interface_num
|
|
47
|
+
|
|
48
|
+
if interface_num == 0:
|
|
49
|
+
k_z_previous_layer = _get_relative_k_z(k_z0, torch.zeros(batch_size, 1).to(sld))
|
|
50
|
+
else:
|
|
51
|
+
thickness_prev_layer = thickness[:, prev_layer_idx].unsqueeze(1)
|
|
52
|
+
k_z_previous_layer = _get_relative_k_z(k_z0, sld[:, prev_layer_idx].unsqueeze(1))
|
|
53
|
+
|
|
54
|
+
k_z_next_layer = _get_relative_k_z(k_z0, sld[:, next_layer_idx].unsqueeze(1)) # (batch_num, q_num)
|
|
55
|
+
|
|
56
|
+
reflection_matrix = _make_reflection_matrix(
|
|
57
|
+
k_z_previous_layer, k_z_next_layer, roughness[:, interface_num].unsqueeze(1)
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
if interface_num == 0:
|
|
61
|
+
total_reflectivity_matrix = reflection_matrix
|
|
62
|
+
else:
|
|
63
|
+
translation_matrix = _make_translation_matrix(k_z_previous_layer, thickness_prev_layer)
|
|
64
|
+
|
|
65
|
+
total_reflectivity_matrix = torch.einsum(
|
|
66
|
+
'bnmr, bmlr, bljr -> bnjr', total_reflectivity_matrix, translation_matrix, reflection_matrix
|
|
67
|
+
)
|
|
68
|
+
|
|
69
|
+
r = total_reflectivity_matrix[:, 0, 1] / total_reflectivity_matrix[:, 1, 1]
|
|
70
|
+
|
|
71
|
+
reflectivity = torch.clamp_max_(torch.abs(r) ** 2, 1.).flatten(1)
|
|
72
|
+
|
|
73
|
+
return reflectivity
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _get_relative_k_z(k_z0, scattering_length_density):
|
|
77
|
+
return torch.sqrt(k_z0 ** 2 - 4 * pi * scattering_length_density)
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
def _make_reflection_matrix(k_z_previous_layer, k_z_next_layer, interface_roughness):
|
|
81
|
+
p = _safe_div((k_z_previous_layer + k_z_next_layer), (2 * k_z_previous_layer)) * \
|
|
82
|
+
torch.exp(-(k_z_previous_layer - k_z_next_layer) ** 2 * 0.5 * interface_roughness ** 2)
|
|
83
|
+
|
|
84
|
+
m = _safe_div((k_z_previous_layer - k_z_next_layer), (2 * k_z_previous_layer)) * \
|
|
85
|
+
torch.exp(-(k_z_previous_layer + k_z_next_layer) ** 2 * 0.5 * interface_roughness ** 2)
|
|
86
|
+
|
|
87
|
+
return _stack_mtx(p, m, m, p)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def _stack_mtx(a11, a12, a21, a22):
|
|
91
|
+
return torch.stack([
|
|
92
|
+
torch.stack([a11, a12], dim=1),
|
|
93
|
+
torch.stack([a21, a22], dim=1),
|
|
94
|
+
], dim=1)
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
def _make_translation_matrix(k_z, thickness):
|
|
98
|
+
return _stack_mtx(
|
|
99
|
+
torch.exp(-1j * k_z * thickness), torch.zeros_like(k_z),
|
|
100
|
+
torch.zeros_like(k_z), torch.exp(1j * k_z * thickness)
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _safe_div(numerator, denominator):
|
|
105
|
+
return torch.where(denominator == 0, numerator, torch.divide(numerator, denominator))
|
|
@@ -1,120 +1,120 @@
|
|
|
1
|
-
# -*- coding: utf-8 -*-
|
|
2
|
-
|
|
3
|
-
import numpy as np
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
def abeles_np(
|
|
7
|
-
q: np.ndarray,
|
|
8
|
-
thickness: np.ndarray,
|
|
9
|
-
roughness: np.ndarray,
|
|
10
|
-
sld: np.ndarray,
|
|
11
|
-
):
|
|
12
|
-
c_dtype = np.complex128 if q.dtype is np.float64 else np.complex64
|
|
13
|
-
|
|
14
|
-
if q.ndim == thickness.ndim == roughness.ndim == sld.ndim == 1:
|
|
15
|
-
zero_batch = True
|
|
16
|
-
else:
|
|
17
|
-
zero_batch = False
|
|
18
|
-
|
|
19
|
-
thickness = np.atleast_2d(thickness)
|
|
20
|
-
roughness = np.atleast_2d(roughness)
|
|
21
|
-
sld = np.atleast_2d(sld)
|
|
22
|
-
|
|
23
|
-
batch_size, num_layers = thickness.shape
|
|
24
|
-
|
|
25
|
-
sld = np.concatenate([np.zeros((batch_size, 1)).astype(sld.dtype), sld], -1)[:, None]
|
|
26
|
-
thickness = np.concatenate([np.zeros((batch_size, 1)).astype(thickness.dtype), thickness], -1)[:, None]
|
|
27
|
-
roughness = roughness[:, None] ** 2
|
|
28
|
-
|
|
29
|
-
sld = sld * 1e-6 + 1e-30j
|
|
30
|
-
|
|
31
|
-
k_z0 = (q / 2).astype(c_dtype)
|
|
32
|
-
|
|
33
|
-
if len(k_z0.shape) == 1:
|
|
34
|
-
k_z0 = k_z0[None]
|
|
35
|
-
|
|
36
|
-
if len(k_z0.shape) == 2:
|
|
37
|
-
k_z0 = k_z0[..., None]
|
|
38
|
-
|
|
39
|
-
k_n = np.sqrt(k_z0 ** 2 - 4 * np.pi * sld)
|
|
40
|
-
|
|
41
|
-
# k_n.shape - (batch, q, layers)
|
|
42
|
-
|
|
43
|
-
k_n, k_np1 = k_n[..., :-1], k_n[..., 1:]
|
|
44
|
-
|
|
45
|
-
beta = 1j * thickness * k_n
|
|
46
|
-
|
|
47
|
-
exp_beta = np.exp(beta)
|
|
48
|
-
exp_m_beta = np.exp(-beta)
|
|
49
|
-
|
|
50
|
-
rn = (k_n - k_np1) / (k_n + k_np1) * np.exp(- 2 * k_n * k_np1 * roughness)
|
|
51
|
-
|
|
52
|
-
c_matrices = np.stack([
|
|
53
|
-
np.stack([exp_beta, rn * exp_m_beta], -1),
|
|
54
|
-
np.stack([rn * exp_beta, exp_m_beta], -1),
|
|
55
|
-
], -1)
|
|
56
|
-
|
|
57
|
-
c_matrices = np.moveaxis(c_matrices, -3, 0)
|
|
58
|
-
|
|
59
|
-
m, c_matrices = c_matrices[0], c_matrices[1:]
|
|
60
|
-
|
|
61
|
-
for c in c_matrices:
|
|
62
|
-
m = m @ c
|
|
63
|
-
|
|
64
|
-
r = np.abs(m[..., 1, 0] / m[..., 0, 0]) ** 2
|
|
65
|
-
r = np.clip(r, None, 1.)
|
|
66
|
-
|
|
67
|
-
if zero_batch:
|
|
68
|
-
r = r[0]
|
|
69
|
-
|
|
70
|
-
return r
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
def kinematical_approximation_np(
|
|
74
|
-
q: np.ndarray,
|
|
75
|
-
thickness: np.ndarray,
|
|
76
|
-
roughness: np.ndarray,
|
|
77
|
-
sld: np.ndarray,
|
|
78
|
-
):
|
|
79
|
-
if q.ndim == thickness.ndim == roughness.ndim == sld.ndim == 1:
|
|
80
|
-
zero_batch = True
|
|
81
|
-
else:
|
|
82
|
-
zero_batch = False
|
|
83
|
-
|
|
84
|
-
thickness = np.atleast_2d(thickness)
|
|
85
|
-
roughness = np.atleast_2d(roughness)
|
|
86
|
-
sld = np.atleast_2d(sld) * 1e-6 + 1e-30j
|
|
87
|
-
substrate_sld = sld[:, -1:]
|
|
88
|
-
|
|
89
|
-
batch_size, num_layers = thickness.shape
|
|
90
|
-
|
|
91
|
-
if q.ndim == 1:
|
|
92
|
-
q = q[None]
|
|
93
|
-
|
|
94
|
-
if q.ndim == 2:
|
|
95
|
-
q = q[..., None]
|
|
96
|
-
|
|
97
|
-
drho = np.concatenate([sld[..., 0][..., None], sld[..., 1:] - sld[..., :-1]], -1)[:, None]
|
|
98
|
-
thickness = np.cumsum(np.concatenate([np.zeros((batch_size, 1)), thickness], -1), -1)[:, None]
|
|
99
|
-
roughness = roughness[:, None]
|
|
100
|
-
|
|
101
|
-
r = np.abs((drho * np.exp(- (roughness * q) ** 2 / 2 + 1j * (q * thickness))).sum(-1)).astype(float) ** 2
|
|
102
|
-
|
|
103
|
-
rf = _get_resnel_reflectivity_np(q, substrate_sld[:, None])
|
|
104
|
-
|
|
105
|
-
r = np.clip(r * rf / np.real(substrate_sld) ** 2, None, 1.)
|
|
106
|
-
|
|
107
|
-
if zero_batch:
|
|
108
|
-
r = r[0]
|
|
109
|
-
|
|
110
|
-
return r
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
def _get_resnel_reflectivity_np(q, substrate_slds):
|
|
114
|
-
_RE_CONST = 0.28174103675406496
|
|
115
|
-
|
|
116
|
-
q_c = np.sqrt(substrate_slds + 0j) / _RE_CONST * 2
|
|
117
|
-
q_prime = np.sqrt(q ** 2 - q_c ** 2 + 0j)
|
|
118
|
-
r_f = np.abs((q - q_prime) / (q + q_prime)).astype(float) ** 2
|
|
119
|
-
|
|
120
|
-
return r_f[..., 0]
|
|
1
|
+
# -*- coding: utf-8 -*-
|
|
2
|
+
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def abeles_np(
|
|
7
|
+
q: np.ndarray,
|
|
8
|
+
thickness: np.ndarray,
|
|
9
|
+
roughness: np.ndarray,
|
|
10
|
+
sld: np.ndarray,
|
|
11
|
+
):
|
|
12
|
+
c_dtype = np.complex128 if q.dtype is np.float64 else np.complex64
|
|
13
|
+
|
|
14
|
+
if q.ndim == thickness.ndim == roughness.ndim == sld.ndim == 1:
|
|
15
|
+
zero_batch = True
|
|
16
|
+
else:
|
|
17
|
+
zero_batch = False
|
|
18
|
+
|
|
19
|
+
thickness = np.atleast_2d(thickness)
|
|
20
|
+
roughness = np.atleast_2d(roughness)
|
|
21
|
+
sld = np.atleast_2d(sld)
|
|
22
|
+
|
|
23
|
+
batch_size, num_layers = thickness.shape
|
|
24
|
+
|
|
25
|
+
sld = np.concatenate([np.zeros((batch_size, 1)).astype(sld.dtype), sld], -1)[:, None]
|
|
26
|
+
thickness = np.concatenate([np.zeros((batch_size, 1)).astype(thickness.dtype), thickness], -1)[:, None]
|
|
27
|
+
roughness = roughness[:, None] ** 2
|
|
28
|
+
|
|
29
|
+
sld = sld * 1e-6 + 1e-30j
|
|
30
|
+
|
|
31
|
+
k_z0 = (q / 2).astype(c_dtype)
|
|
32
|
+
|
|
33
|
+
if len(k_z0.shape) == 1:
|
|
34
|
+
k_z0 = k_z0[None]
|
|
35
|
+
|
|
36
|
+
if len(k_z0.shape) == 2:
|
|
37
|
+
k_z0 = k_z0[..., None]
|
|
38
|
+
|
|
39
|
+
k_n = np.sqrt(k_z0 ** 2 - 4 * np.pi * sld)
|
|
40
|
+
|
|
41
|
+
# k_n.shape - (batch, q, layers)
|
|
42
|
+
|
|
43
|
+
k_n, k_np1 = k_n[..., :-1], k_n[..., 1:]
|
|
44
|
+
|
|
45
|
+
beta = 1j * thickness * k_n
|
|
46
|
+
|
|
47
|
+
exp_beta = np.exp(beta)
|
|
48
|
+
exp_m_beta = np.exp(-beta)
|
|
49
|
+
|
|
50
|
+
rn = (k_n - k_np1) / (k_n + k_np1) * np.exp(- 2 * k_n * k_np1 * roughness)
|
|
51
|
+
|
|
52
|
+
c_matrices = np.stack([
|
|
53
|
+
np.stack([exp_beta, rn * exp_m_beta], -1),
|
|
54
|
+
np.stack([rn * exp_beta, exp_m_beta], -1),
|
|
55
|
+
], -1)
|
|
56
|
+
|
|
57
|
+
c_matrices = np.moveaxis(c_matrices, -3, 0)
|
|
58
|
+
|
|
59
|
+
m, c_matrices = c_matrices[0], c_matrices[1:]
|
|
60
|
+
|
|
61
|
+
for c in c_matrices:
|
|
62
|
+
m = m @ c
|
|
63
|
+
|
|
64
|
+
r = np.abs(m[..., 1, 0] / m[..., 0, 0]) ** 2
|
|
65
|
+
r = np.clip(r, None, 1.)
|
|
66
|
+
|
|
67
|
+
if zero_batch:
|
|
68
|
+
r = r[0]
|
|
69
|
+
|
|
70
|
+
return r
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def kinematical_approximation_np(
|
|
74
|
+
q: np.ndarray,
|
|
75
|
+
thickness: np.ndarray,
|
|
76
|
+
roughness: np.ndarray,
|
|
77
|
+
sld: np.ndarray,
|
|
78
|
+
):
|
|
79
|
+
if q.ndim == thickness.ndim == roughness.ndim == sld.ndim == 1:
|
|
80
|
+
zero_batch = True
|
|
81
|
+
else:
|
|
82
|
+
zero_batch = False
|
|
83
|
+
|
|
84
|
+
thickness = np.atleast_2d(thickness)
|
|
85
|
+
roughness = np.atleast_2d(roughness)
|
|
86
|
+
sld = np.atleast_2d(sld) * 1e-6 + 1e-30j
|
|
87
|
+
substrate_sld = sld[:, -1:]
|
|
88
|
+
|
|
89
|
+
batch_size, num_layers = thickness.shape
|
|
90
|
+
|
|
91
|
+
if q.ndim == 1:
|
|
92
|
+
q = q[None]
|
|
93
|
+
|
|
94
|
+
if q.ndim == 2:
|
|
95
|
+
q = q[..., None]
|
|
96
|
+
|
|
97
|
+
drho = np.concatenate([sld[..., 0][..., None], sld[..., 1:] - sld[..., :-1]], -1)[:, None]
|
|
98
|
+
thickness = np.cumsum(np.concatenate([np.zeros((batch_size, 1)), thickness], -1), -1)[:, None]
|
|
99
|
+
roughness = roughness[:, None]
|
|
100
|
+
|
|
101
|
+
r = np.abs((drho * np.exp(- (roughness * q) ** 2 / 2 + 1j * (q * thickness))).sum(-1)).astype(float) ** 2
|
|
102
|
+
|
|
103
|
+
rf = _get_resnel_reflectivity_np(q, substrate_sld[:, None])
|
|
104
|
+
|
|
105
|
+
r = np.clip(r * rf / np.real(substrate_sld) ** 2, None, 1.)
|
|
106
|
+
|
|
107
|
+
if zero_batch:
|
|
108
|
+
r = r[0]
|
|
109
|
+
|
|
110
|
+
return r
|
|
111
|
+
|
|
112
|
+
|
|
113
|
+
def _get_resnel_reflectivity_np(q, substrate_slds):
|
|
114
|
+
_RE_CONST = 0.28174103675406496
|
|
115
|
+
|
|
116
|
+
q_c = np.sqrt(substrate_slds + 0j) / _RE_CONST * 2
|
|
117
|
+
q_prime = np.sqrt(q ** 2 - q_c ** 2 + 0j)
|
|
118
|
+
r_f = np.abs((q - q_prime) / (q + q_prime)).astype(float) ** 2
|
|
119
|
+
|
|
120
|
+
return r_f[..., 0]
|