From 4bf9324c2c5859572659cbd14783c9a01298971e Mon Sep 17 00:00:00 2001 From: "Adam J. Jackson" Date: Fri, 22 Nov 2024 09:37:07 +0000 Subject: [PATCH] Set up tox-conda/pytest (#1) * Set up tox-conda/pytest - Set build-backend so isolated pip install works - Add pychop as testing dependency - Configure tox-conda - Create __init__.py files under tests/ so that all tests are found by pytest * Update existing tests - Use reservoir sampling to efficiently grab a subset of Fermi chopper frequency combinations - Simplify error matching with match= argument - Get id on the fly with a function rather than precomputing - defer string ids to a function run by test - replace explicit list-extending with itertools.chain - Test only on a subset of the energy-frequency matrix --- .github/workflows/run_tests.yml | 30 ++++ {tests => dev}/plotting/plot_pychop.py | 0 {tests => dev}/test_convert_units.py | 0 pyproject.toml | 5 +- tests/__init__.py | 0 tests/integration_tests/__init__.py | 0 tests/integration_tests/test_lagrange.py | 20 ++- tests/integration_tests/test_pychop.py | 31 ++-- tests/unit_tests/__init__.py | 0 tests/unit_tests/test_pychop.py | 206 +++++++++++++++-------- tox.ini | 24 +++ 11 files changed, 229 insertions(+), 87 deletions(-) create mode 100644 .github/workflows/run_tests.yml rename {tests => dev}/plotting/plot_pychop.py (100%) rename {tests => dev}/test_convert_units.py (100%) create mode 100644 tests/__init__.py create mode 100644 tests/integration_tests/__init__.py create mode 100644 tests/unit_tests/__init__.py create mode 100644 tox.ini diff --git a/.github/workflows/run_tests.yml b/.github/workflows/run_tests.yml new file mode 100644 index 0000000..09add98 --- /dev/null +++ b/.github/workflows/run_tests.yml @@ -0,0 +1,30 @@ +name: Run tests + +on: [push,workflow_dispatch] + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [ubuntu-latest, windows-latest] + + runs-on: ${{ matrix.os }} + + steps: + - uses: actions/checkout@v3 + + - name: Setup Miniconda + uses: conda-incubator/setup-miniconda@v3.0.4 + with: + python-version: '3.10' + channels: conda-forge + miniforge-version: "latest" + + - name: Install tox + shell: bash -el {0} + run: mamba install tox + + - name: Test with tox + shell: bash -el {0} + run: tox diff --git a/tests/plotting/plot_pychop.py b/dev/plotting/plot_pychop.py similarity index 100% rename from tests/plotting/plot_pychop.py rename to dev/plotting/plot_pychop.py diff --git a/tests/test_convert_units.py b/dev/test_convert_units.py similarity index 100% rename from tests/test_convert_units.py rename to dev/test_convert_units.py diff --git a/pyproject.toml b/pyproject.toml index 32caa50..c5615fa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,5 +1,6 @@ [build-system] requires = ["setuptools"] +build-backend = "setuptools.build_meta" [project] name = 'resolutions_functions' @@ -27,7 +28,9 @@ dev = [ ] test = [ "pytest >= 8.3.2", - "mantid >= 6.10.0" + "mantid >= 6.10.0", + "pychop @ git+https://github.com/mducle/pychop.git", + "more_itertools >= 10.5.0", ] [project.urls] diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration_tests/__init__.py b/tests/integration_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/integration_tests/test_lagrange.py b/tests/integration_tests/test_lagrange.py index 6d3f29b..eb6b45a 100644 --- a/tests/integration_tests/test_lagrange.py +++ b/tests/integration_tests/test_lagrange.py @@ -24,13 +24,23 @@ def lagrange_abins_plus_rf_abins_resolution_function(lagrange_rf, request): return abins, rf -@pytest.mark.parametrize('frequencies', - [np.arange(10, 400, 10), np.arange(100, 1000, 50)]) -def test_lagrange_against_abins(frequencies, - lagrange_abins_plus_rf_abins_resolution_function): +@pytest.mark.parametrize( + "frequencies", + [ + pytest.param(np.arange(100, 1000, 50), id="high frequencies: 100:1000:50"), + pytest.param( + np.arange(10, 100, 10), + id="low frequencies: 10:100:10", + marks=pytest.mark.xfail(reason="LAGRANGE low frequencies to be fixed in Abins"), + ), + ], +) +def test_lagrange_against_abins( + frequencies, lagrange_abins_plus_rf_abins_resolution_function +): abins, rf = lagrange_abins_plus_rf_abins_resolution_function actual = rf(frequencies) expected = abins.get_sigma(frequencies * MEV_TO_WAVENUMBER) * WAVENUMBER_TO_MEV - assert_allclose(actual, expected) + assert_allclose(actual, expected, rtol=1e-5) diff --git a/tests/integration_tests/test_pychop.py b/tests/integration_tests/test_pychop.py index 64726c9..862d27b 100644 --- a/tests/integration_tests/test_pychop.py +++ b/tests/integration_tests/test_pychop.py @@ -1,4 +1,5 @@ import itertools +import warnings import numpy as np from numpy.linalg.linalg import LinAlgError @@ -70,18 +71,24 @@ def _test_against_abins(abins, rf_2d, setting, matrix): abins._pychop_instrument.chopper_system.setFrequency(chopper_frequency) abins._pychop_instrument.frequency = chopper_frequency - try: - abins._polyfits = {} - expected = abins.calculate_sigma(frequencies * MEV_TO_WAVENUMBER) * WAVENUMBER_TO_MEV - except LinAlgError: - with pytest.raises(NoTransmissionError): - rf_2d.get_resolution_function('PyChop_fit', chopper_package=setting, e_init=energy, chopper_frequency=chopper_frequency) - else: - rf = rf_2d.get_resolution_function('PyChop_fit', chopper_package=setting, e_init=energy, - chopper_frequency=chopper_frequency) - actual = rf(frequencies) - - assert_allclose(actual, expected, rtol=1e-5) + abins._polyfits = {} + + with warnings.catch_warnings(): + warnings.filterwarnings("error", message="PyChop: tchop\(\): No transmission.*") + try: + expected = abins.calculate_sigma(frequencies * MEV_TO_WAVENUMBER) * WAVENUMBER_TO_MEV + + except Warning: + # Make sure this library agrees it is a no-transmission situation. + with pytest.raises(NoTransmissionError): + rf_2d.get_resolution_function('PyChop_fit', chopper_package=setting, e_init=energy, chopper_frequency=chopper_frequency +) + return + + rf = rf_2d.get_resolution_function('PyChop_fit', chopper_package=setting, e_init=energy, + chopper_frequency=chopper_frequency) + actual = rf(frequencies) + assert_allclose(actual, expected, rtol=1e-5) @pytest.fixture(scope='module', diff --git a/tests/unit_tests/__init__.py b/tests/unit_tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/unit_tests/test_pychop.py b/tests/unit_tests/test_pychop.py index 9add98f..a2e6a8b 100644 --- a/tests/unit_tests/test_pychop.py +++ b/tests/unit_tests/test_pychop.py @@ -1,5 +1,11 @@ +"""Validate PyChop_fit resolutions against reference PyChop library""" + +from __future__ import annotations + import itertools +import random +from more_itertools import sample as reservoir_sample import numpy as np from numpy.testing import assert_allclose import pytest @@ -8,24 +14,44 @@ from PyChop.Chop import tube_mts from PyChop.MulpyRep import calcChopTimes -# import mantid -# from pychop.Instruments import Instrument as PyChopInstrument - from resolution_functions.instrument import Instrument from resolution_functions.models.pychop import * -from resolution_functions.models.model_base import InvalidInputError +from resolution_functions.models.model_base import InvalidInputError, ModelData + +if TYPE_CHECKING: + from jaxtyping import Float + + +random.seed(1) DEBUG = False +N_SAMPLES = 10 EINIT = np.arange(50, 2000, 50) +EINIT_SAMPLE = reservoir_sample(EINIT, k=N_SAMPLES) CHOPPER_FREQ_FERMI = np.arange(50, 601, 50) -MATRIX_FERMI = list(itertools.product(EINIT, CHOPPER_FREQ_FERMI)) -MATRIX_IDS_FERMI = [f'e_init={ei},f={f}' for ei, f in MATRIX_FERMI] +MATRIX_FERMI = list(reservoir_sample( + itertools.product(EINIT, CHOPPER_FREQ_FERMI), + k=N_SAMPLES) +) CHOPPER_FREQ_NONFERMI = np.arange(60, 301, 60) -MATRIX_NONFERMI = list(itertools.product(EINIT, CHOPPER_FREQ_NONFERMI, CHOPPER_FREQ_NONFERMI)) -MATRIX_IDS_NONFERMI = [f'e_init={ei},f1={f1},f2={f2}' for ei, f1, f2 in MATRIX_NONFERMI] +MATRIX_NONFERMI = list(reservoir_sample( + itertools.product(EINIT, CHOPPER_FREQ_NONFERMI, CHOPPER_FREQ_NONFERMI), + k=N_SAMPLES) +) + +def matrix_fermi_id(matrix_row: tuple[int, int]) -> str: + """Get test id string from Fermi chopper frequencies""" + e_init, frequency = matrix_row + return f"e_init={e_init},f={frequency}" + + +def matrix_nonfermi_id(matrix_row: tuple[int, int, int]) -> str: + """Get test id string from Non-Fermi chopper frequencies""" + e_init, f1, f2 = matrix_row + return f"e_init={e_init},f1={f1},f2={f2}" INSTRUMENTS_FERMI = [ @@ -36,6 +62,7 @@ [('MERLIN', 'MERLIN')], [('SEQUOIA', 'SEQUOIA')], ] + INSTRUMENT_SETTINGS_FERMI = [ ['SEQ-100-2.0-AST', 'SEQ-700-3.5-AST', 'ARCS-100-1.5-AST', 'ARCS-700-1.5-AST', 'ARCS-700-0.5-AST', 'ARCS-100-1.5-SMI', 'ARCS-700-1.5-SMI'], @@ -47,33 +74,40 @@ 'ARCS-700-0.5-AST', 'ARCS-100-1.5-SMI', 'ARCS-700-1.5-SMI'], ] -INSTRUMENT_MATRIX_FERMI, INSTRUMENT_IDS_FERMI = [], [] -for instr, settings in zip(INSTRUMENTS_FERMI, INSTRUMENT_SETTINGS_FERMI): - lst = list(itertools.product(instr, settings)) - INSTRUMENT_MATRIX_FERMI.extend(lst) - INSTRUMENT_IDS_FERMI.extend([f'{i[0]}_{s}' for i, s in lst]) +INSTRUMENT_MATRIX_FERMI = list( + itertools.chain.from_iterable( + itertools.product(instr, settings) + for instr, settings in zip(INSTRUMENTS_FERMI, INSTRUMENT_SETTINGS_FERMI) + ) +) +INSTRUMENTS_NONFERMI = [[('CNCS', 'CNCS')]] + +INSTRUMENT_SETTINGS_NONFERMI = [['High Flux', 'Intermediate', 'High Resolution']] + +INSTRUMENT_MATRIX_NONFERMI = list( + itertools.chain.from_iterable( + itertools.product(instr, settings) + for instr, settings in zip(INSTRUMENTS_NONFERMI, INSTRUMENT_SETTINGS_NONFERMI) + ) +) -INSTRUMENTS_NONFERMI = [ - [('CNCS', 'CNCS')] -] -INSTRUMENT_SETTINGS_NONFERMI = [ - ['High Flux', 'Intermediate', 'High Resolution'] -] -INSTRUMENT_MATRIX_NONFERMI, INSTRUMENT_IDS_NONFERMI = [], [] -for instr, settings in zip(INSTRUMENTS_NONFERMI, INSTRUMENT_SETTINGS_NONFERMI): - lst = list(itertools.product(instr, settings)) - INSTRUMENT_MATRIX_NONFERMI.extend(lst) - INSTRUMENT_IDS_NONFERMI.extend([f'{i[0]}_{s}' for i, s in lst]) +def instrument_id(matrix_row: tuple[tuple[str, str], str]) -> str: + """Get test id NAME_SETTING from input row ((NAME, NAME), SETTING)""" + (instrument, _), setting = matrix_row + return f"{instrument}_{setting}" +# id formatter for E_i input +format_ei = "ei={}".format -def get_fake_frequencies(e_init: float): + +def get_fake_frequencies(e_init: float) -> Float[np.ndarray]: return np.linspace(0, e_init, 40, endpoint=False) -@pytest.fixture(scope="module", params=INSTRUMENT_MATRIX_FERMI, ids=INSTRUMENT_IDS_FERMI) -def pychop_fermi_data(request): +@pytest.fixture(scope="module", params=INSTRUMENT_MATRIX_FERMI, ids=instrument_id) +def pychop_fermi_data(request) -> tuple[ModelData, PyChopInstrument]: (name, version), setting = request.param maps = Instrument.from_default(name, version) rf = maps.get_model_data('PyChop_fit', chopper_package=setting) @@ -82,8 +116,8 @@ def pychop_fermi_data(request): return rf, pc -@pytest.fixture(scope="module", params=INSTRUMENT_MATRIX_NONFERMI, ids=INSTRUMENT_IDS_NONFERMI) -def pychop_nonfermi_data(request): +@pytest.fixture(scope="module", params=INSTRUMENT_MATRIX_NONFERMI, ids=instrument_id) +def pychop_nonfermi_data(request) -> tuple[ModelData, PyChopInstrument]: (name, version), setting = request.param maps = Instrument.from_default(name, version) rf = maps.get_model_data('PyChop_fit', chopper_package=setting) @@ -108,40 +142,67 @@ def cncs_data(): return rf -@pytest.mark.parametrize('chopper_frequency', - [49.99999, -0.048, -np.inf, 600.00017, np.inf, 13554, np.nan, 50.5, 57.5, 500.0000001, 480]) -def test_fermi_invalid_chopper_frequency(chopper_frequency, mari_data: tuple[PyChopModelDataFermi, PyChopInstrument]): - with pytest.raises(InvalidInputError) as e: +@pytest.mark.parametrize( + "chopper_frequency", + [ + 49.99999, + -0.048, + -np.inf, + 600.00017, + np.inf, + 13554, + np.nan, + 50.5, + 57.5, + 500.0000001, + 480, + ], +) +def test_fermi_invalid_chopper_frequency( + chopper_frequency, mari_data: tuple[PyChopModelDataFermi, PyChopInstrument] +): + with pytest.raises(InvalidInputError, match="The provided chopper frequency"): PyChopModelFermi(mari_data[0], chopper_frequency=chopper_frequency) - assert 'The provided chopper frequency' in str(e.value) - -@pytest.mark.parametrize('e_init', [-5, -0.00048, -np.inf, 2000.1, np.inf, 13554.1654, np.nan]) -def test_fermi_invalid_e_init(e_init, mari_data: tuple[PyChopModelDataFermi, PyChopInstrument]): - with pytest.raises(InvalidInputError) as e: +@pytest.mark.parametrize( + "e_init", [-5, -0.00048, -np.inf, 2000.1, np.inf, 13554.1654, np.nan] +) +def test_fermi_invalid_e_init( + e_init, mari_data: tuple[PyChopModelDataFermi, PyChopInstrument] +): + with pytest.raises(InvalidInputError, match="The provided incident energy"): PyChopModelFermi(mari_data[0], e_init=e_init) - assert 'The provided incident energy' in str(e.value) - - -@pytest.mark.parametrize('chopper_frequency', - [[59.99999, 60], [-0.048] * 2, [-np.inf, 0], [120, 300.00017], [np.inf, np.inf], - [300, np.nan], [60.5, 60], [180, 67.5], [600, 600], [130, 130]]) -def test_nonfermi_invalid_chopper_frequency(chopper_frequency, cncs_data: PyChopModelDataNonFermi): - with pytest.raises(InvalidInputError) as e: +@pytest.mark.parametrize( + "chopper_frequency", + [ + [59.99999, 60], + [-0.048] * 2, + [-np.inf, 0], + [120, 300.00017], + [np.inf, np.inf], + [300, np.nan], + [60.5, 60], + [180, 67.5], + [600, 600], + [130, 130], + ], +) +def test_nonfermi_invalid_chopper_frequency( + chopper_frequency, cncs_data: PyChopModelDataNonFermi +): + with pytest.raises(InvalidInputError, match="The provided chopper frequency"): PyChopModelNonFermi(cncs_data, chopper_frequency=chopper_frequency) - assert 'The provided chopper frequency' in str(e.value) - -@pytest.mark.parametrize('e_init', [-5, -0.00048, -np.inf, 2000.1, np.inf, 13554.1654, np.nan]) +@pytest.mark.parametrize( + "e_init", [-5, -0.00048, -np.inf, 2000.1, np.inf, 13554.1654, np.nan] +) def test_nonfermi_invalid_e_init(e_init, cncs_data: PyChopModelDataNonFermi): - with pytest.raises(InvalidInputError) as e: + with pytest.raises(InvalidInputError, match="The provided incident energy"): PyChopModelNonFermi(cncs_data, e_init=e_init) - assert 'The provided incident energy' in str(e.value) - def test_distances(mari_data: tuple[PyChopModelData, PyChopInstrument]): maps_data, pychop = mari_data @@ -153,12 +214,12 @@ def test_distances(mari_data: tuple[PyChopModelData, PyChopInstrument]): assert xm == expected[-1] -@pytest.mark.parametrize('e_init', EINIT) +@pytest.mark.parametrize('e_init', EINIT_SAMPLE) def test_fermi_moderator_width_analytical(e_init, pychop_fermi_data): _test_moderator_width_analytical(e_init, *pychop_fermi_data, PyChopModelFermi) -@pytest.mark.parametrize('e_init', EINIT) +@pytest.mark.parametrize('e_init', EINIT_SAMPLE) def test_nonfermi_moderator_width_analytical(e_init, pychop_nonfermi_data): _test_moderator_width_analytical(e_init, *pychop_nonfermi_data, PyChopModelNonFermi) @@ -172,12 +233,12 @@ def _test_moderator_width_analytical(e_init, data, pychop, cls): assert_allclose(actual, expected, rtol=0, atol=1e-8) -@pytest.mark.parametrize('e_init', EINIT, ids=[f'ei={ei}' for ei in EINIT]) +@pytest.mark.parametrize('e_init', EINIT_SAMPLE, ids=format_ei) def test_fermi_moderator_width(e_init, pychop_fermi_data): _test_moderator_width(e_init, PyChopModelFermi, *pychop_fermi_data) -@pytest.mark.parametrize('e_init', EINIT, ids=[f'ei={ei}' for ei in EINIT]) +@pytest.mark.parametrize('e_init', EINIT_SAMPLE, ids=format_ei) def test_nonfermi_moderator_width(e_init, pychop_fermi_data): _test_moderator_width(e_init, PyChopModelNonFermi, *pychop_fermi_data) @@ -189,7 +250,7 @@ def _test_moderator_width(e_init, cls, data, pychop): assert_allclose(actual, expected, rtol=0, atol=1e-8) -@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=MATRIX_IDS_FERMI) +@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=matrix_fermi_id) def test_fermi_chopper_width(matrix, pychop_fermi_data): e_init, chopper_frequency = matrix pychop_fermi_data, pychop = pychop_fermi_data @@ -212,7 +273,7 @@ def test_fermi_chopper_width(matrix, pychop_fermi_data): assert actual[1] is None -@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=MATRIX_IDS_NONFERMI) +@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=matrix_nonfermi_id) def test_nonfermi_chopper_width(matrix, pychop_nonfermi_data): e_init, *chopper_frequencies = matrix data, pychop = pychop_nonfermi_data @@ -226,7 +287,7 @@ def test_nonfermi_chopper_width(matrix, pychop_nonfermi_data): assert_allclose(actual[1], expected[1], rtol=0, atol=1e-8) -@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=MATRIX_IDS_NONFERMI) +@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=matrix_nonfermi_id) def test_long_frequency(matrix, pychop_nonfermi_data): e_init, *chopper_frequencies = matrix data, pychop = pychop_nonfermi_data @@ -239,7 +300,7 @@ def test_long_frequency(matrix, pychop_nonfermi_data): assert_allclose(actual, expected) -@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=MATRIX_IDS_NONFERMI) +@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=matrix_nonfermi_id) def test_chop_times(matrix, pychop_nonfermi_data): e_init, *chopper_frequencies = matrix data, pychop = pychop_nonfermi_data @@ -277,12 +338,12 @@ def test_he_detector_width_squared(): assert_allclose(actual, expected) -@pytest.mark.parametrize('e_init', EINIT, ids=[f'ei={ei}' for ei in EINIT]) +@pytest.mark.parametrize('e_init', EINIT, ids=format_ei) def test_fermi_detector_width_squared(e_init, pychop_fermi_data): _test_get_detector_width_squared(e_init, PyChopModelFermi, *pychop_fermi_data) -@pytest.mark.parametrize('e_init', EINIT, ids=[f'ei={ei}' for ei in EINIT]) +@pytest.mark.parametrize('e_init', EINIT, ids=format_ei) def test_nonfermi_detector_width_squared(e_init, pychop_nonfermi_data): _test_get_detector_width_squared(e_init, PyChopModelNonFermi, *pychop_nonfermi_data) @@ -315,14 +376,14 @@ def test_nonfermi_sample_width_squared(pychop_nonfermi_data): @pytest.mark.skipif(DEBUG, reason='Debugging precompute_van_var; its outputs have been temporarily changed.') -@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=MATRIX_IDS_FERMI) +@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=matrix_fermi_id) def test_fermi_precompute_van_var(matrix, pychop_fermi_data): e_init, chopper_frequency = matrix _test_precompute_van_var(e_init, [chopper_frequency], PyChopModelFermi, *pychop_fermi_data) @pytest.mark.skipif(DEBUG, reason='Debugging precompute_van_var; its outputs have been temporarily changed.') -@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=MATRIX_IDS_NONFERMI) +@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=matrix_nonfermi_id) def test_nonfermi_precompute_van_var(matrix, pychop_nonfermi_data): e_init, *chopper_frequency = matrix _test_precompute_van_var(e_init, chopper_frequency, PyChopModelNonFermi, *pychop_nonfermi_data) @@ -349,14 +410,14 @@ def _test_precompute_van_var(e_init, chopper_frequency, cls, data, pychop): @pytest.mark.skipif(not DEBUG, reason='Not debugging; normal version of the function only returns vsq_van') -@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=MATRIX_IDS_FERMI) +@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=matrix_fermi_id) def test_debug_fermi_precompute_van_var(matrix, pychop_fermi_data): e_init, chopper_frequency = matrix _test_debug_precompute_van_var(e_init, [chopper_frequency], PyChopModelFermi, *pychop_fermi_data) @pytest.mark.skipif(not DEBUG, reason='Not debugging; normal version of the function only returns vsq_van') -@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=MATRIX_IDS_NONFERMI) +@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=matrix_nonfermi_id) def test_debug_nonfermi_precompute_van_var(matrix, pychop_nonfermi_data): e_init, *chopper_frequency = matrix _test_debug_precompute_van_var(e_init, chopper_frequency, PyChopModelNonFermi, *pychop_nonfermi_data) @@ -390,14 +451,14 @@ def _test_debug_precompute_van_var(e_init, chopper_frequency, cls, data, pychop) @pytest.mark.skipif(DEBUG, reason='Debugging precompute_van_var; its outputs have been temporarily changed.') -@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=MATRIX_IDS_FERMI) +@pytest.mark.parametrize('matrix', MATRIX_FERMI, ids=matrix_fermi_id) def test_fermi_precompute_resolution(matrix, pychop_fermi_data): e_init, chopper_frequency = matrix _test_precompute_resolution(e_init, [chopper_frequency], PyChopModelFermi, *pychop_fermi_data) @pytest.mark.skipif(DEBUG, reason='Debugging precompute_van_var; its outputs have been temporarily changed.') -@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=MATRIX_IDS_NONFERMI) +@pytest.mark.parametrize('matrix', MATRIX_NONFERMI, ids=matrix_nonfermi_id) def test_nonfermi_precompute_resolution(matrix, pychop_nonfermi_data): e_init, *chopper_frequency = matrix _test_precompute_resolution(e_init, chopper_frequency, PyChopModelNonFermi, *pychop_nonfermi_data) @@ -407,8 +468,15 @@ def _test_precompute_resolution(e_init, chopper_frequency, cls, data, pychop): try: pychop.chopper_system.setFrequency(chopper_frequency) except ValueError as e: - if 'maximum allowed' in str(e): + if 'Value of frequencies outside maximum allowed' in str(e): + # Energy out of range for Pychop: make sure the model agrees + with pytest.raises( + InvalidInputError, + match=rf"The provided chopper frequency \(\[{chopper_frequency[0]}\]\) is not allowed"): + cls(model_data=data, chopper_frequency=chopper_frequency, e_init=e_init) + return + raise e fake_frequencies = np.linspace(0, e_init, 40, endpoint=False) expected_resolution = pychop.getResolution(Ei_in=e_init, Etrans=fake_frequencies) diff --git a/tox.ini b/tox.ini new file mode 100644 index 0000000..2c5ac83 --- /dev/null +++ b/tox.ini @@ -0,0 +1,24 @@ +[tox] +requires = + tox-conda +envlist = py310 +isolated_build = True + +[testenv] +base_python = python3.10 + +deps = + git+https://github.com/mducle/pychop.git + more_itertools + +conda_deps = + mantid + pytest>=8.3.2 + pytest-xdist + +conda_channels = + conda-forge + mantid + +commands = + pytest -n auto \ No newline at end of file