Skip to content

Commit 1e2baa9

Browse files
committed
feat: implement calibration and reduction
1 parent e5ebabd commit 1e2baa9

File tree

2 files changed

+189
-6
lines changed

2 files changed

+189
-6
lines changed

src/ess/estia/calibration.py

+173
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
import numpy as np
2+
import scipp as sc
3+
4+
from ..reflectometry.normalization import (
5+
reduce_from_events_to_lz,
6+
reduce_from_events_to_q,
7+
reduce_from_lz_to_q,
8+
)
9+
10+
11+
def solve_for_calibration_parameters(Io, Is):
12+
Iopp, Iopa, Ioap, Ioaa = Io
13+
Ipp, Ipa, Iap, Iaa = Is
14+
15+
I0 = 2 * (Iopp * Ioaa - Iopa * Ioap) / (Iopp + Ioaa - Iopa - Ioap)
16+
rho = (Ioaa - Ioap) / (Iopp - Iopa)
17+
alp = (Ioaa - Iopa) / (Iopp - Ioap)
18+
19+
Rspp_plus_Rsaa = (
20+
4
21+
* (rho * alp * Ipp + Iaa + rho * Ipa + alp * Iap)
22+
/ ((1 + rho) * (1 + alp) * I0)
23+
)
24+
Pp = sc.sqrt(
25+
(Ipp + Iaa - Ipa - Iap)
26+
* (alp * (Ipp - Iap) - Iaa + Ipa)
27+
/ (
28+
(rho * alp * Ipp + Iaa + rho * Ipa + alp * Iap)
29+
* (rho * (Ipp - Ipa) - Iaa + Iap)
30+
)
31+
)
32+
Ap = sc.sqrt(
33+
(Ipp + Iaa - Ipa - Iap)
34+
* (rho * (Ipp - Ipa) - Iaa + Iap)
35+
/ (
36+
(rho * alp * Ipp + Iaa + rho * Ipa + alp * Iap)
37+
* (alp * (Ipp - Iap) - Iaa + Ipa)
38+
)
39+
)
40+
Rs = sc.sqrt(
41+
(alp * (Ipp - Iap) - Iaa + Ipa)
42+
* (rho * (Ipp - Ipa) - Iaa + Iap)
43+
/ ((rho * alp * Ipp + Iaa + rho * Ipa + alp * Iap) * (Ipp + Iaa - Ipa - Iap))
44+
)
45+
46+
Pa = -rho * Pp
47+
Aa = -alp * Ap
48+
49+
Rspp_minus_Rsaa = Rs * Rspp_plus_Rsaa
50+
Rspp = (Rspp_plus_Rsaa + Rspp_minus_Rsaa) / 2
51+
Rsaa = Rspp_plus_Rsaa - Rspp
52+
53+
return I0 / 4, Pp, Pa, Ap, Aa, Rspp, Rsaa
54+
55+
56+
def generate_valid_calibration_parameters():
57+
I0 = np.random.random()
58+
Pp = np.random.random()
59+
Pa = -np.random.random()
60+
Ap = np.random.random()
61+
Aa = -np.random.random()
62+
Rspp = np.random.random()
63+
Rsaa = Rspp * np.random.random()
64+
return tuple(map(sc.scalar, (I0, Pp, Pa, Ap, Aa, Rspp, Rsaa)))
65+
66+
67+
def intensity_from_parameters(I0, Pp, Pa, Ap, Aa, Rpp, Rpa, Rap, Raa):
68+
return (
69+
I0
70+
* (
71+
Rpp * (1 + Ap) * (1 + Pp)
72+
+ Rpa * (1 - Ap) * (1 + Pp)
73+
+ Rap * (1 + Ap) * (1 - Pp)
74+
+ Raa * (1 - Ap) * (1 - Pp)
75+
),
76+
I0
77+
* (
78+
Rpp * (1 + Aa) * (1 + Pp)
79+
+ Rpa * (1 - Aa) * (1 + Pp)
80+
+ Rap * (1 + Aa) * (1 - Pp)
81+
+ Raa * (1 - Aa) * (1 - Pp)
82+
),
83+
I0
84+
* (
85+
Rpp * (1 + Ap) * (1 + Pa)
86+
+ Rpa * (1 - Ap) * (1 + Pa)
87+
+ Rap * (1 + Ap) * (1 - Pa)
88+
+ Raa * (1 - Ap) * (1 - Pa)
89+
),
90+
I0
91+
* (
92+
Rpp * (1 + Aa) * (1 + Pa)
93+
+ Rpa * (1 - Aa) * (1 + Pa)
94+
+ Rap * (1 + Aa) * (1 - Pa)
95+
+ Raa * (1 - Aa) * (1 - Pa)
96+
),
97+
)
98+
99+
100+
def correction_matrix(Pp, Pa, Ap, Aa):
101+
return [
102+
[
103+
(1 + Pp) * (1 + Ap),
104+
(1 + Pp) * (1 - Ap),
105+
(1 - Pp) * (1 + Ap),
106+
(1 - Pp) * (1 - Ap),
107+
],
108+
[
109+
(1 + Pp) * (1 + Aa),
110+
(1 + Pp) * (1 - Aa),
111+
(1 - Pp) * (1 + Aa),
112+
(1 - Pp) * (1 - Aa),
113+
],
114+
[
115+
(1 + Pa) * (1 + Ap),
116+
(1 + Pa) * (1 - Ap),
117+
(1 - Pa) * (1 + Ap),
118+
(1 - Pa) * (1 - Ap),
119+
],
120+
[
121+
(1 + Pa) * (1 + Aa),
122+
(1 + Pa) * (1 - Aa),
123+
(1 - Pa) * (1 + Aa),
124+
(1 - Pa) * (1 - Aa),
125+
],
126+
]
127+
128+
129+
def compute_calibration_factors(Io, Is):
130+
I0, Pp, Pa, Ap, Aa, _, _ = solve_for_calibration_parameters(Io, Is)
131+
return I0, correction_matrix(Pp, Pa, Ap, Aa)
132+
133+
134+
def linsolve(A, b):
135+
return np.linalg.solve(
136+
np.stack([[a.values for a in row] for row in A]),
137+
np.stack([bi.values for bi in b], axis=-1),
138+
)
139+
140+
141+
def computer_reflectivity_calibrate_on_q(
142+
reference_supermirror,
143+
reference_polarized_supermirror,
144+
sample,
145+
qbins,
146+
):
147+
reference_supermirror = [
148+
reduce_from_lz_to_q(i, qbins) for i in reference_supermirror
149+
]
150+
reference_polarized_supermirror = [
151+
reduce_from_lz_to_q(i, qbins) for i in reference_polarized_supermirror
152+
]
153+
sample = [reduce_from_events_to_q(i, qbins) for i in sample]
154+
I0, C = compute_calibration_factors(
155+
reference_supermirror, reference_polarized_supermirror
156+
)
157+
return [i / I0 for i in linsolve(C, sample)]
158+
159+
160+
def computer_reflectivity_calibrate_on_lz(
161+
reference_supermirror,
162+
reference_polarized_supermirror,
163+
sample,
164+
wbins,
165+
qbins,
166+
):
167+
sample = reduce_from_events_to_lz(sample, wbins)
168+
I0, C = compute_calibration_factors(
169+
reference_supermirror, reference_polarized_supermirror
170+
)
171+
sample = linsolve(C, sample)
172+
I0 = reduce_from_lz_to_q(I0, qbins)
173+
return [i / I0 for i in reduce_from_lz_to_q(sample, qbins)]

