-
Notifications
You must be signed in to change notification settings - Fork 0
Add function which calculates Polarization & propagating uncertainties #97
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
88202fa
46cee18
bce1bbe
60904c0
a3c5fbf
b1b2e7e
c3e53d8
6bcfac1
1c2e292
bbfd749
b4ff8ed
68be42e
8cd8a33
a9ee9a2
bc707fb
897d12f
e2a42aa
968f77d
f363136
2fc7803
68d1744
7ba2d5a
7bd51d0
9e2b122
e7d10f4
8632f3a
08855ef
93acddd
97628ec
2f5db04
67e90cb
cbb43ae
24fb6d0
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -7,6 +7,7 @@ | |
import pytest | ||
import scipp as sc | ||
from ophyd_async.testing import get_mock_put, set_mock_value | ||
from uncertainties import ufloat, unumpy | ||
|
||
from ibex_bluesky_core.devices.simpledae import ( | ||
VARIANCE_ADDITION, | ||
|
@@ -19,7 +20,7 @@ | |
tof_bounded_spectra, | ||
wavelength_bounded_spectra, | ||
) | ||
from ibex_bluesky_core.devices.simpledae._reducers import DSpacingMappingReducer | ||
from ibex_bluesky_core.devices.simpledae._reducers import DSpacingMappingReducer, polarization | ||
|
||
|
||
@pytest.fixture | ||
|
@@ -965,6 +966,93 @@ def test_wavelength_bounded_spectra_bounds_missing_or_too_many(data: list[float] | |
) | ||
|
||
|
||
# Polarization | ||
@pytest.mark.parametrize( | ||
("a", "b", "variance_a", "variance_b"), | ||
[ | ||
# Case 1: Symmetric case with equal uncertainties | ||
(5.0, 3.0, 0.1, 0.1), | ||
# Case 2: Asymmetric case with different uncertainties | ||
(10.0, 6.0, 0.2, 0.3), | ||
# Case 3: Case with larger values and different uncertainty magnitudes | ||
(100.0, 60.0, 1.0, 2.0), | ||
], | ||
) | ||
def test_polarization_function_calculates_accurately(a, b, variance_a, variance_b): | ||
# 'Uncertainties' library ufloat type; a nominal value and an error value | ||
a_ufloat = ufloat(a, variance_a) | ||
b_ufloat = ufloat(b, variance_b) | ||
|
||
# polarization value, i.e. (a - b) / (a + b) | ||
polarization_ufloat = (a_ufloat.n - b_ufloat.n) / (a_ufloat.n + b_ufloat.n) | ||
|
||
# the partial derivatives of a and b, calculated with 'uncertainties' library's ufloat type | ||
partial_a = (2 * b_ufloat.n) / ((a_ufloat.n + b_ufloat.n) ** 2) | ||
partial_b = (-2 * a_ufloat.n) / ((a_ufloat.n + b_ufloat.n) ** 2) | ||
|
||
# variance calculated with 'uncertainties' library | ||
variance = (partial_a**2 * a_ufloat.s) + (partial_b**2 * b_ufloat.s) | ||
uncertainty = variance**0.5 # uncertainty is sqrt of variance | ||
|
||
# Two scipp scalars, to test our polarization function | ||
var_a = sc.scalar(value=a, variance=variance_a, unit="", dtype="float64") | ||
var_b = sc.scalar(value=b, variance=variance_b, unit="", dtype="float64") | ||
result_value = polarization(var_a, var_b) | ||
result_uncertainy = (result_value.variance) ** 0.5 # uncertainty is sqrt of variance | ||
Comment on lines
+983
to
+1001
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't quite understand what's going on in this test; we shouldn't need partial derivatives with the uncertainties library as that's what the library is doing for us. I think conceptually it should be:
i.e. similar to the test below There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. whoops yes sorry I meant to remove the unnecessary bits and forgot, will do it now. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Actually sorry, pretty sure we need those; when calculating the polarization from the nominal values, the variances do not get carried into that value, so we need to calculate it in long form. It was easier with the arrays, because it was just adding an array of ufloats. But in this test we are calculating with the ufloat's nominal values themselves. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. for whatever reason, ufloat library really didn't like doing this
And needs it to be nominal values.... which then creates |
||
|
||
assert result_value.value == pytest.approx(polarization_ufloat) | ||
assert result_uncertainy == pytest.approx(uncertainty) | ||
|
||
|
||
# test that arrays are supported | ||
@pytest.mark.parametrize( | ||
("a", "b", "variances_a", "variances_b"), | ||
[ | ||
([5.0, 10.0, 100.0], [3.0, 6.0, 60.0], [0.1, 0.2, 1.0], [0.1, 0.3, 2.0]), | ||
], | ||
) | ||
def test_polarization_2_arrays(a, b, variances_a, variances_b): | ||
# 'Uncertainties' library ufloat type; a nominal value and an error value | ||
|
||
a_arr = unumpy.uarray(a, [v**0.5 for v in variances_a]) # convert variances to std dev | ||
b_arr = unumpy.uarray(b, [v**0.5 for v in variances_b]) | ||
|
||
# polarization value, i.e. (a - b) / (a + b) | ||
polarization_ufloat = (a_arr - b_arr) / (a_arr + b_arr) | ||
|
||
var_a = sc.array(dims="x", values=a, variances=variances_a, unit="", dtype="float64") | ||
var_b = sc.array(dims="x", values=b, variances=variances_b, unit="", dtype="float64") | ||
|
||
result_value = polarization(var_a, var_b) | ||
|
||
result_uncertainties = (result_value.variances) ** 0.5 | ||
|
||
assert result_value.values == pytest.approx(unumpy.nominal_values(polarization_ufloat)) | ||
assert result_uncertainties == pytest.approx(unumpy.std_devs(polarization_ufloat)) | ||
|
||
|
||
# test that units don't match | ||
def test_polarization_units_mismatch(): | ||
var_a = sc.scalar(value=1, variance=0.1, unit="m", dtype="float64") | ||
var_b = sc.scalar(value=1, variance=0.1, unit="u", dtype="float64") | ||
|
||
with pytest.raises( | ||
expected_exception=ValueError, match=r"The units of a and b are not equivalent." | ||
): | ||
polarization(var_a, var_b) | ||
|
||
|
||
# test that arrays are of unmatching sizes | ||
def test_polarization_arrays_of_different_sizes(): | ||
var_a = sc.array(dims=["x"], values=[1, 2], variances=[0.1, 0.1], unit="m", dtype="float64") | ||
var_b = sc.array(dims=["x"], values=[1], variances=[0.1], unit="m", dtype="float64") | ||
|
||
with pytest.raises( | ||
expected_exception=ValueError, match=r"Dimensions/shape of a and b must match." | ||
): | ||
polarization(var_a, var_b) | ||
|
||
|
||
@pytest.mark.parametrize( | ||
("current_period", "mon_integrals", "det_integrals"), | ||
[ | ||
|
Uh oh!
There was an error while loading. Please reload this page.