429 lines
16 KiB
Python
429 lines
16 KiB
Python
"""
|
|
@package tests.test_data
|
|
unit tests for pmsco.data
|
|
|
|
the purpose of these tests is to mainly to check the syntax, and correct data types,
|
|
i.e. anything that could cause a run-time error.
|
|
calculation results are sometimes checked for plausibility but not exact values,
|
|
depending on the level of debugging required for a specific part of the code.
|
|
|
|
to run the tests, change to the directory which contains the tests directory, and execute =nosetests=.
|
|
|
|
@pre nose must be installed (python-nose package on Debian).
|
|
|
|
@author Matthias Muntwiler, matthias.muntwiler@psi.ch
|
|
|
|
@copyright (c) 2015 by Paul Scherrer Institut @n
|
|
Licensed under the Apache License, Version 2.0 (the "License"); @n
|
|
you may not use this file except in compliance with the License.
|
|
You may obtain a copy of the License at
|
|
http://www.apache.org/licenses/LICENSE-2.0
|
|
"""
|
|
|
|
import unittest
|
|
import math
|
|
import numpy as np
|
|
import pmsco.data as md
|
|
|
|
|
|
class TestDataFunctions(unittest.TestCase):
|
|
def setUp(self):
|
|
# before each test method
|
|
shape = (10, )
|
|
self.e_scan = md.create_data(shape, dtype=md.DTYPE_EI)
|
|
self.e_scan['e'] = np.linspace(100.0, 200.0, shape[0])
|
|
self.e_scan['i'] = np.linspace(0.0, 10.0, shape[0])
|
|
|
|
shape = (12, )
|
|
self.ea_scan = md.create_data(shape, dtype=md.DTYPE_ETPAI)
|
|
self.ea_scan[0] = (1.0, 0.0, 0.0, -1.0, 1.0)
|
|
self.ea_scan[1] = (1.0, 0.0, 0.0, 0.0, 1.0)
|
|
self.ea_scan[2] = (1.0, 0.0, 0.0, 1.0, 1.0)
|
|
self.ea_scan[3] = (1.0, 0.0, 0.0, 2.0, 1.0)
|
|
self.ea_scan[4] = (2.0, 0.0, 0.0, -1.0, 2.0)
|
|
self.ea_scan[5] = (2.0, 0.0, 0.0, 0.0, 2.0)
|
|
self.ea_scan[6] = (2.0, 0.0, 0.0, 1.0, 2.0)
|
|
self.ea_scan[7] = (2.0, 0.0, 0.0, 2.0, 2.0)
|
|
self.ea_scan[8] = (3.0, 0.0, 0.0, -1.0, 3.0)
|
|
self.ea_scan[9] = (3.0, 0.0, 0.0, 0.0, 3.0)
|
|
self.ea_scan[10] = (3.0, 0.0, 0.0, 1.0, 3.0)
|
|
self.ea_scan[11] = (3.0, 0.0, 0.0, 2.0, 3.0)
|
|
|
|
shape = (10, )
|
|
self.holo_scan = md.create_data(shape, dtype=md.DTYPE_ETPI)
|
|
self.holo_scan[0] = (1.0, 0.0, 0.0, 1.0)
|
|
self.holo_scan[1] = (1.0, 1.0, 0.0, 2.0)
|
|
self.holo_scan[2] = (1.0, 1.0, 120.0, 3.0)
|
|
self.holo_scan[3] = (1.0, 1.0, 240.0, 4.0)
|
|
self.holo_scan[4] = (1.0, 2.0, 0.0, 5.0)
|
|
self.holo_scan[5] = (1.0, 2.0, 60.0, 6.0)
|
|
self.holo_scan[6] = (1.0, 2.0, 120.0, 7.0)
|
|
self.holo_scan[7] = (1.0, 2.0, 180.0, 8.0)
|
|
self.holo_scan[8] = (1.0, 2.0, 240.0, 9.0)
|
|
self.holo_scan[9] = (1.0, 2.0, 300.0, 10.0)
|
|
|
|
def tearDown(self):
|
|
# after each test method
|
|
pass
|
|
|
|
@classmethod
|
|
def setup_class(cls):
|
|
# before any methods in this class
|
|
pass
|
|
|
|
@classmethod
|
|
def teardown_class(cls):
|
|
# teardown_class() after any methods in this class
|
|
pass
|
|
|
|
def test_create_data(self):
|
|
shape = (10, )
|
|
data = md.create_data(shape, dtype=md.DTYPE_ETPAIS)
|
|
expected_names = ('e', 't', 'p', 'a', 'i', 's')
|
|
self.assertItemsEqual(data.dtype.names, expected_names)
|
|
self.assertEqual(data.shape, shape)
|
|
|
|
def test_detect_scan_mode_1d(self):
|
|
scan_mode, scan_positions = md.detect_scan_mode(self.e_scan)
|
|
|
|
expected_mode = ['e']
|
|
expected_positions = {}
|
|
expected_positions['e'] = np.linspace(0.0, 10.0, 10)
|
|
|
|
self.assertItemsEqual(scan_mode, expected_mode)
|
|
self.assertItemsEqual(scan_positions, expected_positions)
|
|
|
|
def test_detect_scan_mode_2d(self):
|
|
scan_mode, scan_positions = md.detect_scan_mode(self.ea_scan)
|
|
|
|
expected_mode = ['e', 'a']
|
|
expected_positions = {}
|
|
expected_positions['e'] = np.asarray((1.0, 2.0, 3.0))
|
|
expected_positions['t'] = np.zeros((1))
|
|
expected_positions['p'] = np.zeros((1))
|
|
expected_positions['a'] = np.asarray((-1.0, 0.0, 1.0, 2.0))
|
|
|
|
self.assertItemsEqual(scan_mode, expected_mode)
|
|
self.assertItemsEqual(scan_positions, expected_positions)
|
|
|
|
def test_detect_scan_mode_holo(self):
|
|
scan_mode, scan_positions = md.detect_scan_mode(self.holo_scan)
|
|
|
|
expected_mode = ['t', 'p']
|
|
expected_positions = {}
|
|
expected_positions['e'] = np.ones((1))
|
|
expected_positions['t'] = self.holo_scan['t']
|
|
expected_positions['p'] = self.holo_scan['p']
|
|
|
|
self.assertItemsEqual(scan_mode, expected_mode)
|
|
self.assertItemsEqual(scan_positions, expected_positions)
|
|
|
|
def test_detect_scan_mode_theta(self):
|
|
scan = self.holo_scan
|
|
scan['t'] = np.linspace(1.0, 2.0, scan.shape[0])
|
|
scan['p'] = 3.3
|
|
scan_mode, scan_positions = md.detect_scan_mode(scan)
|
|
|
|
expected_mode = ['t']
|
|
expected_positions = {}
|
|
expected_positions['e'] = np.ones((1))
|
|
expected_positions['t'] = np.linspace(1.0, 2.0, scan.shape[0])
|
|
expected_positions['p'] = np.ones((1)) * 3.3
|
|
|
|
self.assertItemsEqual(scan_mode, expected_mode)
|
|
self.assertItemsEqual(scan_positions, expected_positions)
|
|
|
|
def test_calc_modfunc_mean_1d(self):
|
|
modf = md.calc_modfunc_mean(self.e_scan)
|
|
|
|
exp_modf = self.e_scan.copy()
|
|
exp_modf['i'] = (self.e_scan['i'] - 5.0) / 5.0
|
|
|
|
np.testing.assert_allclose(modf['e'], exp_modf['e'])
|
|
np.testing.assert_allclose(modf['i'], exp_modf['i'])
|
|
|
|
def test_calc_modfunc_mean_2d(self):
|
|
modf = md.calc_modfunc_mean(self.ea_scan)
|
|
|
|
exp_modf = self.ea_scan.copy()
|
|
exp_modf['i'][0] = -0.5
|
|
exp_modf['i'][1] = -0.5
|
|
exp_modf['i'][2] = -0.5
|
|
exp_modf['i'][3] = -0.5
|
|
exp_modf['i'][4] = 0.0
|
|
exp_modf['i'][5] = 0.0
|
|
exp_modf['i'][6] = 0.0
|
|
exp_modf['i'][7] = 0.0
|
|
exp_modf['i'][8] = 0.5
|
|
exp_modf['i'][9] = 0.5
|
|
exp_modf['i'][10] = 0.5
|
|
exp_modf['i'][11] = 0.5
|
|
|
|
np.testing.assert_allclose(modf['e'], exp_modf['e'])
|
|
np.testing.assert_allclose(modf['t'], exp_modf['t'])
|
|
np.testing.assert_allclose(modf['p'], exp_modf['p'])
|
|
np.testing.assert_allclose(modf['a'], exp_modf['a'])
|
|
np.testing.assert_allclose(modf['i'], exp_modf['i'])
|
|
|
|
def test_calc_modfunc_loess_1d(self):
|
|
"""
|
|
check that the result of msc_data.calc_modfunc_loess() is between -1 and 1.
|
|
"""
|
|
modf = md.calc_modfunc_loess(self.e_scan)
|
|
self.assertEqual(self.e_scan.shape, modf.shape)
|
|
exp_modf = self.e_scan.copy()
|
|
np.testing.assert_allclose(modf['e'], exp_modf['e'])
|
|
exp_modf['i'] = -1.000001
|
|
np.testing.assert_array_less(exp_modf['i'], modf['i'])
|
|
exp_modf['i'] = +1.000001
|
|
np.testing.assert_array_less(modf['i'], exp_modf['i'])
|
|
|
|
def test_calc_modfunc_loess_1d_nan(self):
|
|
"""
|
|
check that data.calc_modfunc_loess() ignores NaNs gracefully.
|
|
"""
|
|
modified_index = 2
|
|
self.e_scan['i'][modified_index] = np.nan
|
|
modf = md.calc_modfunc_loess(self.e_scan)
|
|
exp_modf = self.e_scan.copy()
|
|
self.assertEqual(self.e_scan.shape, modf.shape)
|
|
np.testing.assert_allclose(modf['e'], exp_modf['e'])
|
|
self.assertTrue(math.isnan(modf['i'][modified_index]))
|
|
modf['i'][modified_index] = 0.0
|
|
exp_modf['i'] = -1.000001
|
|
np.testing.assert_array_less(exp_modf['i'], modf['i'])
|
|
exp_modf['i'] = +1.000001
|
|
np.testing.assert_array_less(modf['i'], exp_modf['i'])
|
|
|
|
def test_calc_modfunc_loess_2d(self):
|
|
"""
|
|
check that the msc_data.calc_modfunc_loess() function does approximately what we want for a two-dimensional dataset.
|
|
"""
|
|
n_e = 10
|
|
n_a = 15
|
|
shape = (n_e * n_a, )
|
|
scan = md.create_data(shape, dtype=md.DTYPE_ETPAI)
|
|
|
|
e_range = np.linspace(100.0, 200.0, n_e)
|
|
a_range = np.linspace(-15.0, 15.0, n_a)
|
|
a_grid, e_grid = np.meshgrid(a_range, e_range)
|
|
scan['e'] = np.ravel(e_grid)
|
|
scan['a'] = np.ravel(a_grid)
|
|
scan['t'] = 0.0
|
|
scan['p'] = 90.0
|
|
scan['i'] = 0.02 * scan['e'] + 0.03 * scan['a'] + np.sin((scan['e'] - 150) / 50 * math.pi) + np.sin(scan['a'] / 180 * math.pi)
|
|
modf = md.calc_modfunc_loess(scan)
|
|
self.assertEqual(scan.shape, modf.shape)
|
|
exp_modf = scan.copy()
|
|
|
|
np.testing.assert_allclose(modf['e'], exp_modf['e'])
|
|
np.testing.assert_allclose(modf['t'], exp_modf['t'])
|
|
np.testing.assert_allclose(modf['p'], exp_modf['p'])
|
|
np.testing.assert_allclose(modf['a'], exp_modf['a'])
|
|
|
|
exp_modf['i'] = -1.000001
|
|
np.testing.assert_array_less(exp_modf['i'], modf['i'])
|
|
exp_modf['i'] = +1.000001
|
|
np.testing.assert_array_less(modf['i'], exp_modf['i'])
|
|
# this is rough estimate of the result, manually optimized by trial and error in Igor.
|
|
# the R factor should be sensitive enough to detect mixed-up axes.
|
|
exp_modf['i'] = 0.03 * np.sin((scan['e'] - 150) / 50 * math.pi)
|
|
rf = md.rfactor(modf, exp_modf)
|
|
print rf
|
|
self.assertLessEqual(rf, 0.50)
|
|
|
|
def test_alpha_mirror_average(self):
|
|
n_e = 10
|
|
n_a = 15
|
|
shape = (n_e * n_a, )
|
|
scan = md.create_data(shape, dtype=md.DTYPE_ETPAI)
|
|
|
|
e_range = np.linspace(100.0, 200.0, n_e)
|
|
a_range = np.linspace(-15.0, 15.0, n_a)
|
|
a_grid, e_grid = np.meshgrid(a_range, e_range)
|
|
scan['e'] = np.ravel(e_grid)
|
|
scan['a'] = np.ravel(a_grid)
|
|
scan['t'] = 0.0
|
|
scan['p'] = 90.0
|
|
scan['i'] = 0.02 * scan['e'] + 0.03 * scan['a']
|
|
act_result = md.alpha_mirror_average(scan)
|
|
exp_result = scan.copy()
|
|
exp_result['i'] = 0.02 * scan['e']
|
|
|
|
np.testing.assert_allclose(act_result['e'], exp_result['e'])
|
|
np.testing.assert_allclose(act_result['t'], exp_result['t'])
|
|
np.testing.assert_allclose(act_result['p'], exp_result['p'])
|
|
np.testing.assert_allclose(act_result['a'], exp_result['a'])
|
|
np.testing.assert_allclose(act_result['i'], exp_result['i'])
|
|
|
|
def test_alpha_average(self):
|
|
data = md.create_data((20), dtype=md.DTYPE_ETPAIS)
|
|
data['e'][0:10] = 11
|
|
data['e'][10:20] = 22
|
|
data['a'][0:10] = np.arange(10)
|
|
data['a'][10:20] = np.arange(10)
|
|
data['i'] = np.arange(20)
|
|
data['s'] = np.arange(20) / 10.0
|
|
|
|
exp_result = md.create_data((2), dtype=md.DTYPE_ETPIS)
|
|
exp_result['e'][0] = 11
|
|
exp_result['e'][1] = 22
|
|
exp_result['i'][0] = 4.5
|
|
exp_result['i'][1] = 14.5
|
|
exp_result['s'] = exp_result['i'] / 10.0
|
|
|
|
act_result = md.alpha_average(data)
|
|
|
|
np.testing.assert_allclose(act_result['e'], exp_result['e'])
|
|
np.testing.assert_allclose(act_result['t'], exp_result['t'])
|
|
np.testing.assert_allclose(act_result['p'], exp_result['p'])
|
|
np.testing.assert_allclose(act_result['i'], exp_result['i'])
|
|
np.testing.assert_allclose(act_result['s'], exp_result['s'])
|
|
|
|
def test_phi_average(self):
|
|
data = self.holo_scan.copy()
|
|
|
|
exp_result = md.create_data((3), dtype=[('e', 'f4'), ('t', 'f4'), ('i', 'f4')])
|
|
exp_result['e'] = np.asarray([1, 1, 1])
|
|
exp_result['t'] = np.asarray([0, 1, 2])
|
|
exp_result['i'] = np.asarray([1, 3, 7.5])
|
|
|
|
act_result = md.phi_average(data)
|
|
|
|
np.testing.assert_allclose(act_result['e'], exp_result['e'])
|
|
np.testing.assert_allclose(act_result['t'], exp_result['t'])
|
|
np.testing.assert_allclose(act_result['i'], exp_result['i'])
|
|
|
|
def test_filter_tp(self):
|
|
data = md.create_data((17), dtype=md.DTYPE_ETPAIS)
|
|
data['e'] = 100.0
|
|
data['t'] = np.array([0.0, 0.0, 0.0, 0.0, 1.0, 1.0, 1.0, 1.0, 2.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0, 3.0])
|
|
data['p'] = np.array([0.0, 1.0, 2.0, 3.0, 0.0, 1.01, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 0.0, 1.0, 2.0, 3.0, 3.1])
|
|
data['a'] = 0.0
|
|
data['i'] = data['t'] * 10.0 + data['p']
|
|
data['s'] = np.sqrt(data['i'])
|
|
# duplicate
|
|
data['p'][10] = 0.0
|
|
|
|
filter = md.create_data((10), dtype=md.DTYPE_ETPI)
|
|
filter['e'] = 100.0
|
|
filter['t'] = np.array([0.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0])
|
|
filter['p'] = np.array([0.0, 0.0, 1.0, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0])
|
|
filter['i'] = -99.999
|
|
|
|
exp_result = md.create_data((10), dtype=md.DTYPE_ETPAIS)
|
|
exp_result['e'] = 100.0
|
|
exp_result['t'] = np.array([0.0, 1.0, 1.00, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0])
|
|
exp_result['p'] = np.array([0.0, 0.0, 1.01, 0.0, 1.0, 2.0, 0.0, 1.0, 2.0, 3.0])
|
|
exp_result['a'] = 0.0
|
|
exp_result['i'] = exp_result['t'] * 10.0 + exp_result['p']
|
|
exp_result['s'] = np.sqrt(exp_result['i'])
|
|
exp_result['p'][5] = 0.0
|
|
|
|
act_result = md.filter_tp(data, filter)
|
|
|
|
np.testing.assert_allclose(act_result['e'], exp_result['e'])
|
|
np.testing.assert_allclose(act_result['t'], exp_result['t'])
|
|
np.testing.assert_allclose(act_result['p'], exp_result['p'])
|
|
np.testing.assert_allclose(act_result['a'], exp_result['a'])
|
|
np.testing.assert_allclose(act_result['i'], exp_result['i'])
|
|
np.testing.assert_allclose(act_result['s'], exp_result['s'])
|
|
|
|
def test_interpolate_hemi_scan(self):
|
|
n_t = 91
|
|
n_p = 11
|
|
shape = (n_t * n_p, )
|
|
calc_data = md.create_data(shape, dtype=md.DTYPE_TPI)
|
|
|
|
t_range = np.linspace(0.0, 90.0, n_t)
|
|
p_range = np.linspace(0.0, 360.0, n_p)
|
|
t_grid, p_grid = np.meshgrid(t_range, p_range)
|
|
calc_data['t'] = np.ravel(t_grid)
|
|
calc_data['p'] = np.ravel(p_grid)
|
|
calc_data['i'] = 100.0 * calc_data['t'] + calc_data['p']
|
|
|
|
scan_data = md.create_data((10), dtype=md.DTYPE_TPI)
|
|
scan_data['t'] = np.array([0.0, 1.0, 1.0, 2.0, 2.0, 2.0, 3.0, 3.0, 3.0, 3.0])
|
|
scan_data['p'] = np.array([0.0, 0.0, 1.0, 0.5, 1.7, 2.8, 0.0, 90.0, 180.0, 360.0])
|
|
scan_data['i'] = -99.999
|
|
|
|
exp_result = scan_data.copy()
|
|
exp_result['i'] = 100.0 * scan_data['t'] + scan_data['p']
|
|
|
|
act_result = md.interpolate_hemi_scan(calc_data, scan_data)
|
|
|
|
np.testing.assert_allclose(act_result['t'], exp_result['t'])
|
|
np.testing.assert_allclose(act_result['p'], exp_result['p'])
|
|
np.testing.assert_allclose(act_result['i'], exp_result['i'])
|
|
|
|
def test_scaled_rfactor(self):
|
|
n = 20
|
|
calc_modf = md.create_etpi((n,), False)
|
|
calc_modf['e'] = 0.0
|
|
calc_modf['t'] = 0.0
|
|
calc_modf['p'] = np.linspace(0, np.pi, n)
|
|
calc_modf['i'] = np.sin(calc_modf['p'])
|
|
|
|
exp_modf = calc_modf.copy()
|
|
exp_modf['i'] = 0.6 * np.sin(exp_modf['p'] + np.pi / 100.0)
|
|
|
|
weights = np.ones_like(exp_modf['i'])
|
|
|
|
r = md.scaled_rfactor(1.4, exp_modf['i'], weights, calc_modf['i'])
|
|
self.assertGreater(r, 0.0)
|
|
self.assertLess(r, 0.05)
|
|
|
|
def test_rfactor(self):
|
|
n = 20
|
|
calc_modf = md.create_data((n,), dtype=md.DTYPE_ETPI)
|
|
calc_modf['e'] = 0.0
|
|
calc_modf['t'] = 0.0
|
|
calc_modf['p'] = np.linspace(0, np.pi, n)
|
|
calc_modf['i'] = np.sin(calc_modf['p'])
|
|
|
|
exp_modf = md.create_data((n,), dtype=md.DTYPE_ETPIS)
|
|
exp_modf['i'] = 0.6 * np.sin(exp_modf['p'] + np.pi / 100.0)
|
|
exp_modf['s'] = np.sqrt(np.abs(exp_modf['i']))
|
|
|
|
r1 = md.rfactor(exp_modf, calc_modf)
|
|
self.assertAlmostEqual(r1, 0.95, delta=0.02)
|
|
|
|
# one nan should not make a big difference
|
|
calc_modf['i'][3] = np.nan
|
|
r2 = md.rfactor(exp_modf, calc_modf)
|
|
self.assertAlmostEqual(r1, r2, delta=0.02)
|
|
|
|
# all values nan should raise an exception
|
|
with self.assertRaises(ValueError):
|
|
calc_modf['i'] = np.nan
|
|
md.rfactor(exp_modf, calc_modf)
|
|
|
|
def test_optimize_rfactor(self):
|
|
n = 20
|
|
calc_modf = md.create_data((n,), dtype=md.DTYPE_ETPI)
|
|
calc_modf['e'] = 0.0
|
|
calc_modf['t'] = 0.0
|
|
calc_modf['p'] = np.linspace(0, np.pi, n)
|
|
calc_modf['i'] = np.sin(calc_modf['p'])
|
|
|
|
exp_modf = md.create_data((n,), dtype=md.DTYPE_ETPIS)
|
|
exp_modf['i'] = 0.6 * np.sin(exp_modf['p'] + np.pi / 100.0)
|
|
exp_modf['s'] = np.sqrt(np.abs(exp_modf['i']))
|
|
|
|
r1 = md.optimize_rfactor(exp_modf, calc_modf)
|
|
self.assertAlmostEqual(r1, 0.55, delta=0.02)
|
|
|
|
# one nan should not make a big difference
|
|
calc_modf['i'][3] = np.nan
|
|
r2 = md.optimize_rfactor(exp_modf, calc_modf)
|
|
self.assertAlmostEqual(r1, r2, delta=0.02)
|
|
|
|
# all values nan should raise an exception
|
|
with self.assertRaises(ValueError):
|
|
calc_modf['i'] = np.nan
|
|
md.optimize_rfactor(exp_modf, calc_modf)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
unittest.main()
|