src/ess/reflectometry/normalization.py

+16-6
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,18 @@
2020
)
2121

2222

23+
def reduce_from_events_to_q(da, qbins):
24+
return da.bins.concat().bin(Q=qbins)
25+
26+
27+
def reduce_from_events_to_lz(da, wbins):
28+
return da.bins.concat(('stripe',)).bin(wavelength=wbins)
29+
30+
31+
def reduce_from_lz_to_q(da, qbins):
32+
return da.flatten(to='Q').hist(Q=qbins)
33+
34+
2335
def reduce_reference(
2436
reference: ReducibleData[ReferenceRun],
2537
wavelength_bins: WavelengthBins,
@@ -40,7 +52,7 @@ def reduce_reference(
4052
)
4153
reference = reference.bins.assign_masks(invalid=sc.isnan(R))
4254
reference = reference / R
43-
out = reference.bins.concat(('stripe',)).hist(wavelength=wavelength_bins)
55+
out = reduce_from_events_to_lz(reference, wavelength_bins).hist()
4456

4557
if 'position' in reference.coords:
4658
out.coords['position'] = reference.coords['position'].mean('stripe')
@@ -59,8 +71,8 @@ def reduce_sample_over_q(
5971
6072
Returns reflectivity as a function of :math:`Q`.
6173
"""
62-
s = sample.bins.concat().bin(Q=qbins)
63-
h = sc.values(reference.hist(Q=s.coords['Q']))
74+
s = reduce_from_events_to_q(sample, qbins)
75+
h = sc.values(reduce_from_lz_to_q(reference, s.coords['Q']))
6476
R = s / h.data
6577
R.coords['Q_resolution'] = sc.sqrt(
6678
(
@@ -85,9 +97,7 @@ def reduce_sample_over_zw(
8597
8698
Returns reflectivity as a function of ``blade``, ``wire`` and :math:`\\wavelength`.
8799
"""
88-
return sample.bins.concat(('stripe',)).bin(wavelength=wbins) / sc.values(
89-
reference.data
90-
)
100+
return reduce_from_events_to_lz(sample, wbins) / sc.values(reference.data)
91101

92102

93103
providers = (

0 commit comments

Comments
 (0)