xrayscatteringtools 0.0.1__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.
- xrayscatteringtools/__init__.py +3 -0
- xrayscatteringtools/calib/__init__.py +0 -0
- xrayscatteringtools/calib/geometry_calibration.py +204 -0
- xrayscatteringtools/calib/scattering_corrections.py +488 -0
- xrayscatteringtools/epicsArch.py +392 -0
- xrayscatteringtools/io.py +310 -0
- xrayscatteringtools/plotting.py +88 -0
- xrayscatteringtools/theory/Compton_Factors.npy +0 -0
- xrayscatteringtools/theory/Scattering_Factors.npy +0 -0
- xrayscatteringtools/theory/__init__.py +1 -0
- xrayscatteringtools/theory/data/sf6.h5 +0 -0
- xrayscatteringtools/theory/iam.py +213 -0
- xrayscatteringtools/theory/patterns.py +36 -0
- xrayscatteringtools/utils.py +456 -0
- xrayscatteringtools-0.0.1.dist-info/METADATA +79 -0
- xrayscatteringtools-0.0.1.dist-info/RECORD +18 -0
- xrayscatteringtools-0.0.1.dist-info/WHEEL +4 -0
- xrayscatteringtools-0.0.1.dist-info/licenses/LICENSE +21 -0
|
File without changes
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import numpy as np
|
|
2
|
+
from scipy.interpolate import InterpolatedUnivariateSpline
|
|
3
|
+
from scipy.optimize import curve_fit
|
|
4
|
+
from xrayscatteringtools.utils import theta2q
|
|
5
|
+
from xrayscatteringtools.calib.scattering_corrections import correction_factor
|
|
6
|
+
|
|
7
|
+
def run_geometry_calibration(
|
|
8
|
+
raw_image,
|
|
9
|
+
x,
|
|
10
|
+
y,
|
|
11
|
+
mask,
|
|
12
|
+
theory_q,
|
|
13
|
+
theory_Iq,
|
|
14
|
+
photon_energy_keV,
|
|
15
|
+
initial_guess={'amplitude' : 1,'x0': 0, 'y0': 0, 'z0': 90000},
|
|
16
|
+
polarization = 0,
|
|
17
|
+
mask_center=True,
|
|
18
|
+
mask_center_size=7500,
|
|
19
|
+
bounds = ([0, -10_000, -10_000, 30_000], [np.inf, 10_000, 10_000, 150_000])
|
|
20
|
+
):
|
|
21
|
+
"""
|
|
22
|
+
Perform geometry calibration on a raw detector image using a theoretical scattering pattern.
|
|
23
|
+
|
|
24
|
+
This function fits the detector geometry parameters (x0, y0, z0) by comparing the
|
|
25
|
+
measured image to a theoretical scattering pattern and applying corrections for
|
|
26
|
+
polarization (Thompson), geometry, and angle-of-scattering effects.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
raw_image : ndarray
|
|
31
|
+
Array of measured intensities from the detector.
|
|
32
|
+
x : ndarray
|
|
33
|
+
Array of x-coordinates for each pixel.
|
|
34
|
+
y : ndarray
|
|
35
|
+
Array of y-coordinates for each pixel.
|
|
36
|
+
mask : ndarray of bool
|
|
37
|
+
Boolean mask array indicating which pixels to include in the fit.
|
|
38
|
+
theory_q : ndarray
|
|
39
|
+
1D array of scattering vector magnitudes corresponding to the theoretical pattern.
|
|
40
|
+
theory_Iq : ndarray
|
|
41
|
+
1D array of theoretical scattering intensities at each q.
|
|
42
|
+
photon_energy_keV : float
|
|
43
|
+
Photon energy of the X-ray in keV.
|
|
44
|
+
initial_guess : dict, optional
|
|
45
|
+
Initial guess for fit parameters:
|
|
46
|
+
- 'amplitude' : float, scaling of the intensity
|
|
47
|
+
- 'x0', 'y0', 'z0' : float, initial guesses for detector geometry
|
|
48
|
+
Default: {'amplitude': 1, 'x0': 0, 'y0': 0, 'z0': 90000}.
|
|
49
|
+
polarization : float, optional
|
|
50
|
+
Polarization direction (-pi/2 to pi/2). Default is 0 (horizontally polarized).
|
|
51
|
+
mask_center : bool, optional
|
|
52
|
+
If True, exclude pixels near the center from the fit. Default is True.
|
|
53
|
+
mask_center_size : float, optional
|
|
54
|
+
Radius (in pixels) of the excluded central region. Default is 7500.
|
|
55
|
+
fit_bounds : tuple of array_like, optional
|
|
56
|
+
Lower and upper bounds for the fit parameters. Default is
|
|
57
|
+
([0, -10000, -10000, 30000, -π/2], [inf, 10000, 10000, 150000, π/2]).
|
|
58
|
+
|
|
59
|
+
Returns
|
|
60
|
+
-------
|
|
61
|
+
fit : ndarray
|
|
62
|
+
3D array representing the fitted detector image (reshaped to match input dimensions).
|
|
63
|
+
popt : ndarray
|
|
64
|
+
Optimized fit parameters: [amplitude, x0, y0, z0]. In units of microns.
|
|
65
|
+
pcov : ndarray
|
|
66
|
+
Covariance matrix of the optimized parameters.
|
|
67
|
+
|
|
68
|
+
Notes
|
|
69
|
+
-----
|
|
70
|
+
The returned z0 distance must be converted to millimeters when editing the producer. It should not be in microns.
|
|
71
|
+
The fitting applies:
|
|
72
|
+
- `thompson_correction` for polarization effects,
|
|
73
|
+
- `geometry_correction` for geometric distortions,
|
|
74
|
+
- `correction_factor` for angle-of-scattering corrections (Lingyu Ma et al 2024 J. Phys. B: At. Mol. Opt. Phys. 57 205602).
|
|
75
|
+
|
|
76
|
+
Examples
|
|
77
|
+
--------
|
|
78
|
+
>>> fit, popt, pcov = run_geometry_calibration(raw_image, x, y, mask, theory_q, theory_Iq, 12.7)
|
|
79
|
+
"""
|
|
80
|
+
# Step one: generate an interpolation for the theory pattern.
|
|
81
|
+
theory_interpolation = InterpolatedUnivariateSpline(theory_q, theory_Iq, ext=3) # Extrapolation 3. Returns Boundary value if outside of range.
|
|
82
|
+
|
|
83
|
+
# Step two: Define initial guess, bounds, and fitting function using a wrapper
|
|
84
|
+
p0 = [initial_guess['amplitude'], initial_guess['x0'], initial_guess['y0'], initial_guess['z0']]
|
|
85
|
+
def fitting_function(xy, amplitude, x0, y0, z0):
|
|
86
|
+
return model(xy, amplitude, x0, y0, z0, polarization, photon_energy_keV, theory_interpolation)
|
|
87
|
+
|
|
88
|
+
# Step three: Masking and formatting variables
|
|
89
|
+
center_mask = np.ones_like(raw_image, dtype=bool)
|
|
90
|
+
if mask_center:
|
|
91
|
+
center_mask[np.sqrt(x**2 + y**2) < mask_center_size] = False
|
|
92
|
+
|
|
93
|
+
masked_data = np.ravel(raw_image[mask & center_mask])
|
|
94
|
+
x_masked = np.ravel(x[mask & center_mask])
|
|
95
|
+
y_masked = np.ravel(y[mask & center_mask])
|
|
96
|
+
xy_masked = [x_masked, y_masked]
|
|
97
|
+
|
|
98
|
+
# Step four: fitting the data
|
|
99
|
+
popt, pcov = curve_fit(fitting_function, xy_masked, masked_data, p0=p0, bounds=bounds)
|
|
100
|
+
fit = fitting_function([np.ravel(x), np.ravel(y)], *popt).reshape(raw_image.shape)
|
|
101
|
+
return fit, popt, pcov
|
|
102
|
+
|
|
103
|
+
def model(xy, amplitude, x0, y0, z0, phi0, photon_energy_keV, theory_interpolation):
|
|
104
|
+
"""
|
|
105
|
+
Calculate the theoretical detector image for given geometry parameters.
|
|
106
|
+
|
|
107
|
+
Parameters
|
|
108
|
+
----------
|
|
109
|
+
xy : list of ndarray
|
|
110
|
+
[x, y] 2D arrays of pixel coordinates.
|
|
111
|
+
amplitude : float
|
|
112
|
+
Scaling factor for intensity.
|
|
113
|
+
x0, y0, z0 : float
|
|
114
|
+
Detector geometry parameters (pixel offsets and distance).
|
|
115
|
+
phi0 : float
|
|
116
|
+
Azimuthal rotation angle in radians.
|
|
117
|
+
photon_energy_keV : float
|
|
118
|
+
Photon energy in keV.
|
|
119
|
+
theory_interpolation : callable
|
|
120
|
+
Interpolated function for theoretical scattering intensities versus q.
|
|
121
|
+
|
|
122
|
+
Returns
|
|
123
|
+
-------
|
|
124
|
+
fit : ndarray
|
|
125
|
+
Flattened array of predicted intensities for each pixel.
|
|
126
|
+
|
|
127
|
+
"""
|
|
128
|
+
# Pull out the x and y arrays from the input
|
|
129
|
+
x = xy[0]
|
|
130
|
+
y = xy[1]
|
|
131
|
+
# Center the arrays around x0 and y0
|
|
132
|
+
centered_x = x - x0
|
|
133
|
+
centered_y = y - y0
|
|
134
|
+
# Calculate the r array
|
|
135
|
+
r_matrix = np.sqrt(centered_x**2 + centered_y**2)
|
|
136
|
+
theta_matrix = np.arctan(r_matrix/z0)
|
|
137
|
+
q_matrix = theta2q(theta_matrix, photon_energy_keV)
|
|
138
|
+
|
|
139
|
+
# Calculate corrections
|
|
140
|
+
corrections = np.ones_like(q_matrix)
|
|
141
|
+
corrections *= thompson_correction(centered_x, centered_y, z0, phi0) # Polarization
|
|
142
|
+
corrections *= geometry_correction(centered_x, centered_y, z0) # Geometry
|
|
143
|
+
corrections *= correction_factor(q_matrix, photon_energy_keV) # Angle-Of-Scattering, Lingyu Ma et al 2024 J. Phys. B: At. Mol. Opt. Phys. 57 205602
|
|
144
|
+
fit = amplitude * theory_interpolation(q_matrix) * corrections
|
|
145
|
+
return fit
|
|
146
|
+
|
|
147
|
+
def thompson_correction(x, y, z0, phi0):
|
|
148
|
+
"""
|
|
149
|
+
Calculate the Thompson polarization correction for each pixel.
|
|
150
|
+
|
|
151
|
+
Parameters
|
|
152
|
+
----------
|
|
153
|
+
x, y : ndarray
|
|
154
|
+
Pixel coordinates relative to detector origin.
|
|
155
|
+
z0 : float
|
|
156
|
+
Detector distance along beam axis.
|
|
157
|
+
phi0 : float
|
|
158
|
+
Azimuthal rotation angle in radians.
|
|
159
|
+
|
|
160
|
+
Returns
|
|
161
|
+
-------
|
|
162
|
+
correction : ndarray
|
|
163
|
+
Thompson correction factor for each pixel.
|
|
164
|
+
|
|
165
|
+
Notes
|
|
166
|
+
-----
|
|
167
|
+
See Lingyu Ma et al 2024 J. Phys. B: At. Mol. Opt. Phys. 57 205602
|
|
168
|
+
|
|
169
|
+
"""
|
|
170
|
+
# Calculate the Thompson scattering correction factor
|
|
171
|
+
r_matrix = np.sqrt(x**2 + y**2)
|
|
172
|
+
theta = np.arctan(r_matrix/z0)
|
|
173
|
+
phi = np.arctan2(y, x) + phi0
|
|
174
|
+
correction = np.sin(phi)**2+np.cos(theta)**2*np.cos(phi)**2
|
|
175
|
+
return correction
|
|
176
|
+
|
|
177
|
+
def geometry_correction(x, y, z0):
|
|
178
|
+
"""
|
|
179
|
+
Compute geometric correction factor (cos^3(theta)) for a detector.
|
|
180
|
+
|
|
181
|
+
Parameters
|
|
182
|
+
----------
|
|
183
|
+
x, y : ndarray
|
|
184
|
+
Pixel coordinates relative to detector origin.
|
|
185
|
+
z0 : float
|
|
186
|
+
Detector distance along beam axis.
|
|
187
|
+
|
|
188
|
+
Returns
|
|
189
|
+
-------
|
|
190
|
+
correction : ndarray
|
|
191
|
+
Geometric correction factor for each pixel.
|
|
192
|
+
|
|
193
|
+
Notes
|
|
194
|
+
-----
|
|
195
|
+
The geometry correction comes from the inverse square law and the effective area of the pixel.
|
|
196
|
+
The inverse square law accounts for cos^2(theta), and the effective area is another cos(theta).
|
|
197
|
+
See Lingyu Ma et al 2024 J. Phys. B: At. Mol. Opt. Phys. 57 205602
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
"""
|
|
201
|
+
r_matrix = np.sqrt(x**2 + y**2)
|
|
202
|
+
theta = np.arctan(r_matrix / z0)
|
|
203
|
+
correction = np.cos(theta) ** 3
|
|
204
|
+
return correction
|
|
@@ -0,0 +1,488 @@
|
|
|
1
|
+
from scipy.interpolate import InterpolatedUnivariateSpline
|
|
2
|
+
from xrayscatteringtools.utils import q2theta
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
def correction_factor(
|
|
6
|
+
q_arr,
|
|
7
|
+
keV,
|
|
8
|
+
L=2.4e-3,
|
|
9
|
+
tSi=318.5e-6,
|
|
10
|
+
tK=8e-6,
|
|
11
|
+
tAl=4.5e-6,
|
|
12
|
+
tBe=100e-6,
|
|
13
|
+
rBe=250e-6,
|
|
14
|
+
tP=125e-6,
|
|
15
|
+
rP=125e-6
|
|
16
|
+
):
|
|
17
|
+
"""
|
|
18
|
+
Calculate the total X-ray scattering correction factor for a given setup.
|
|
19
|
+
|
|
20
|
+
The total correction factor accounts for absorption and scattering
|
|
21
|
+
from different materials in the beam path, including Si, Kapton,
|
|
22
|
+
Al, Be, and the gas cell itself.
|
|
23
|
+
|
|
24
|
+
Parameters
|
|
25
|
+
----------
|
|
26
|
+
qbins : array-like
|
|
27
|
+
Array of momentum transfer (q) values in Å⁻¹ or equivalent units.
|
|
28
|
+
keV : float
|
|
29
|
+
Photon energy in keV.
|
|
30
|
+
L : float, optional
|
|
31
|
+
Gas cell length in meters. Default is 2.4e-3 m.
|
|
32
|
+
tSi : float, optional
|
|
33
|
+
Thickness of silicon in meters. Default is 318.5e-6 m.
|
|
34
|
+
tK : float, optional
|
|
35
|
+
Thickness of Kapton in meters. Default is 8e-6 m.
|
|
36
|
+
tAl : float, optional
|
|
37
|
+
Thickness of aluminum in meters. Default is 4.5e-6 m.
|
|
38
|
+
tBe : float, optional
|
|
39
|
+
Thickness of beryllium in meters. Default is 100e-6 m.
|
|
40
|
+
rBe : float, optional
|
|
41
|
+
Radius of the Be window in meters. Default is 250e-6 m.
|
|
42
|
+
tP : float, optional
|
|
43
|
+
Thickness of the gas cell in meters. Default is 125e-6 m.
|
|
44
|
+
rP : float, optional
|
|
45
|
+
Hole radius of the gas cell platinum pinhole in meters. Default is 125e-6 m.
|
|
46
|
+
|
|
47
|
+
Returns
|
|
48
|
+
-------
|
|
49
|
+
numpy.ndarray
|
|
50
|
+
Array of total correction factors corresponding to each q value.
|
|
51
|
+
|
|
52
|
+
Notes
|
|
53
|
+
-----
|
|
54
|
+
The total correction factor is computed as the product of individual
|
|
55
|
+
material corrections:
|
|
56
|
+
|
|
57
|
+
total_correction = Si_correction * KaptonHN_correction *
|
|
58
|
+
Al_correction * Be_correction * cell_correction
|
|
59
|
+
"""
|
|
60
|
+
return (
|
|
61
|
+
Si_correction(q_arr, keV, tSi) *
|
|
62
|
+
KaptonHN_correction(q_arr, keV, tK) *
|
|
63
|
+
Al_correction(q_arr, keV, tAl) *
|
|
64
|
+
Be_correction(q_arr, keV, tBe, rBe, L) *
|
|
65
|
+
cell_correction(q_arr, keV, tP, rP, L)
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
def Si_correction(q_arr, keV, tSi=318.5e-6):
|
|
69
|
+
"""
|
|
70
|
+
Calculate the Silicon correction factor.
|
|
71
|
+
|
|
72
|
+
Parameters
|
|
73
|
+
----------
|
|
74
|
+
q_arr : array-like
|
|
75
|
+
Array of momentum transfer (q) values.
|
|
76
|
+
keV : float
|
|
77
|
+
Photon energy in keV.
|
|
78
|
+
tSi : float, optional
|
|
79
|
+
Silicon thickness in meters. Default is 318.5e-6 m.
|
|
80
|
+
|
|
81
|
+
Returns
|
|
82
|
+
-------
|
|
83
|
+
numpy.ndarray
|
|
84
|
+
Silicon correction factor for each q value.
|
|
85
|
+
|
|
86
|
+
Notes
|
|
87
|
+
-----
|
|
88
|
+
Uses the angle-dependent absorption formula:
|
|
89
|
+
nSi = (1 - exp(-tSi / (λ_Si * cos(theta)))) / (1 - exp(-tSi / λ_Si))
|
|
90
|
+
where λ_Si is the X-ray attenuation length for silicon at the given energy.
|
|
91
|
+
"""
|
|
92
|
+
Silen = Si_attenuation_length(keV)
|
|
93
|
+
thetas = q2theta(q_arr, keV) # In this implementation, the theta here is the same as 2theta in Ma et al.
|
|
94
|
+
nSi = (1 - np.exp(- tSi / (Silen * np.cos(thetas)))) / (1 - np.exp(- tSi / Silen))
|
|
95
|
+
return nSi
|
|
96
|
+
|
|
97
|
+
def KaptonHN_correction(q_arr, keV, tK=8e-6):
|
|
98
|
+
"""
|
|
99
|
+
Calculate the Kapton (HN) correction factor.
|
|
100
|
+
|
|
101
|
+
Parameters
|
|
102
|
+
----------
|
|
103
|
+
q_arr : array-like
|
|
104
|
+
Array of momentum transfer (q) values.
|
|
105
|
+
keV : float
|
|
106
|
+
Photon energy in keV.
|
|
107
|
+
tK : float, optional
|
|
108
|
+
Kapton thickness in meters. Default is 8e-6 m.
|
|
109
|
+
|
|
110
|
+
Returns
|
|
111
|
+
-------
|
|
112
|
+
numpy.ndarray
|
|
113
|
+
Kapton correction factor for each q value.
|
|
114
|
+
|
|
115
|
+
Notes
|
|
116
|
+
-----
|
|
117
|
+
Uses the angle-dependent exponential attenuation:
|
|
118
|
+
nK = exp(-tK / (λ_K * cos(theta))) / exp(-tK / λ_K)
|
|
119
|
+
where λ_K is the X-ray attenuation length for Kapton at the given energy.
|
|
120
|
+
"""
|
|
121
|
+
Klen = KaptonHN_attenuation_length(keV)
|
|
122
|
+
thetas = q2theta(q_arr, keV) # In this implementation, the theta here is the same as 2theta in Ma et al.
|
|
123
|
+
nK = np.exp(- tK / (Klen * np.cos(thetas))) / np.exp(- tK / Klen)
|
|
124
|
+
return nK
|
|
125
|
+
|
|
126
|
+
def Al_correction(q_arr, keV, tAl=4.5e-6):
|
|
127
|
+
"""
|
|
128
|
+
Calculate the Aluminum correction factor.
|
|
129
|
+
|
|
130
|
+
Parameters
|
|
131
|
+
----------
|
|
132
|
+
q_arr : array-like
|
|
133
|
+
Array of momentum transfer (q) values.
|
|
134
|
+
keV : float
|
|
135
|
+
Photon energy in keV.
|
|
136
|
+
tAl : float, optional
|
|
137
|
+
Aluminum thickness in meters. Default is 4.5e-6 m.
|
|
138
|
+
|
|
139
|
+
Returns
|
|
140
|
+
-------
|
|
141
|
+
numpy.ndarray
|
|
142
|
+
Aluminum correction factor for each q value.
|
|
143
|
+
|
|
144
|
+
Notes
|
|
145
|
+
-----
|
|
146
|
+
Uses the angle-dependent exponential attenuation:
|
|
147
|
+
nAl = exp(-tAl / (λ_Al * cos(theta))) / exp(-tAl / λ_Al)
|
|
148
|
+
where λ_Al is the X-ray attenuation length for Aluminum at the given energy.
|
|
149
|
+
"""
|
|
150
|
+
Allen = Al_attenuation_length(keV)
|
|
151
|
+
thetas = q2theta(q_arr, keV) # In this implementation, the theta here is the same as 2theta in Ma et al.
|
|
152
|
+
nAl = np.exp(- tAl / (Allen * np.cos(thetas))) / np.exp(- tAl / Allen)
|
|
153
|
+
return nAl
|
|
154
|
+
|
|
155
|
+
|
|
156
|
+
def Be_correction(q_arr, keV, tBe=100e-6, rBe=250e-6, L=2.4e-3):
|
|
157
|
+
"""
|
|
158
|
+
Calculate the Beryllium correction factor considering window geometry.
|
|
159
|
+
|
|
160
|
+
Parameters
|
|
161
|
+
----------
|
|
162
|
+
q_arr : array-like
|
|
163
|
+
Array of momentum transfer (q) values.
|
|
164
|
+
keV : float
|
|
165
|
+
Photon energy in keV.
|
|
166
|
+
tBe : float, optional
|
|
167
|
+
Beryllium thickness in meters. Default is 100e-6 m.
|
|
168
|
+
rBe : float, optional
|
|
169
|
+
Radius of the Be window in meters. Default is 250e-6 m.
|
|
170
|
+
L : float, optional
|
|
171
|
+
Gas cell length in meters. Default is 2.4e-3 m.
|
|
172
|
+
|
|
173
|
+
Returns
|
|
174
|
+
-------
|
|
175
|
+
numpy.ndarray
|
|
176
|
+
Beryllium correction factor for each q value.
|
|
177
|
+
|
|
178
|
+
Notes
|
|
179
|
+
-----
|
|
180
|
+
Accounts for partial path length through the Be window depending on
|
|
181
|
+
scattering angle.
|
|
182
|
+
"""
|
|
183
|
+
# Compute attenuation length and scattering angles
|
|
184
|
+
Belen = Be_attenuation_length(keV)
|
|
185
|
+
thetas = q2theta(q_arr, keV) # same shape as q_arr
|
|
186
|
+
|
|
187
|
+
# Compute partial path length through Be window
|
|
188
|
+
xBe = np.minimum(rBe / np.tan(thetas), L)
|
|
189
|
+
|
|
190
|
+
# Avoid division by zero for theta=0
|
|
191
|
+
xBe = np.nan_to_num(xBe, nan=L, posinf=L, neginf=L)
|
|
192
|
+
|
|
193
|
+
# Compute correction factor
|
|
194
|
+
nBe = xBe / L + (L - xBe) / L * np.exp(-tBe / (Belen * np.cos(thetas)))
|
|
195
|
+
return nBe
|
|
196
|
+
|
|
197
|
+
def cell_correction(q_arr, keV, tP=125e-6, rP=125e-6, L=2.4e-3):
|
|
198
|
+
"""
|
|
199
|
+
Calculate the gas cell geometry correction factor.
|
|
200
|
+
|
|
201
|
+
Parameters
|
|
202
|
+
----------
|
|
203
|
+
q_arr : array-like
|
|
204
|
+
Array of momentum transfer (q) values.
|
|
205
|
+
keV : float
|
|
206
|
+
Photon energy in keV.
|
|
207
|
+
tP : float, optional
|
|
208
|
+
Gas cell thickness in meters. Default is 125e-6 m.
|
|
209
|
+
rP : float, optional
|
|
210
|
+
Radius of the gas cell window in meters. Default is 125e-6 m.
|
|
211
|
+
L : float, optional
|
|
212
|
+
Gas cell length in meters. Default is 2.4e-3 m.
|
|
213
|
+
|
|
214
|
+
Returns
|
|
215
|
+
-------
|
|
216
|
+
numpy.ndarray
|
|
217
|
+
Cell geometry correction factor for each q value.
|
|
218
|
+
|
|
219
|
+
Notes
|
|
220
|
+
-----
|
|
221
|
+
Accounts for the angle-dependent path length and geometry of the gas cell.
|
|
222
|
+
"""
|
|
223
|
+
# Compute scattering angles
|
|
224
|
+
thetas = q2theta(q_arr, keV) # same shape as q_arr
|
|
225
|
+
|
|
226
|
+
# Precompute helper array
|
|
227
|
+
xmax = tP - rP / np.tan(thetas)
|
|
228
|
+
|
|
229
|
+
# Mask to separate the two cases
|
|
230
|
+
cond = np.tan(thetas) >= (rP / tP)
|
|
231
|
+
|
|
232
|
+
# Initialize output
|
|
233
|
+
nCell = np.empty_like(q_arr, dtype=float)
|
|
234
|
+
|
|
235
|
+
# Case 1: tan(theta) >= rP/tP
|
|
236
|
+
nCell[cond] = 1 + (rP / (L * np.tan(thetas[cond])))
|
|
237
|
+
|
|
238
|
+
# Case 2: tan(theta) < rP/tP
|
|
239
|
+
num = tP + (rP * xmax[~cond]) / (xmax[~cond] - rP)
|
|
240
|
+
nCell[~cond] = 1 + num / L
|
|
241
|
+
|
|
242
|
+
# Handle any division-by-zero or invalid values safely
|
|
243
|
+
nCell = np.nan_to_num(nCell, nan=1.0, posinf=1.0, neginf=1.0)
|
|
244
|
+
return nCell
|
|
245
|
+
|
|
246
|
+
|
|
247
|
+
def Si_attenuation_length(keV):
|
|
248
|
+
"""
|
|
249
|
+
Calculate the X-ray attenuation length of Silicon (Si) in meters.
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
keV : float
|
|
254
|
+
Photon energy in keV.
|
|
255
|
+
|
|
256
|
+
Returns
|
|
257
|
+
-------
|
|
258
|
+
float
|
|
259
|
+
Attenuation length of Silicon in meters.
|
|
260
|
+
|
|
261
|
+
Notes
|
|
262
|
+
-----
|
|
263
|
+
Uses a spline interpolation of tabulated data. The input energy in keV
|
|
264
|
+
is converted to eV for the interpolation, and the returned value is converted
|
|
265
|
+
from microns to meters.
|
|
266
|
+
"""
|
|
267
|
+
E_values = [
|
|
268
|
+
6000.00, 6140.00, 6280.00, 6420.00, 6560.00, 6700.00, 6840.00, 6980.00,
|
|
269
|
+
7120.00, 7260.00, 7400.00, 7540.00, 7680.00, 7820.00, 7960.00, 8100.00,
|
|
270
|
+
8240.00, 8380.00, 8520.00, 8660.00, 8800.00, 8940.00, 9080.00, 9220.00,
|
|
271
|
+
9360.00, 9500.00, 9640.00, 9780.00, 9920.00, 10060.0, 10200.0, 10340.0,
|
|
272
|
+
10480.0, 10620.0, 10760.0, 10900.0, 11040.0, 11180.0, 11320.0, 11460.0,
|
|
273
|
+
11600.0, 11740.0, 11880.0, 12020.0, 12160.0, 12300.0, 12440.0, 12580.0,
|
|
274
|
+
12720.0, 12860.0, 13000.0, 13140.0, 13280.0, 13420.0, 13560.0, 13700.0,
|
|
275
|
+
13840.0, 13980.0, 14120.0, 14260.0, 14400.0, 14540.0, 14680.0, 14820.0,
|
|
276
|
+
14960.0, 15100.0, 15240.0, 15380.0, 15520.0, 15660.0, 15800.0, 15940.0,
|
|
277
|
+
16080.0, 16220.0, 16360.0, 16500.0, 16640.0, 16780.0, 16920.0, 17060.0,
|
|
278
|
+
17200.0, 17340.0, 17480.0, 17620.0, 17760.0, 17900.0, 18040.0, 18180.0,
|
|
279
|
+
18320.0, 18460.0, 18600.0, 18740.0, 18880.0, 19020.0, 19160.0, 19300.0,
|
|
280
|
+
19440.0, 19580.0, 19720.0, 19860.0, 20000.0
|
|
281
|
+
] # eV
|
|
282
|
+
|
|
283
|
+
length = [
|
|
284
|
+
30.2985, 32.3736, 34.5409, 36.8058, 39.1665, 41.6272, 44.1903, 46.8565,
|
|
285
|
+
49.6295, 52.5100, 55.5013, 58.6048, 61.8219, 65.1565, 68.6095, 72.1834,
|
|
286
|
+
75.8799, 79.7013, 83.6497, 87.7275, 91.9370, 96.2790, 100.757, 105.373,
|
|
287
|
+
110.128, 115.025, 120.067, 125.254, 130.590, 136.076, 141.715, 147.509,
|
|
288
|
+
153.460, 159.570, 165.839, 172.274, 178.872, 185.637, 192.574, 199.679,
|
|
289
|
+
206.960, 214.417, 222.051, 229.865, 237.861, 246.040, 254.404, 262.959,
|
|
290
|
+
271.704, 280.639, 289.770, 299.096, 308.619, 318.347, 328.274, 338.404,
|
|
291
|
+
348.743, 359.292, 370.049, 381.017, 392.198, 403.601, 415.221, 427.057,
|
|
292
|
+
439.120, 451.410, 463.930, 476.677, 489.653, 502.866, 516.312, 529.997,
|
|
293
|
+
543.916, 558.080, 572.484, 587.133, 602.027, 617.169, 632.558, 648.196,
|
|
294
|
+
664.092, 680.241, 696.650, 713.315, 730.242, 747.427, 764.877, 782.593,
|
|
295
|
+
800.574, 818.826, 837.346, 856.145, 875.212, 894.559, 914.172, 934.073,
|
|
296
|
+
954.240, 974.706, 995.446, 1016.48, 1037.80
|
|
297
|
+
] # Micron
|
|
298
|
+
Mu_Spline = InterpolatedUnivariateSpline(E_values, length)
|
|
299
|
+
return Mu_Spline(keV*1000) * 1e-6 # Convert to m
|
|
300
|
+
|
|
301
|
+
def Al_attenuation_length(keV):
|
|
302
|
+
"""
|
|
303
|
+
Calculate the X-ray attenuation length of Aluminum (Al) for a given photon energy.
|
|
304
|
+
|
|
305
|
+
Parameters
|
|
306
|
+
----------
|
|
307
|
+
keV : float
|
|
308
|
+
Photon energy in keV.
|
|
309
|
+
|
|
310
|
+
Returns
|
|
311
|
+
-------
|
|
312
|
+
float
|
|
313
|
+
Attenuation length of Al in meters.
|
|
314
|
+
|
|
315
|
+
Notes
|
|
316
|
+
-----
|
|
317
|
+
Uses a spline interpolation of tabulated data to compute the attenuation
|
|
318
|
+
length. Tabulated values are converted from microns to meters.
|
|
319
|
+
"""
|
|
320
|
+
E_values = [
|
|
321
|
+
6000.00, 6140.00, 6280.00, 6420.00, 6560.00, 6700.00, 6840.00, 6980.00,
|
|
322
|
+
7120.00, 7260.00, 7400.00, 7540.00, 7680.00, 7820.00, 7960.00, 8100.00,
|
|
323
|
+
8240.00, 8380.00, 8520.00, 8660.00, 8800.00, 8940.00, 9080.00, 9220.00,
|
|
324
|
+
9360.00, 9500.00, 9640.00, 9780.00, 9920.00, 10060.0, 10200.0, 10340.0,
|
|
325
|
+
10480.0, 10620.0, 10760.0, 10900.0, 11040.0, 11180.0, 11320.0, 11460.0,
|
|
326
|
+
11600.0, 11740.0, 11880.0, 12020.0, 12160.0, 12300.0, 12440.0, 12580.0,
|
|
327
|
+
12720.0, 12860.0, 13000.0, 13140.0, 13280.0, 13420.0, 13560.0, 13700.0,
|
|
328
|
+
13840.0, 13980.0, 14120.0, 14260.0, 14400.0, 14540.0, 14680.0, 14820.0,
|
|
329
|
+
14960.0, 15100.0, 15240.0, 15380.0, 15520.0, 15660.0, 15800.0, 15940.0,
|
|
330
|
+
16080.0, 16220.0, 16360.0, 16500.0, 16640.0, 16780.0, 16920.0, 17060.0,
|
|
331
|
+
17200.0, 17340.0, 17480.0, 17620.0, 17760.0, 17900.0, 18040.0, 18180.0,
|
|
332
|
+
18320.0, 18460.0, 18600.0, 18740.0, 18880.0, 19020.0, 19160.0, 19300.0,
|
|
333
|
+
19440.0, 19580.0, 19720.0, 19860.0, 20000.0
|
|
334
|
+
] # eV
|
|
335
|
+
|
|
336
|
+
length = [
|
|
337
|
+
33.5963, 35.9138, 38.3384, 40.8699, 43.5113, 46.2649, 49.1331, 52.1183,
|
|
338
|
+
55.2230, 58.4497, 61.8005, 65.2780, 68.8848, 72.6228, 76.4952, 80.5027,
|
|
339
|
+
84.6497, 88.9378, 93.3698, 97.9478, 102.673, 107.549, 112.580, 117.765,
|
|
340
|
+
123.108, 128.611, 134.276, 140.108, 146.102, 152.272, 158.613, 165.131,
|
|
341
|
+
171.824, 178.696, 185.752, 192.991, 200.418, 208.034, 215.842, 223.844,
|
|
342
|
+
232.042, 240.437, 249.034, 257.835, 266.841, 276.056, 285.482, 295.117,
|
|
343
|
+
304.970, 315.038, 325.324, 335.837, 346.572, 357.532, 368.723, 380.144,
|
|
344
|
+
391.796, 403.686, 415.811, 428.178, 440.785, 453.638, 466.735, 480.075,
|
|
345
|
+
493.674, 507.531, 521.648, 536.026, 550.660, 565.562, 580.723, 596.154,
|
|
346
|
+
611.848, 627.816, 644.054, 660.571, 677.362, 694.432, 711.784, 729.418,
|
|
347
|
+
747.339, 765.542, 784.034, 802.811, 821.888, 841.256, 860.921, 880.880,
|
|
348
|
+
901.138, 921.698, 942.559, 963.730, 985.201, 1006.98, 1029.06, 1051.47,
|
|
349
|
+
1074.17, 1097.20, 1120.53, 1144.28, 1168.39
|
|
350
|
+
] # Micron
|
|
351
|
+
Mu_Spline = InterpolatedUnivariateSpline(E_values, length)
|
|
352
|
+
return Mu_Spline(keV*1000) * 1e-6 # Convert to meters
|
|
353
|
+
|
|
354
|
+
def Be_attenuation_length(keV):
|
|
355
|
+
"""
|
|
356
|
+
Calculate the X-ray attenuation length of Beryllium (Be) for a given photon energy.
|
|
357
|
+
|
|
358
|
+
Parameters
|
|
359
|
+
----------
|
|
360
|
+
keV : float
|
|
361
|
+
Photon energy in keV.
|
|
362
|
+
|
|
363
|
+
Returns
|
|
364
|
+
-------
|
|
365
|
+
float
|
|
366
|
+
Attenuation length of Be in meters.
|
|
367
|
+
|
|
368
|
+
Notes
|
|
369
|
+
-----
|
|
370
|
+
Computed via spline interpolation of tabulated attenuation data (in microns),
|
|
371
|
+
which is converted to meters.
|
|
372
|
+
"""
|
|
373
|
+
E_values = [
|
|
374
|
+
6000.00, 6140.00, 6280.00, 6420.00, 6560.00, 6700.00, 6840.00, 6980.00,
|
|
375
|
+
7120.00, 7260.00, 7400.00, 7540.00, 7680.00, 7820.00, 7960.00, 8100.00,
|
|
376
|
+
8240.00, 8380.00, 8520.00, 8660.00, 8800.00, 8940.00, 9080.00, 9220.00,
|
|
377
|
+
9360.00, 9500.00, 9640.00, 9780.00, 9920.00, 10060.0, 10200.0, 10340.0,
|
|
378
|
+
10480.0, 10620.0, 10760.0, 10900.0, 11040.0, 11180.0, 11320.0, 11460.0,
|
|
379
|
+
11600.0, 11740.0, 11880.0, 12020.0, 12160.0, 12300.0, 12440.0, 12580.0,
|
|
380
|
+
12720.0, 12860.0, 13000.0, 13140.0, 13280.0, 13420.0, 13560.0, 13700.0,
|
|
381
|
+
13840.0, 13980.0, 14120.0, 14260.0, 14400.0, 14540.0, 14680.0, 14820.0,
|
|
382
|
+
14960.0, 15100.0, 15240.0, 15380.0, 15520.0, 15660.0, 15800.0, 15940.0,
|
|
383
|
+
16080.0, 16220.0, 16360.0, 16500.0, 16640.0, 16780.0, 16920.0, 17060.0,
|
|
384
|
+
17200.0, 17340.0, 17480.0, 17620.0, 17760.0, 17900.0, 18040.0, 18180.0,
|
|
385
|
+
18320.0, 18460.0, 18600.0, 18740.0, 18880.0, 19020.0, 19160.0, 19300.0,
|
|
386
|
+
19440.0, 19580.0, 19720.0, 19860.0, 20000.0
|
|
387
|
+
]
|
|
388
|
+
|
|
389
|
+
length = [
|
|
390
|
+
2236.49, 2400.58, 2571.82, 2751.11, 2938.12, 3132.45, 3334.08, 3542.98,
|
|
391
|
+
3759.08, 3982.32, 4212.59, 4449.79, 4693.79, 4944.48, 5201.64, 5465.44,
|
|
392
|
+
5735.56, 6012.75, 6296.96, 6586.83, 6882.13, 7182.60, 7487.94, 7797.85,
|
|
393
|
+
8112.04, 8430.21, 8752.01, 9077.13, 9405.23, 9737.18, 10073.2, 10411.6,
|
|
394
|
+
10752.0, 11094.1, 11437.5, 11782.0, 12127.2, 12472.7, 12818.3, 13163.7,
|
|
395
|
+
13508.5, 13852.4, 14195.1, 14536.5, 14876.0, 15213.7, 15549.8, 15885.7,
|
|
396
|
+
16218.9, 16549.2, 16876.3, 17200.0, 17520.2, 17836.7, 18149.3, 18457.8,
|
|
397
|
+
18762.0, 19062.0, 19357.5, 19648.4, 19934.7, 20216.2, 20492.9, 20764.7,
|
|
398
|
+
21031.4, 21302.6, 21572.9, 21838.7, 22099.9, 22356.7, 22608.8, 22856.4,
|
|
399
|
+
23099.4, 23337.8, 23572.2, 23803.1, 24029.4, 24251.3, 24468.5, 24681.1,
|
|
400
|
+
24889.3, 25093.0, 25292.3, 25487.3, 25678.0, 25864.3, 26046.5, 26224.5,
|
|
401
|
+
26398.4, 26568.2, 26734.0, 26895.9, 27054.0, 27208.2, 27358.8, 27505.5,
|
|
402
|
+
27648.7, 27788.3, 27924.5, 28057.2, 28186.6
|
|
403
|
+
]
|
|
404
|
+
|
|
405
|
+
Mu_Spline = InterpolatedUnivariateSpline(E_values, length)
|
|
406
|
+
return Mu_Spline(keV*1000) * 1e-6 # Convert to m
|
|
407
|
+
|
|
408
|
+
def KaptonHN_attenuation_length(keV):
|
|
409
|
+
"""
|
|
410
|
+
Calculate the X-ray attenuation length of Kapton HN for a given photon energy.
|
|
411
|
+
|
|
412
|
+
Parameters
|
|
413
|
+
----------
|
|
414
|
+
keV : float
|
|
415
|
+
Photon energy in keV.
|
|
416
|
+
|
|
417
|
+
Returns
|
|
418
|
+
-------
|
|
419
|
+
float
|
|
420
|
+
Attenuation length of Kapton HN in meters.
|
|
421
|
+
|
|
422
|
+
Notes
|
|
423
|
+
-----
|
|
424
|
+
Uses spline interpolation of tabulated attenuation lengths (in microns),
|
|
425
|
+
then converts them to meters.
|
|
426
|
+
"""
|
|
427
|
+
E_values = [
|
|
428
|
+
6000.00, 6140.00, 6280.00, 6420.00, 6560.00, 6700.00, 6840.00, 6980.00,
|
|
429
|
+
7120.00, 7260.00, 7400.00, 7540.00, 7680.00, 7820.00, 7960.00, 8100.00,
|
|
430
|
+
8240.00, 8380.00, 8520.00, 8660.00, 8800.00, 8940.00, 9080.00, 9220.00,
|
|
431
|
+
9360.00, 9500.00, 9640.00, 9780.00, 9920.00, 10060.0, 10200.0, 10340.0,
|
|
432
|
+
10480.0, 10620.0, 10760.0, 10900.0, 11040.0, 11180.0, 11320.0, 11460.0,
|
|
433
|
+
11600.0, 11740.0, 11880.0, 12020.0, 12160.0, 12300.0, 12440.0, 12580.0,
|
|
434
|
+
12720.0, 12860.0, 13000.0, 13140.0, 13280.0, 13420.0, 13560.0, 13700.0,
|
|
435
|
+
13840.0, 13980.0, 14120.0, 14260.0, 14400.0, 14540.0, 14680.0, 14820.0,
|
|
436
|
+
14960.0, 15100.0, 15240.0, 15380.0, 15520.0, 15660.0, 15800.0, 15940.0,
|
|
437
|
+
16080.0, 16220.0, 16360.0, 16500.0, 16640.0, 16780.0, 16920.0, 17060.0,
|
|
438
|
+
17200.0, 17340.0, 17480.0, 17620.0, 17760.0, 17900.0, 18040.0, 18180.0,
|
|
439
|
+
18320.0, 18460.0, 18600.0, 18740.0, 18880.0, 19020.0, 19160.0, 19300.0,
|
|
440
|
+
19440.0, 19580.0, 19720.0, 19860.0, 20000.0
|
|
441
|
+
]
|
|
442
|
+
|
|
443
|
+
length = [
|
|
444
|
+
482.639, 518.357, 555.868, 595.186, 636.377, 679.478, 724.515, 771.554,
|
|
445
|
+
820.639, 871.805, 925.074, 980.527, 1038.17, 1098.07, 1160.24, 1224.76,
|
|
446
|
+
1291.66, 1360.97, 1432.71, 1506.92, 1583.63, 1662.89, 1744.71, 1829.12,
|
|
447
|
+
1916.14, 2005.81, 2098.15, 2193.20, 2290.93, 2391.48, 2494.84, 2601.00,
|
|
448
|
+
2709.95, 2821.71, 2936.29, 3053.69, 3173.94, 3297.03, 3422.98, 3551.74,
|
|
449
|
+
3683.35, 3817.78, 3955.02, 4095.08, 4237.92, 4383.57, 4531.99, 4683.10,
|
|
450
|
+
4836.97, 4993.50, 5152.65, 5314.53, 5478.98, 5645.95, 5815.49, 5987.49,
|
|
451
|
+
6161.89, 6338.73, 6517.89, 6699.35, 6883.04, 7068.94, 7256.91, 7446.90,
|
|
452
|
+
7638.99, 7833.72, 8030.71, 8229.62, 8430.37, 8632.92, 8837.16, 9043.03,
|
|
453
|
+
9250.44, 9459.37, 9669.72, 9881.46, 10094.5, 10308.7, 10524.0, 10740.4,
|
|
454
|
+
10957.8, 11176.1, 11395.2, 11615.0, 11835.5, 12056.6, 12278.2, 12500.2,
|
|
455
|
+
12722.5, 12945.2, 13168.0, 13391.0, 13614.1, 13837.0, 14060.0, 14282.6,
|
|
456
|
+
14505.1, 14727.2, 14948.9, 15160.9, 15367.5
|
|
457
|
+
]
|
|
458
|
+
|
|
459
|
+
Mu_Spline = InterpolatedUnivariateSpline(E_values, length)
|
|
460
|
+
return Mu_Spline(keV*1000) * 1e-6 # Convert to m
|
|
461
|
+
|
|
462
|
+
def J4M_efficiency(theta, keV, tSi = 318.5e-6, tAl = 4.5e-6, tK = 8e-6):
|
|
463
|
+
"""
|
|
464
|
+
Calculate the detector efficiency of the Jungfrau4M detector for a given photon energy.
|
|
465
|
+
|
|
466
|
+
Parameters
|
|
467
|
+
----------
|
|
468
|
+
theta : float
|
|
469
|
+
Scattering angle in radians.
|
|
470
|
+
keV : float
|
|
471
|
+
Photon energy in keV.
|
|
472
|
+
tSi : float, optional
|
|
473
|
+
Thickness of the silicon sensor in meters. Default is 318.5e-6.
|
|
474
|
+
tAl : float, optional
|
|
475
|
+
Thickness (total) of the aluminum layer + sputter coating in meters. Default is 4.5e-6.
|
|
476
|
+
tK : float, optional
|
|
477
|
+
Thickness of the kapton layer in meters. Default is 8e-6.
|
|
478
|
+
|
|
479
|
+
Returns
|
|
480
|
+
-------
|
|
481
|
+
float
|
|
482
|
+
Detector efficiency of the Jungfrau4M detector, from 0-1.
|
|
483
|
+
|
|
484
|
+
Notes
|
|
485
|
+
-----
|
|
486
|
+
Uses spline interpolation of tabulated values to calculate the detector efficiency.
|
|
487
|
+
"""
|
|
488
|
+
return (1-np.exp(-tSi /(np.cos(theta) * Si_attenuation_length(keV))))*(np.exp(-tK /(np.cos(theta) *KaptonHN_attenuation_length(keV)))) * (np.exp(-tAl /(np.cos(theta) * Al_attenuation_length(keV))))
|