From 6e20be3383daaf8b4b3f6aba0a86da61883a980b Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Sun, 12 Apr 2026 07:25:39 +0200 Subject: [PATCH 01/12] Enable RUF linting --- pixi.lock | 4 ++-- pyproject.toml | 6 +++--- .../convolution/analytical_convolution.py | 4 +++- src/easydynamics/convolution/convolution.py | 4 +++- .../convolution/convolution_base.py | 2 +- .../convolution/numerical_convolution_base.py | 4 ++-- src/easydynamics/sample_model/__init__.py | 18 +++++++++--------- .../sample_model/component_collection.py | 2 +- .../sample_model/components/__init__.py | 10 +++++----- .../components/expression_component.py | 7 ++++--- .../sample_model/components/model_component.py | 4 ++-- .../diffusion_model/diffusion_model_base.py | 2 +- .../sample_model/instrument_model.py | 2 +- src/easydynamics/sample_model/model_base.py | 2 +- src/easydynamics/sample_model/sample_model.py | 2 +- 15 files changed, 39 insertions(+), 34 deletions(-) diff --git a/pixi.lock b/pixi.lock index efafa5aa..143c83a7 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+dev5 - sha256: ff8f55922804cdb622d0eb0aecd00105aeb30e470aa79b05bf6f92556ad8ce67 + version: 0.4.0+devdirty5 + sha256: abccb565cab4643229a25f1fc3cb629447ababf30fa1bf2e0a6c7f140094f1ea requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index 17452cd7..46604757 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -205,7 +205,7 @@ select = [ #'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy #'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh #'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf - #'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try 'UP', # https://docs.astral.sh/ruff/rules/#pyupgrade-up # pycodestyle (E, W) rules @@ -256,8 +256,7 @@ select = [ # Ignore specific rules globally ignore = [ 'COM812', # https://docs.astral.sh/ruff/rules/missing-trailing-comma/ - # The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] - 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc + # The following is replaced by 'D'/[tool.ruff.lint.pydocstyle] and [tool.pydoclint] 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc # Disable, as [tool.format_docstring] split one-line docstrings into the canonical multi-line layout 'D200', # https://docs.astral.sh/ruff/rules/unnecessary-multiline-docstring/ ] @@ -273,6 +272,7 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ + 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf ] 'docs/**' = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ diff --git a/src/easydynamics/convolution/analytical_convolution.py b/src/easydynamics/convolution/analytical_convolution.py index a835f215..2fec57eb 100644 --- a/src/easydynamics/convolution/analytical_convolution.py +++ b/src/easydynamics/convolution/analytical_convolution.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause +from typing import ClassVar + import numpy as np import scipp as sc from easyscience.variable import Parameter @@ -26,7 +28,7 @@ class AnalyticalConvolution(ConvolutionBase): # Mapping of supported component type pairs to convolution methods. # Delta functions are handled separately. - _CONVOLUTIONS = { + _CONVOLUTIONS: ClassVar[dict[str, object]] = { ('Gaussian', 'Gaussian'): '_convolute_gaussian_gaussian', ('Gaussian', 'Lorentzian'): '_convolute_gaussian_lorentzian', ('Gaussian', 'Voigt'): '_convolute_gaussian_voigt', diff --git a/src/easydynamics/convolution/convolution.py b/src/easydynamics/convolution/convolution.py index 32d3fb96..51d3bfa1 100644 --- a/src/easydynamics/convolution/convolution.py +++ b/src/easydynamics/convolution/convolution.py @@ -1,6 +1,8 @@ # SPDX-FileCopyrightText: 2026 EasyScience contributors # SPDX-License-Identifier: BSD-3-Clause +from typing import ClassVar + import numpy as np import scipp as sc from easyscience.variable import Parameter @@ -33,7 +35,7 @@ class Convolution(NumericalConvolutionBase): # When these attributes are changed, the convolution plan # needs to be rebuilt - _invalidate_plan_on_change = { + _invalidate_plan_on_change: ClassVar[dict[str, object]] = { 'energy', '_energy', '_energy_grid', diff --git a/src/easydynamics/convolution/convolution_base.py b/src/easydynamics/convolution/convolution_base.py index d328dbed..aa36c24d 100644 --- a/src/easydynamics/convolution/convolution_base.py +++ b/src/easydynamics/convolution/convolution_base.py @@ -222,7 +222,7 @@ def energy_unit(self, _unit_str: str) -> None: raise AttributeError( f'Unit is read-only. Use convert_unit to change the unit between allowed types ' f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) def convert_energy_unit(self, energy_unit: str | sc.Unit) -> None: """ diff --git a/src/easydynamics/convolution/numerical_convolution_base.py b/src/easydynamics/convolution/numerical_convolution_base.py index e2d6f5b6..c5db7326 100644 --- a/src/easydynamics/convolution/numerical_convolution_base.py +++ b/src/easydynamics/convolution/numerical_convolution_base.py @@ -432,8 +432,8 @@ def __repr__(self) -> str: return ( f'{self.__class__.__name__}(' f'energy=array of shape {self.energy.values.shape},\n ' - f'sample_components={repr(self.sample_components)}, \n' - f'resolution_components={repr(self.resolution_components)},\n ' + f'sample_components={self.sample_components!r}, \n' + f'resolution_components={self.resolution_components!r},\n ' f'energy_unit={self._energy_unit}, ' f'upsample_factor={self.upsample_factor}, ' f'extension_factor={self.extension_factor}, ' diff --git a/src/easydynamics/sample_model/__init__.py b/src/easydynamics/sample_model/__init__.py index f52c44bd..6f75bafd 100644 --- a/src/easydynamics/sample_model/__init__.py +++ b/src/easydynamics/sample_model/__init__.py @@ -21,18 +21,18 @@ from easydynamics.sample_model.sample_model import SampleModel __all__ = [ + 'BackgroundModel', + 'BrownianTranslationalDiffusion', 'ComponentCollection', + 'DampedHarmonicOscillator', + 'DeltaFunction', + 'Exponential', + 'ExpressionComponent', 'Gaussian', + 'InstrumentModel', 'Lorentzian', - 'Voigt', - 'DeltaFunction', - 'DampedHarmonicOscillator', 'Polynomial', - 'Exponential', - 'BrownianTranslationalDiffusion', - 'SampleModel', 'ResolutionModel', - 'BackgroundModel', - 'InstrumentModel', - 'ExpressionComponent', + 'SampleModel', + 'Voigt', ] diff --git a/src/easydynamics/sample_model/component_collection.py b/src/easydynamics/sample_model/component_collection.py index fd5b5ebd..f5f1a45a 100644 --- a/src/easydynamics/sample_model/component_collection.py +++ b/src/easydynamics/sample_model/component_collection.py @@ -173,7 +173,7 @@ def unit(self, _unit_str: str) -> None: raise AttributeError( f'Unit is read-only. Use convert_unit to change the unit between allowed types ' f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) def convert_unit(self, unit: str | sc.Unit) -> None: """ diff --git a/src/easydynamics/sample_model/components/__init__.py b/src/easydynamics/sample_model/components/__init__.py index 747dc594..940f41a7 100644 --- a/src/easydynamics/sample_model/components/__init__.py +++ b/src/easydynamics/sample_model/components/__init__.py @@ -13,12 +13,12 @@ from easydynamics.sample_model.components.voigt import Voigt __all__ = [ - 'Gaussian', - 'Lorentzian', - 'Voigt', - 'DeltaFunction', 'DampedHarmonicOscillator', - 'Polynomial', + 'DeltaFunction', 'Exponential', 'ExpressionComponent', + 'Gaussian', + 'Lorentzian', + 'Polynomial', + 'Voigt', ] diff --git a/src/easydynamics/sample_model/components/expression_component.py b/src/easydynamics/sample_model/components/expression_component.py index d87c0e07..79ce73ef 100644 --- a/src/easydynamics/sample_model/components/expression_component.py +++ b/src/easydynamics/sample_model/components/expression_component.py @@ -4,6 +4,7 @@ from __future__ import annotations from typing import TYPE_CHECKING +from typing import ClassVar import sympy as sp from easyscience.variable import Parameter @@ -29,7 +30,7 @@ class ExpressionComponent(ModelComponent): # ------------------------- # Allowed symbolic functions # ------------------------- - _ALLOWED_FUNCS = { + _ALLOWED_FUNCS: ClassVar[dict[str, object]] = { # Exponentials & logs 'exp': sp.exp, 'log': sp.log, @@ -62,12 +63,12 @@ class ExpressionComponent(ModelComponent): # ------------------------- # Allowed constants # ------------------------- - _ALLOWED_CONSTANTS = { + _ALLOWED_CONSTANTS: ClassVar[dict[str, object]] = { 'pi': sp.pi, 'E': sp.E, } - _RESERVED_NAMES = {'x'} + _RESERVED_NAMES: ClassVar[dict[str, object]] = {'x'} def __init__( self, diff --git a/src/easydynamics/sample_model/components/model_component.py b/src/easydynamics/sample_model/components/model_component.py index f4d0c15e..4322c5da 100644 --- a/src/easydynamics/sample_model/components/model_component.py +++ b/src/easydynamics/sample_model/components/model_component.py @@ -72,7 +72,7 @@ def unit(self, _unit_str: str) -> None: raise AttributeError( f'Unit is read-only. Use convert_unit to change the unit between allowed types ' f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) def fix_all_parameters(self) -> None: """Fix all parameters in the model component.""" @@ -123,7 +123,7 @@ def _prepare_x_for_evaluate( f'Found {ncoords} coordinates: {coord_names}.' ) # get the coordinate, it's a sc.Variable - coord_name, coord_obj = next(iter(coords.items())) + _, coord_obj = next(iter(coords.items())) x = coord_obj if isinstance(x, sc.Variable): # Need to check if the units are consistent, diff --git a/src/easydynamics/sample_model/diffusion_model/diffusion_model_base.py b/src/easydynamics/sample_model/diffusion_model/diffusion_model_base.py index 177d5e79..8525546b 100644 --- a/src/easydynamics/sample_model/diffusion_model/diffusion_model_base.py +++ b/src/easydynamics/sample_model/diffusion_model/diffusion_model_base.py @@ -96,7 +96,7 @@ def unit(self, _unit_str: str) -> None: raise AttributeError( f'Unit is read-only. Use convert_unit to change the unit between allowed types ' f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) @property def scale(self) -> Parameter: diff --git a/src/easydynamics/sample_model/instrument_model.py b/src/easydynamics/sample_model/instrument_model.py index 00cf2544..6468cd3b 100644 --- a/src/easydynamics/sample_model/instrument_model.py +++ b/src/easydynamics/sample_model/instrument_model.py @@ -259,7 +259,7 @@ def unit(self, _unit_str: str) -> None: raise AttributeError( f'Unit is read-only. Use convert_unit to change the unit between allowed types ' f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) @property def energy_offset(self) -> Parameter: diff --git a/src/easydynamics/sample_model/model_base.py b/src/easydynamics/sample_model/model_base.py index 1b5d9363..7c5711b9 100644 --- a/src/easydynamics/sample_model/model_base.py +++ b/src/easydynamics/sample_model/model_base.py @@ -173,7 +173,7 @@ def unit(self, _unit_str: str) -> None: raise AttributeError( f'Unit is read-only. Use convert_unit to change the unit between allowed types ' f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) def convert_unit(self, unit: str | sc.Unit) -> None: """ diff --git a/src/easydynamics/sample_model/sample_model.py b/src/easydynamics/sample_model/sample_model.py index e1ec6b05..c7ae1633 100644 --- a/src/easydynamics/sample_model/sample_model.py +++ b/src/easydynamics/sample_model/sample_model.py @@ -302,7 +302,7 @@ def temperature_unit(self, _value: str | sc.Unit) -> None: raise AttributeError( f'Temperature_unit is read-only. Use convert_temperature_unit to change the unit between allowed types ' # noqa: E501 f'or create a new {self.__class__.__name__} with the desired unit.' - ) # noqa: E501 + ) def convert_temperature_unit(self, unit: str | sc.Unit) -> None: """ From 993527e0bb1cd0f698be9d0e42b0ec3ae74078a6 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Sun, 12 Apr 2026 20:19:49 +0200 Subject: [PATCH 02/12] Enable RUF for tests --- pixi.lock | 4 +-- pyproject.toml | 2 +- .../easydynamics/analysis/test_analysis.py | 6 ++-- .../easydynamics/analysis/test_analysis1d.py | 8 ++--- .../analysis/test_analysis_base.py | 4 +-- .../test_analytical_convolution.py | 4 +-- .../convolution/test_convolution.py | 20 ++++++++---- .../convolution/test_convolution_base.py | 20 ++++++------ .../convolution/test_numerical_convolution.py | 2 +- .../test_numerical_convolution_base.py | 8 ++--- .../experiment/test_experiment.py | 6 ++-- .../test_damped_harmonic_oscillator.py | 20 ++++++------ .../components/test_delta_function.py | 14 ++++---- .../components/test_exponential.py | 30 ++++++++--------- .../components/test_expression_component.py | 14 ++++---- .../sample_model/components/test_gaussian.py | 24 +++++++------- .../components/test_lorentzian.py | 22 ++++++------- .../sample_model/components/test_mixins.py | 12 +++---- .../components/test_model_component.py | 20 ++++++------ .../components/test_polynomial.py | 10 +++--- .../sample_model/components/test_voigt.py | 32 +++++++++---------- .../test_brownian_translational_diffusion.py | 14 ++++---- .../diffusion_model/test_diffusion_model.py | 8 ++--- .../test_jump_translational_diffusion.py | 22 ++++++------- .../sample_model/test_component_collection.py | 22 ++++++------- .../sample_model/test_instrument_model.py | 4 +-- .../sample_model/test_model_base.py | 2 +- .../sample_model/test_sample_model.py | 12 +++---- .../utils/test_detailed_balance.py | 8 ++--- 29 files changed, 191 insertions(+), 183 deletions(-) diff --git a/pixi.lock b/pixi.lock index 143c83a7..acba6db3 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty5 - sha256: abccb565cab4643229a25f1fc3cb629447ababf30fa1bf2e0a6c7f140094f1ea + version: 0.4.0+devdirty6 + sha256: 3fe9c7ab629ba0adcbfc5c57698ae2c6f86a62dfae4f6d2c357bee3f83aa8931 requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index 46604757..3eed6bcf 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -272,7 +272,7 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ - 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + # 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf ] 'docs/**' = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ diff --git a/tests/unit/easydynamics/analysis/test_analysis.py b/tests/unit/easydynamics/analysis/test_analysis.py index 25274a55..67e93aab 100644 --- a/tests/unit/easydynamics/analysis/test_analysis.py +++ b/tests/unit/easydynamics/analysis/test_analysis.py @@ -177,7 +177,7 @@ def test_fit_with_invalid_fit_method(self, analysis): # WHEN / THEN / EXPECT with pytest.raises( ValueError, - match="Invalid fit method. Choose 'independent' or 'simultaneous'.", + match=r"Invalid fit method. Choose 'independent' or 'simultaneous'.", ): analysis.fit(fit_method='invalid_fit_method') @@ -646,7 +646,7 @@ def test_fit_all_Q_simultaneously(self, analysis): expected_fit_objects = analysis.analysis_list expected_fit_functions = analysis.get_fit_functions() mock_fitter.assert_called_once() - args, kwargs = mock_fitter.call_args + _, kwargs = mock_fitter.call_args assert kwargs['fit_objects'] == expected_fit_objects assert kwargs['fit_functions'] == expected_fit_functions @@ -663,7 +663,7 @@ def test_fit_all_Q_simultaneously(self, analysis): expected_ws.append(1.0 / np.sqrt(data.variances)) fake_fitter_instance.fit.assert_called_once() - args, kwargs = fake_fitter_instance.fit.call_args + _, kwargs = fake_fitter_instance.fit.call_args np.testing.assert_array_equal(kwargs['x'], expected_xs) np.testing.assert_array_equal(kwargs['y'], expected_ys) np.testing.assert_array_equal(kwargs['weights'], expected_ws) diff --git a/tests/unit/easydynamics/analysis/test_analysis1d.py b/tests/unit/easydynamics/analysis/test_analysis1d.py index 43763011..dbd0ae51 100644 --- a/tests/unit/easydynamics/analysis/test_analysis1d.py +++ b/tests/unit/easydynamics/analysis/test_analysis1d.py @@ -262,7 +262,7 @@ def test_plot_calls_plopp_with_correct_arguments(self, analysis1d): mock_plot.assert_called_once() # Inspect arguments - args, kwargs = mock_plot.call_args + args, _ = mock_plot.call_args dataset_passed = args[0] @@ -344,7 +344,7 @@ def test_verify_energy_raises(self, analysis1d): energy = np.array([10.0, 20.0]) # THEN / EXPECT - with pytest.raises(TypeError, match='Energy must be a sc.Variable or None'): + with pytest.raises(TypeError, match=r'Energy must be a sc.Variable or None'): analysis1d._verify_energy(energy) def test_calculate_energy_with_offset(self, analysis1d): @@ -399,7 +399,7 @@ def test_evaluate_components_no_components(self, analysis1d): # EXPECT assert isinstance(result, np.ndarray) assert result.shape == (len(analysis1d.experiment.energy),) - assert np.all(result == 0.0) + assert np.all(result == pytest.approx(0.0)) def test_evaluate_components_no_convolution(self, analysis1d): # WHEN @@ -608,7 +608,7 @@ def test_create_convolver(self, analysis1d): assert kwargs['resolution_components'] is resolution_components assert sc.identical(kwargs['energy'], analysis1d.energy) assert kwargs['temperature'] is analysis1d.temperature - assert kwargs['energy_offset'] == 123.0 + assert kwargs['energy_offset'] == pytest.approx(123.0) assert result == MockConvolution.return_value diff --git a/tests/unit/easydynamics/analysis/test_analysis_base.py b/tests/unit/easydynamics/analysis/test_analysis_base.py index 4b47c59e..59b0ff90 100644 --- a/tests/unit/easydynamics/analysis/test_analysis_base.py +++ b/tests/unit/easydynamics/analysis/test_analysis_base.py @@ -163,7 +163,7 @@ def test_Q_property(self, analysis_base): def test_Q_setter_raises(self, analysis_base): with pytest.raises( AttributeError, - match='Q is a read-only property derived from the Experiment.', + match=r'Q is a read-only property derived from the Experiment.', ): analysis_base.Q = [1, 2, 3] @@ -183,7 +183,7 @@ def test_energy_property(self, analysis_base): def test_energy_setter_raises(self, analysis_base): with pytest.raises( AttributeError, - match='energy is a read-only property derived from the Experiment.', + match=r'energy is a read-only property derived from the Experiment.', ): analysis_base.energy = [10, 20, 30] diff --git a/tests/unit/easydynamics/convolution/test_analytical_convolution.py b/tests/unit/easydynamics/convolution/test_analytical_convolution.py index 162a920b..c9d3f46e 100644 --- a/tests/unit/easydynamics/convolution/test_analytical_convolution.py +++ b/tests/unit/easydynamics/convolution/test_analytical_convolution.py @@ -126,7 +126,7 @@ def mock_convolute_analytic_pair(sample_component, resolution_component): # noq # EXPECT # 2 sample components x 2 resolution components - assert np.all(result == 4.0) + assert np.all(result == pytest.approx(4.0)) assert mocked_pair.call_count == 4 # Gather the actual calls to verify correct pairs @@ -178,7 +178,7 @@ def mock_convolute_analytic_pair(sample_component, resolution_component): # noq # EXPECT # 1 sample component x 1 resolution component - assert np.all(result == 1.0) + assert np.all(result == pytest.approx(1.0)) assert mocked_pair.call_count == 1 # Gather the actual calls to verify correct pairs diff --git a/tests/unit/easydynamics/convolution/test_convolution.py b/tests/unit/easydynamics/convolution/test_convolution.py index 468df680..26a95037 100644 --- a/tests/unit/easydynamics/convolution/test_convolution.py +++ b/tests/unit/easydynamics/convolution/test_convolution.py @@ -2,6 +2,7 @@ # SPDX-License-Identifier: BSD-3-Clause from contextlib import nullcontext +from typing import ClassVar from unittest.mock import patch import numpy as np @@ -74,7 +75,7 @@ def test_init(self, default_convolution): assert isinstance(default_convolution._sample_components, ComponentCollection) assert isinstance(default_convolution._resolution_components, ComponentCollection) assert default_convolution.upsample_factor == 5 - assert default_convolution.extension_factor == 0.2 + assert default_convolution.extension_factor == pytest.approx(0.2) assert default_convolution.temperature is None assert default_convolution.energy_unit == 'meV' assert default_convolution.normalize_detailed_balance is True @@ -108,7 +109,7 @@ def test_init_components(self, convolution_with_components): assert isinstance(convolution_with_components._sample_components, ComponentCollection) assert isinstance(convolution_with_components._resolution_components, ComponentCollection) assert convolution_with_components.upsample_factor == 5 - assert convolution_with_components.extension_factor == 0.2 + assert convolution_with_components.extension_factor == pytest.approx(0.2) assert convolution_with_components.temperature is None assert convolution_with_components.energy_unit == 'meV' assert convolution_with_components.normalize_detailed_balance is True @@ -317,7 +318,7 @@ def test_convolve_delta_functions(self, default_convolution): assert np.allclose(result, expected_values) # List of analytic functions - analytic_functions = [ + analytic_functions: ClassVar[list[object]] = [ Gaussian(display_name='G', area=1.0, center=0.0, width=0.1), Lorentzian(display_name='L', area=1.0, center=0.0, width=0.1), Voigt( @@ -330,14 +331,19 @@ def test_convolve_delta_functions(self, default_convolution): ] # List of non-analytic functions - non_analytic_functions = [ + non_analytic_functions: ClassVar[list[object]] = [ DampedHarmonicOscillator(display_name='DHO', area=1.0, center=1.0, width=0.1), Polynomial(display_name='P', coefficients=[1.0, 0.0, 0.0]), ] - all_functions_except_delta = analytic_functions + non_analytic_functions - all_functions = all_functions_except_delta + [ - DeltaFunction(display_name='Delta', area=1.0, center=0.0) + all_functions_except_delta: ClassVar[list[object]] = [ + *analytic_functions, + *non_analytic_functions, + ] + + all_functions: ClassVar[list[object]] = [ + *all_functions_except_delta, + DeltaFunction(display_name='Delta', area=1.0, center=0.0), ] @pytest.mark.parametrize('function1', all_functions, ids=lambda f: f.__class__.__name__) diff --git a/tests/unit/easydynamics/convolution/test_convolution_base.py b/tests/unit/easydynamics/convolution/test_convolution_base.py index 44ae133c..68e30388 100644 --- a/tests/unit/easydynamics/convolution/test_convolution_base.py +++ b/tests/unit/easydynamics/convolution/test_convolution_base.py @@ -169,7 +169,7 @@ def test_energy_setter_invalid_type_raises(self, convolution_base): # WHEN THEN EXPECT with pytest.raises( TypeError, - match='Energy must be a Number, a numpy ndarray or a scipp Variable.', + match=r'Energy must be a Number, a numpy ndarray or a scipp Variable.', ): convolution_base.energy = 'invalid' @@ -181,7 +181,7 @@ def test_energy_unit_setter_raises(self, convolution_base): # WHEN THEN EXPECT with pytest.raises( AttributeError, - match='Use convert_unit to change the unit between allowed types ', + match=r'Use convert_unit to change the unit between allowed types ', ): convolution_base.energy_unit = 'K' @@ -198,7 +198,7 @@ def test_convert_energy_unit_invalid_type_raises(self, convolution_base): # WHEN THEN EXPECT with pytest.raises( TypeError, - match='Energy unit must be a string or scipp unit.', + match=r'Energy unit must be a string or scipp unit.', ): convolution_base.convert_energy_unit(123) @@ -206,7 +206,7 @@ def test_convert_energy_unit_invalid_unit_rollback(self, convolution_base): # WHEN THEN with pytest.raises( UnitError, - match='Conversion from `meV` to `s` is not valid.', + match=r'Conversion from `meV` to `s` is not valid.', ): convolution_base.convert_energy_unit('s') @@ -221,7 +221,7 @@ def test_convert_energy_unit_invalid_offset_unit_rollback(self, convolution_base # THEN with pytest.raises( UnitError, - match='Conversion from `s` to `meV` is not valid.', + match=r'Conversion from `s` to `meV` is not valid.', ): convolution_base.convert_energy_unit('meV') @@ -246,7 +246,7 @@ def test_energy_offset_setter_invalid_type_raises(self, convolution_base): # WHEN THEN EXPECT with pytest.raises( TypeError, - match='Energy_offset must be a number or a Parameter.', + match=r'Energy_offset must be a number or a Parameter.', ): convolution_base.energy_offset = 'invalid' @@ -277,8 +277,8 @@ def test_sample_components_setter_invalid_type_raises(self, convolution_base): with pytest.raises( TypeError, match=( - '`sample_components` is an instance of str, ' - 'but must be a ComponentCollection or ModelComponent.' + r'`sample_components` is an instance of str, ' + r'but must be a ComponentCollection or ModelComponent.' ), ): convolution_base.sample_components = 'invalid' @@ -301,8 +301,8 @@ def test_resolution_components_setter_invalid_type_raises(self, convolution_base with pytest.raises( TypeError, match=( - '`resolution_components` is an instance of str, ' - 'but must be a ComponentCollection or ModelComponent.' + r'`resolution_components` is an instance of str, ' + r'but must be a ComponentCollection or ModelComponent.' ), ): convolution_base.resolution_components = 'invalid' diff --git a/tests/unit/easydynamics/convolution/test_numerical_convolution.py b/tests/unit/easydynamics/convolution/test_numerical_convolution.py index 29e5dfd6..320d6c1d 100644 --- a/tests/unit/easydynamics/convolution/test_numerical_convolution.py +++ b/tests/unit/easydynamics/convolution/test_numerical_convolution.py @@ -46,7 +46,7 @@ def test_init(self, default_numerical_convolution): default_numerical_convolution._resolution_components, ComponentCollection ) assert default_numerical_convolution.upsample_factor == 5 - assert default_numerical_convolution.extension_factor == 0.2 + assert default_numerical_convolution.extension_factor == pytest.approx(0.2) assert default_numerical_convolution.temperature is None assert default_numerical_convolution.energy_unit == 'meV' assert default_numerical_convolution.normalize_detailed_balance is True diff --git a/tests/unit/easydynamics/convolution/test_numerical_convolution_base.py b/tests/unit/easydynamics/convolution/test_numerical_convolution_base.py index 3934eefb..3cc00bdc 100644 --- a/tests/unit/easydynamics/convolution/test_numerical_convolution_base.py +++ b/tests/unit/easydynamics/convolution/test_numerical_convolution_base.py @@ -44,7 +44,7 @@ def test_init(self, default_numerical_convolution_base): ComponentCollection, ) assert default_numerical_convolution_base.upsample_factor == 5 - assert default_numerical_convolution_base.extension_factor == 0.2 + assert default_numerical_convolution_base.extension_factor == pytest.approx(0.2) assert default_numerical_convolution_base.temperature is None assert default_numerical_convolution_base.energy_unit == 'meV' assert default_numerical_convolution_base.normalize_detailed_balance is True @@ -100,7 +100,7 @@ def test_init_raises_type_error_for_invalid_temperature(self): invalid_temperature = 'invalid_temperature' # THEN EXPECT - with pytest.raises(TypeError, match='Temperature must be None, a number or a Parameter.'): + with pytest.raises(TypeError, match=r'Temperature must be None, a number or a Parameter.'): NumericalConvolutionBase( energy=energy, sample_components=sample_components, @@ -120,7 +120,7 @@ def test_init_raises_type_error_for_invalid_temperature_unit(self): invalid_temperature_unit = 123 # Not a string or sc.Unit # THEN EXPECT - with pytest.raises(TypeError, match='Temperature_unit must be a string or sc.Unit.'): + with pytest.raises(TypeError, match=r'Temperature_unit must be a string or sc.Unit.'): NumericalConvolutionBase( energy=energy, sample_components=sample_components, @@ -287,7 +287,7 @@ def test_temperature_setter_does_not_replace_parameter( # EXPECT assert default_numerical_convolution_base.temperature is temp_param - assert default_numerical_convolution_base.temperature.value == 350.0 + assert default_numerical_convolution_base.temperature.value == pytest.approx(350.0) def test_temperature_setter_raises(self, default_numerical_convolution_base): """ diff --git a/tests/unit/easydynamics/experiment/test_experiment.py b/tests/unit/easydynamics/experiment/test_experiment.py index 8430d13f..7ffc83c8 100644 --- a/tests/unit/easydynamics/experiment/test_experiment.py +++ b/tests/unit/easydynamics/experiment/test_experiment.py @@ -305,7 +305,7 @@ def test_get_masked_energy(self, experiment_with_data): # EXPECT assert len(masked_energy) == 1 - assert masked_energy.values == 30.0 + assert masked_energy.values == pytest.approx(30.0) def test_get_masked_energy_no_data_returns_None(self): "Test getting masked energy returns zero when no data is present" @@ -526,7 +526,9 @@ def test_extract_x_y_weights_only_finite(self, experiment_with_data): assert np.isfinite(x).all() assert np.isfinite(y).all() assert np.isfinite(weights).all() - assert weights[0] == 1.0 / (experiment_with_data.data.variances[Q_index][2] ** 0.5) + assert weights[0] == pytest.approx( + 1.0 / (experiment_with_data.data.variances[Q_index][2] ** 0.5) + ) assert len(x) == len(y) == len(weights) == 1 # 2 values should be removed # Mask should indicate which values were removed assert np.array_equal(mask, [False, False, True]) diff --git a/tests/unit/easydynamics/sample_model/components/test_damped_harmonic_oscillator.py b/tests/unit/easydynamics/sample_model/components/test_damped_harmonic_oscillator.py index 702c0bfc..79e1913f 100644 --- a/tests/unit/easydynamics/sample_model/components/test_damped_harmonic_oscillator.py +++ b/tests/unit/easydynamics/sample_model/components/test_damped_harmonic_oscillator.py @@ -24,17 +24,17 @@ def test_init_no_inputs(self): # EXPECT assert dho.display_name == 'DampedHarmonicOscillator' - assert dho.area.value == 1.0 - assert dho.center.value == 1.0 - assert dho.width.value == 1.0 + assert dho.area.value == pytest.approx(1.0) + assert dho.center.value == pytest.approx(1.0) + assert dho.width.value == pytest.approx(1.0) assert dho.unit == 'meV' def test_initialization(self, dho: DampedHarmonicOscillator): # WHEN THEN EXPECT assert dho.display_name == 'TestDHO' - assert dho.area.value == 2.0 - assert dho.center.value == 1.5 - assert dho.width.value == 0.3 + assert dho.area.value == pytest.approx(2.0) + assert dho.center.value == pytest.approx(1.5) + assert dho.width.value == pytest.approx(0.3) assert dho.unit == 'meV' def test_init_with_parameters(self): @@ -88,7 +88,7 @@ def test_negative_width_raises(self): # WHEN THEN EXPECT with pytest.raises( ValueError, - match='The width of a DampedHarmonicOscillator must be greater than zero.', + match=r'The width of a DampedHarmonicOscillator must be greater than zero.', ): DampedHarmonicOscillator( display_name='TestDampedHarmonicOscillator', @@ -190,9 +190,9 @@ def test_convert_unit(self, dho: DampedHarmonicOscillator): # EXPECT assert dho.unit == 'microeV' - assert dho.area.value == 2 * 1e3 - assert dho.center.value == 1.5 * 1e3 - assert dho.width.value == 0.3 * 1e3 + assert dho.area.value == pytest.approx(2 * 1e3) + assert dho.center.value == pytest.approx(1.5 * 1e3) + assert dho.width.value == pytest.approx(0.3 * 1e3) def test_copy(self, dho: DampedHarmonicOscillator): # WHEN THEN diff --git a/tests/unit/easydynamics/sample_model/components/test_delta_function.py b/tests/unit/easydynamics/sample_model/components/test_delta_function.py index b5c4bc11..be1e33a0 100644 --- a/tests/unit/easydynamics/sample_model/components/test_delta_function.py +++ b/tests/unit/easydynamics/sample_model/components/test_delta_function.py @@ -23,16 +23,16 @@ def test_init_no_inputs(self): # EXPECT assert delta_function.display_name == 'DeltaFunction' - assert delta_function.area.value == 1.0 - assert delta_function.center.value == 0.0 + assert delta_function.area.value == pytest.approx(1.0) + assert delta_function.center.value == pytest.approx(0.0) assert delta_function.unit == 'meV' assert delta_function.center.fixed is True def test_initialization(self, delta_function: DeltaFunction): # WHEN THEN EXPECT assert delta_function.display_name == 'TestDeltaFunction' - assert delta_function.area.value == 2.0 - assert delta_function.center.value == 0.5 + assert delta_function.area.value == pytest.approx(2.0) + assert delta_function.center.value == pytest.approx(0.5) assert delta_function.unit == 'meV' @pytest.mark.parametrize( @@ -167,7 +167,7 @@ def test_center_is_fixed_if_set_to_None(self, delta_function: DeltaFunction): delta_function.center = None # EXPECT - assert delta_function.center.value == 0.0 + assert delta_function.center.value == pytest.approx(0.0) assert delta_function.center.fixed is True def test_get_all_parameters(self, delta_function: DeltaFunction): @@ -190,8 +190,8 @@ def test_convert_unit(self, delta_function: DeltaFunction): # EXPECT assert delta_function.unit == 'microeV' - assert delta_function.area.value == 2 * 1e3 - assert delta_function.center.value == 0.5 * 1e3 + assert delta_function.area.value == pytest.approx(2 * 1e3) + assert delta_function.center.value == pytest.approx(0.5 * 1e3) def test_copy(self, delta_function: DeltaFunction): # WHEN THEN diff --git a/tests/unit/easydynamics/sample_model/components/test_exponential.py b/tests/unit/easydynamics/sample_model/components/test_exponential.py index d61be76e..2a824532 100644 --- a/tests/unit/easydynamics/sample_model/components/test_exponential.py +++ b/tests/unit/easydynamics/sample_model/components/test_exponential.py @@ -28,17 +28,17 @@ def test_init_no_inputs(self): # THEN EXPECT assert exponential.display_name == 'Exponential' - assert exponential.amplitude.value == 1.0 - assert exponential.center.value == 0.0 - assert exponential.rate.value == 1.0 + assert exponential.amplitude.value == pytest.approx(1.0) + assert exponential.center.value == pytest.approx(0.0) + assert exponential.rate.value == pytest.approx(1.0) assert exponential.unit == 'meV' def test_initialization(self, exponential: Exponential): # WHEN THEN EXPECT assert exponential.display_name == 'TestExponential' - assert exponential.amplitude.value == 2.0 - assert exponential.center.value == 0.5 - assert exponential.rate.value == 1.2 + assert exponential.amplitude.value == pytest.approx(2.0) + assert exponential.center.value == pytest.approx(0.5) + assert exponential.rate.value == pytest.approx(1.2) assert exponential.unit == 'meV' def test_init_with_parameters(self): @@ -133,7 +133,7 @@ def test_center_is_fixed_if_set_to_None(self, exponential: Exponential): exponential.center = None # EXPECT - assert exponential.center.value == 0.0 + assert exponential.center.value == pytest.approx(0.0) assert exponential.center.fixed is True def test_evaluate(self, exponential: Exponential): @@ -172,33 +172,33 @@ def test_convert_unit(self, exponential: Exponential): # THEN EXPECT assert exponential.unit == 'microeV' - assert exponential.amplitude.value == 2.0 * 1e3 - assert exponential.center.value == 0.5 * 1e3 + assert exponential.amplitude.value == pytest.approx(2.0 * 1e3) + assert exponential.center.value == pytest.approx(0.5 * 1e3) # rate should scale inversely - assert exponential.rate.value == 1.2 / 1e3 + assert exponential.rate.value == pytest.approx(1.2 / 1e3) assert str(exponential.rate.unit) == '1/ueV' def test_convert_unit_incorrect_unit_raises(self, exponential: Exponential): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='unit must be a string or sc.Unit'): + with pytest.raises(TypeError, match=r'unit must be a string or sc.Unit'): exponential.convert_unit(123) def test_convert_unit_rollback(self, exponential: Exponential): # WHEN with pytest.raises( UnitError, - match='Failed to convert unit: Conversion from `meV` to `m` is not valid.', + match=r'Failed to convert unit: Conversion from `meV` to `m` is not valid.', ): exponential.convert_unit('m') # THEN EXPECT - values should be unchanged assert exponential.unit == 'meV' - assert exponential.amplitude.value == 2.0 + assert exponential.amplitude.value == pytest.approx(2.0) assert exponential.amplitude.unit == 'meV' - assert exponential.center.value == 0.5 + assert exponential.center.value == pytest.approx(0.5) assert exponential.center.unit == 'meV' - assert exponential.rate.value == 1.2 + assert exponential.rate.value == pytest.approx(1.2) assert exponential.rate.unit == '1/meV' def test_copy(self, exponential: Exponential): diff --git a/tests/unit/easydynamics/sample_model/components/test_expression_component.py b/tests/unit/easydynamics/sample_model/components/test_expression_component.py index 4a7a43fd..27399844 100644 --- a/tests/unit/easydynamics/sample_model/components/test_expression_component.py +++ b/tests/unit/easydynamics/sample_model/components/test_expression_component.py @@ -23,16 +23,16 @@ def test_init_valid(self, expr: ExpressionComponent): assert expr.display_name == 'TestExpression' assert expr.unit == 'meV' - assert expr.A.value == 2.0 - assert expr.x0.value == 0.5 - assert expr.sigma.value == 0.6 + assert expr.A.value == pytest.approx(2.0) + assert expr.x0.value == pytest.approx(0.5) + assert expr.sigma.value == pytest.approx(0.6) def test_init_without_parameters(self): # WHEN THEN expr = ExpressionComponent('A * x', parameters=None) # EXPECT - assert expr.A.value == 1.0 # default + assert expr.A.value == pytest.approx(1.0) # default def test_invalid_expression_raises(self): # WHEN THEN EXPECT @@ -88,7 +88,7 @@ def test_parameter_setter(self, expr: ExpressionComponent): expr.A = 3.0 # EXPECT - assert expr.A.value == 3.0 + assert expr.A.value == pytest.approx(3.0) assert isinstance(expr.A, Parameter) def test_parameter_getter_invalid_name(self, expr: ExpressionComponent): @@ -131,8 +131,8 @@ def test_missing_parameter_defaults(self): expr = ExpressionComponent('A * x + B', parameters={'A': 2.0}) # EXPECT - assert expr.A.value == 2.0 - assert expr.B.value == 1.0 # default + assert expr.A.value == pytest.approx(2.0) + assert expr.B.value == pytest.approx(1.0) # default def test_dir_includes_parameters(self, expr: ExpressionComponent): # WHEN THEN diff --git a/tests/unit/easydynamics/sample_model/components/test_gaussian.py b/tests/unit/easydynamics/sample_model/components/test_gaussian.py index b1ad6f92..7119852a 100644 --- a/tests/unit/easydynamics/sample_model/components/test_gaussian.py +++ b/tests/unit/easydynamics/sample_model/components/test_gaussian.py @@ -22,18 +22,18 @@ def test_init_no_inputs(self): # EXPECT assert gaussian.display_name == 'Gaussian' - assert gaussian.area.value == 1.0 - assert gaussian.center.value == 0.0 - assert gaussian.width.value == 1.0 + assert gaussian.area.value == pytest.approx(1.0) + assert gaussian.center.value == pytest.approx(0.0) + assert gaussian.width.value == pytest.approx(1.0) assert gaussian.unit == 'meV' assert gaussian.center.fixed is True def test_initialization(self, gaussian: Gaussian): # WHEN THEN EXPECT assert gaussian.display_name == 'TestGaussian' - assert gaussian.area.value == 2.0 - assert gaussian.center.value == 0.5 - assert gaussian.width.value == 0.6 + assert gaussian.area.value == pytest.approx(2.0) + assert gaussian.center.value == pytest.approx(0.5) + assert gaussian.width.value == pytest.approx(0.6) assert gaussian.unit == 'meV' def test_init_with_parameters(self): @@ -85,7 +85,9 @@ def test_input_type_validation_raises(self, kwargs, expected_message): def test_negative_width_raises(self): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='The width of a Gaussian must be greater than zero.'): + with pytest.raises( + ValueError, match=r'The width of a Gaussian must be greater than zero.' + ): Gaussian( display_name='TestGaussian', area=2.0, @@ -150,7 +152,7 @@ def test_center_is_fixed_if_set_to_None(self, gaussian: Gaussian): gaussian.center = None # EXPECT - assert gaussian.center.value == 0.0 + assert gaussian.center.value == pytest.approx(0.0) assert gaussian.center.fixed is True def test_get_all_parameters(self, gaussian: Gaussian): @@ -190,9 +192,9 @@ def test_convert_unit(self, gaussian: Gaussian): # EXPECT assert gaussian.unit == 'microeV' - assert gaussian.area.value == 2 * 1e3 - assert gaussian.center.value == 0.5 * 1e3 - assert gaussian.width.value == 0.6 * 1e3 + assert gaussian.area.value == pytest.approx(2 * 1e3) + assert gaussian.center.value == pytest.approx(0.5 * 1e3) + assert gaussian.width.value == pytest.approx(0.6 * 1e3) def test_copy(self, gaussian: Gaussian): # WHEN THEN diff --git a/tests/unit/easydynamics/sample_model/components/test_lorentzian.py b/tests/unit/easydynamics/sample_model/components/test_lorentzian.py index 5f87032d..4ce1e1db 100644 --- a/tests/unit/easydynamics/sample_model/components/test_lorentzian.py +++ b/tests/unit/easydynamics/sample_model/components/test_lorentzian.py @@ -24,18 +24,18 @@ def test_init_no_inputs(self): # EXPECT assert lorentzian.display_name == 'Lorentzian' - assert lorentzian.area.value == 1.0 - assert lorentzian.center.value == 0.0 - assert lorentzian.width.value == 1.0 + assert lorentzian.area.value == pytest.approx(1.0) + assert lorentzian.center.value == pytest.approx(0.0) + assert lorentzian.width.value == pytest.approx(1.0) assert lorentzian.unit == 'meV' assert lorentzian.center.fixed is True def test_initialization(self, lorentzian: Lorentzian): # WHEN THEN EXPECT assert lorentzian.display_name == 'TestLorentzian' - assert lorentzian.area.value == 2.0 - assert lorentzian.center.value == 0.5 - assert lorentzian.width.value == 0.6 + assert lorentzian.area.value == pytest.approx(2.0) + assert lorentzian.center.value == pytest.approx(0.5) + assert lorentzian.width.value == pytest.approx(0.6) assert lorentzian.unit == 'meV' def test_init_with_parameters(self): @@ -88,7 +88,7 @@ def test_input_type_validation_raises(self, kwargs, expected_message): def test_negative_width_raises(self): # WHEN THEN EXPECT with pytest.raises( - ValueError, match='The width of a Lorentzian must be greater than zero.' + ValueError, match=r'The width of a Lorentzian must be greater than zero.' ): Lorentzian( display_name='TestLorentzian', @@ -152,7 +152,7 @@ def test_center_is_fixed_if_set_to_None(self, lorentzian: Lorentzian): lorentzian.center = None # EXPECT - assert lorentzian.center.value == 0.0 + assert lorentzian.center.value == pytest.approx(0.0) assert lorentzian.center.fixed is True def test_get_all_parameters(self, lorentzian: Lorentzian): @@ -189,9 +189,9 @@ def test_convert_unit(self, lorentzian: Lorentzian): # EXPECT assert lorentzian.unit == 'microeV' - assert lorentzian.area.value == 2 * 1e3 - assert lorentzian.center.value == 0.5 * 1e3 - assert lorentzian.width.value == 0.6 * 1e3 + assert lorentzian.area.value == pytest.approx(2 * 1e3) + assert lorentzian.center.value == pytest.approx(0.5 * 1e3) + assert lorentzian.width.value == pytest.approx(0.6 * 1e3) def test_copy(self, lorentzian: Lorentzian): # WHEN THEN diff --git a/tests/unit/easydynamics/sample_model/components/test_mixins.py b/tests/unit/easydynamics/sample_model/components/test_mixins.py index 7c5160ae..4989f76e 100644 --- a/tests/unit/easydynamics/sample_model/components/test_mixins.py +++ b/tests/unit/easydynamics/sample_model/components/test_mixins.py @@ -23,10 +23,10 @@ def test_create_area_parameter_from_numeric(self, dummy_model, area_input, unit) # EXPECT assert isinstance(area_param, Parameter) assert area_param.name == 'TestModel area' - assert area_param.value == float(area_input) + assert area_param.value == pytest.approx(area_input) assert area_param.unit == unit assert not area_param.fixed - assert area_param.min == 0.0 + assert area_param.min == pytest.approx(0.0) def test_create_area_parameter_from_parameter(self, dummy_model): # WHEN @@ -37,7 +37,7 @@ def test_create_area_parameter_from_parameter(self, dummy_model): # EXPECT assert area_param is area_input # Should be the same object - assert area_param.min == 0.0 + assert area_param.min == pytest.approx(0.0) def test_create_area_parameter_invalid_type_raises(self, dummy_model): # WHEN THEN EXPECT @@ -75,7 +75,7 @@ def test_create_center_parameter_from_numeric(self, dummy_model, center_input, u # EXPECT assert isinstance(center_param, Parameter) assert center_param.name == 'TestModel center' - assert center_param.value == float(center_input) + assert center_param.value == pytest.approx(center_input) assert center_param.unit == unit assert not center_param.fixed @@ -88,7 +88,7 @@ def test_create_center_parameter_from_None(self, dummy_model, fix_if_none): # EXPECT assert isinstance(center_param, Parameter) assert center_param.name == 'TestModel center' - assert center_param.value == 0.0 + assert center_param.value == pytest.approx(0.0) assert center_param.unit == 'meV' assert center_param.fixed == fix_if_none @@ -134,7 +134,7 @@ def test_create_width_parameter_from_numeric(self, dummy_model, width_input, uni # EXPECT assert isinstance(width_param, Parameter) assert width_param.name == 'TestModel width' - assert width_param.value == float(width_input) + assert width_param.value == pytest.approx(width_input) assert width_param.unit == unit assert not width_param.fixed diff --git a/tests/unit/easydynamics/sample_model/components/test_model_component.py b/tests/unit/easydynamics/sample_model/components/test_model_component.py index 9e47d4e9..514ced73 100644 --- a/tests/unit/easydynamics/sample_model/components/test_model_component.py +++ b/tests/unit/easydynamics/sample_model/components/test_model_component.py @@ -8,8 +8,6 @@ from easydynamics.sample_model.components.model_component import ModelComponent -Numeric = float | int - class DummyComponent(ModelComponent): def __init__(self): @@ -42,13 +40,13 @@ def test_convert_unit(self, dummy: DummyComponent): # EXPECT assert dummy.unit == 'microeV' - assert dummy.area.value == 1 * 1e3 - assert dummy.center.value == 2 * 1e3 - assert dummy.width.value == 3 * 1e3 + assert dummy.area.value == pytest.approx(1 * 1e3) + assert dummy.center.value == pytest.approx(2 * 1e3) + assert dummy.width.value == pytest.approx(3 * 1e3) def test_convert_unit_incorrect_unit_raises(self, dummy: DummyComponent): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='Unit must be a string or sc.Unit'): + with pytest.raises(TypeError, match=r'Unit must be a string or sc.Unit'): dummy.convert_unit(123) def test_free_and_fix_all_parameters(self, dummy): @@ -150,7 +148,7 @@ def test_prepare_x_for_evaluate_with_different_unit_warns(self, dummy): # THEN EXPECT with pytest.warns( UserWarning, - match='Input x has unit µeV, but DummyComponent component ', + match='Input x has unit [µμ]eV, but DummyComponent component ', ): x_prepared = dummy._prepare_x_for_evaluate(x) @@ -158,7 +156,7 @@ def test_prepare_x_for_evaluate_with_different_unit_warns(self, dummy): assert isinstance(x_prepared, np.ndarray) assert x_prepared.shape == (3,) np.testing.assert_array_equal(x_prepared, [1.0, 2.0, 3.0]) - assert dummy.unit == 'µeV' - assert dummy.area.value == 1.0 * 1e3 - assert dummy.center.value == 2.0 * 1e3 - assert dummy.width.value == 3.0 * 1e3 + assert dummy.unit == 'µeV' # noqa: RUF001 + assert dummy.area.value == pytest.approx(1.0 * 1e3) + assert dummy.center.value == pytest.approx(2.0 * 1e3) + assert dummy.width.value == pytest.approx(3.0 * 1e3) diff --git a/tests/unit/easydynamics/sample_model/components/test_polynomial.py b/tests/unit/easydynamics/sample_model/components/test_polynomial.py index aa7790d2..5e42e677 100644 --- a/tests/unit/easydynamics/sample_model/components/test_polynomial.py +++ b/tests/unit/easydynamics/sample_model/components/test_polynomial.py @@ -22,15 +22,15 @@ def test_init_no_inputs(self): # EXPECT assert polynomial.display_name == 'Polynomial' - assert polynomial.coefficients[0].value == 0.0 + assert polynomial.coefficients[0].value == pytest.approx(0.0) assert polynomial.unit == 'meV' def test_initialization(self, polynomial: Polynomial): # WHEN THEN EXPECT assert polynomial.display_name == 'TestPolynomial' - assert polynomial.coefficients[0].value == 1.0 - assert polynomial.coefficients[1].value == -2.0 - assert polynomial.coefficients[2].value == 3.0 + assert polynomial.coefficients[0].value == pytest.approx(1.0) + assert polynomial.coefficients[1].value == pytest.approx(-2.0) + assert polynomial.coefficients[2].value == pytest.approx(3.0) @pytest.mark.parametrize( 'kwargs, expected_message', @@ -60,7 +60,7 @@ def test_input_type_validation_raises(self, kwargs, expected_message): def test_init_no_coefficients_raises(self): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='At least one coefficient must be provided.'): + with pytest.raises(ValueError, match=r'At least one coefficient must be provided.'): Polynomial(display_name='TestPolynomial', coefficients=[]) def test_negative_value_warns_in_evaluate(self): diff --git a/tests/unit/easydynamics/sample_model/components/test_voigt.py b/tests/unit/easydynamics/sample_model/components/test_voigt.py index f4ea949d..5bf866d8 100644 --- a/tests/unit/easydynamics/sample_model/components/test_voigt.py +++ b/tests/unit/easydynamics/sample_model/components/test_voigt.py @@ -30,20 +30,20 @@ def test_init_no_inputs(self): # EXPECT assert voigt.display_name == 'Voigt' - assert voigt.area.value == 1.0 - assert voigt.center.value == 0.0 - assert voigt.gaussian_width.value == 1.0 - assert voigt.lorentzian_width.value == 1.0 + assert voigt.area.value == pytest.approx(1.0) + assert voigt.center.value == pytest.approx(0.0) + assert voigt.gaussian_width.value == pytest.approx(1.0) + assert voigt.lorentzian_width.value == pytest.approx(1.0) assert voigt.unit == 'meV' assert voigt.center.fixed is True def test_initialization(self, voigt: Voigt): # WHEN THEN EXPECT assert voigt.display_name == 'TestVoigt' - assert voigt.area.value == 2.0 - assert voigt.center.value == 0.5 - assert voigt.gaussian_width.value == 0.6 - assert voigt.lorentzian_width.value == 0.7 + assert voigt.area.value == pytest.approx(2.0) + assert voigt.center.value == pytest.approx(0.5) + assert voigt.gaussian_width.value == pytest.approx(0.6) + assert voigt.lorentzian_width.value == pytest.approx(0.7) assert voigt.unit == 'meV' def test_init_with_parameters(self): @@ -133,7 +133,7 @@ def test_input_type_validation_raises(self, kwargs, expected_message): def test_negative_gaussian_width_raises(self): # WHEN THEN EXPECT with pytest.raises( - ValueError, match='The gaussian_width of a Voigt must be greater than.' + ValueError, match=r'The gaussian_width of a Voigt must be greater than.' ): Voigt( display_name='TestVoigt', @@ -148,7 +148,7 @@ def test_negative_lorentzian_width_raises(self): # WHEN THEN EXPECT with pytest.raises( ValueError, - match='The lorentzian_width of a Voigt must be greater than zero.', + match=r'The lorentzian_width of a Voigt must be greater than zero.', ): Voigt( display_name='TestVoigt', @@ -217,7 +217,7 @@ def test_center_is_fixed_if_set_to_None(self, voigt: Voigt): voigt.center = None # EXPECT - assert voigt.center.value == 0.0 + assert voigt.center.value == pytest.approx(0.0) assert voigt.center.fixed is True def test_evaluate(self, voigt: Voigt): @@ -243,7 +243,7 @@ def test_center_is_fixed_if_init_to_None(self): ) # EXPECT - assert test_voigt.center.value == 0.0 + assert test_voigt.center.value == pytest.approx(0.0) assert test_voigt.center.fixed is True def test_convert_unit(self, voigt: Voigt): @@ -252,10 +252,10 @@ def test_convert_unit(self, voigt: Voigt): # EXPECT assert voigt.unit == 'microeV' - assert voigt.area.value == 2 * 1e3 - assert voigt.center.value == 0.5 * 1e3 - assert voigt.gaussian_width.value == 0.6 * 1e3 - assert voigt.lorentzian_width.value == 0.7 * 1e3 + assert voigt.area.value == pytest.approx(2 * 1e3) + assert voigt.center.value == pytest.approx(0.5 * 1e3) + assert voigt.gaussian_width.value == pytest.approx(0.6 * 1e3) + assert voigt.lorentzian_width.value == pytest.approx(0.7 * 1e3) def test_get_all_parameters(self, voigt: Voigt): # WHEN THEN diff --git a/tests/unit/easydynamics/sample_model/diffusion_model/test_brownian_translational_diffusion.py b/tests/unit/easydynamics/sample_model/diffusion_model/test_brownian_translational_diffusion.py index 80ec766f..be25890e 100644 --- a/tests/unit/easydynamics/sample_model/diffusion_model/test_brownian_translational_diffusion.py +++ b/tests/unit/easydynamics/sample_model/diffusion_model/test_brownian_translational_diffusion.py @@ -26,8 +26,8 @@ def test_init_default(self, brownian_diffusion_model): # WHEN THEN EXPECT assert brownian_diffusion_model.display_name == 'BrownianTranslationalDiffusion' assert brownian_diffusion_model.unit == 'meV' - assert brownian_diffusion_model.scale.value == 1.0 - assert brownian_diffusion_model.diffusion_coefficient.value == 1.0 + assert brownian_diffusion_model.scale.value == pytest.approx(1.0) + assert brownian_diffusion_model.diffusion_coefficient.value == pytest.approx(1.0) @pytest.mark.parametrize( 'kwargs,expected_exception, expected_message', @@ -70,16 +70,16 @@ def test_diffusion_coefficient_setter(self, brownian_diffusion_model): brownian_diffusion_model.diffusion_coefficient = 3.0 # THEN EXPECT - assert brownian_diffusion_model.diffusion_coefficient.value == 3.0 + assert brownian_diffusion_model.diffusion_coefficient.value == pytest.approx(3.0) def test_diffusion_coefficient_setter_raises(self, brownian_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='diffusion_coefficient must be a number.'): + with pytest.raises(TypeError, match=r'diffusion_coefficient must be a number.'): brownian_diffusion_model.diffusion_coefficient = 'invalid' # Invalid type def test_diffusion_coefficient_setter_negative_raises(self, brownian_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='diffusion_coefficient must be non-negative.'): + with pytest.raises(ValueError, match=r'diffusion_coefficient must be non-negative.'): brownian_diffusion_model.diffusion_coefficient = -1.0 # Invalid negative value def test_calculate_width_type_error(self, brownian_diffusion_model): @@ -170,7 +170,7 @@ def test_create_component_collections_component_name_must_be_string( self, brownian_diffusion_model ): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='component_name must be a string.'): + with pytest.raises(TypeError, match=r'component_name must be a string.'): brownian_diffusion_model.create_component_collections( Q=np.array([0.1, 0.2, 0.3]), component_display_name=123 ) @@ -182,7 +182,7 @@ def test_create_component_collections_Q_type_error(self, brownian_diffusion_mode def test_create_component_collections_Q_1dimensional_error(self, brownian_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='Q must be a 1-dimensional array.'): + with pytest.raises(ValueError, match=r'Q must be a 1-dimensional array.'): brownian_diffusion_model.create_component_collections( Q=np.array([[0.1, 0.2], [0.3, 0.4]]) ) # Invalid shape diff --git a/tests/unit/easydynamics/sample_model/diffusion_model/test_diffusion_model.py b/tests/unit/easydynamics/sample_model/diffusion_model/test_diffusion_model.py index 67f654b2..c46dc561 100644 --- a/tests/unit/easydynamics/sample_model/diffusion_model/test_diffusion_model.py +++ b/tests/unit/easydynamics/sample_model/diffusion_model/test_diffusion_model.py @@ -20,7 +20,7 @@ def test_unit_setter_raises(self, diffusion_model): # WHEN THEN EXPECT with pytest.raises( AttributeError, - match='Unit is read-only. Use convert_unit to change the unit between allowed types', + match=r'Unit is read-only. Use convert_unit to change the unit between allowed types', ): diffusion_model.unit = 'eV' @@ -29,16 +29,16 @@ def test_scale_setter(self, diffusion_model): diffusion_model.scale = 2.0 # THEN EXPECT - assert diffusion_model.scale.value == 2.0 + assert diffusion_model.scale.value == pytest.approx(2.0) def test_scale_setter_negative_raises(self, diffusion_model): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='scale must be non-negative.'): + with pytest.raises(ValueError, match=r'scale must be non-negative.'): diffusion_model.scale = -1.0 # Invalid negative value def test_scale_setter_raises(self, diffusion_model): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='scale must be a number.'): + with pytest.raises(TypeError, match=r'scale must be a number.'): diffusion_model.scale = 'invalid' # Invalid type def test_repr(self, diffusion_model): diff --git a/tests/unit/easydynamics/sample_model/diffusion_model/test_jump_translational_diffusion.py b/tests/unit/easydynamics/sample_model/diffusion_model/test_jump_translational_diffusion.py index 756d422f..9388a2db 100644 --- a/tests/unit/easydynamics/sample_model/diffusion_model/test_jump_translational_diffusion.py +++ b/tests/unit/easydynamics/sample_model/diffusion_model/test_jump_translational_diffusion.py @@ -26,9 +26,9 @@ def test_init_default(self, jump_diffusion_model): # WHEN THEN EXPECT assert jump_diffusion_model.display_name == 'JumpTranslationalDiffusion' assert jump_diffusion_model.unit == 'meV' - assert jump_diffusion_model.scale.value == 1.0 - assert jump_diffusion_model.diffusion_coefficient.value == 1.0 - assert jump_diffusion_model.relaxation_time.value == 1.0 + assert jump_diffusion_model.scale.value == pytest.approx(1.0) + assert jump_diffusion_model.diffusion_coefficient.value == pytest.approx(1.0) + assert jump_diffusion_model.relaxation_time.value == pytest.approx(1.0) @pytest.mark.parametrize( 'kwargs,expected_exception, expected_message', @@ -84,16 +84,16 @@ def test_diffusion_coefficient_setter(self, jump_diffusion_model): jump_diffusion_model.diffusion_coefficient = 3.0 # THEN EXPECT - assert jump_diffusion_model.diffusion_coefficient.value == 3.0 + assert jump_diffusion_model.diffusion_coefficient.value == pytest.approx(3.0) def test_diffusion_coefficient_setter_raises(self, jump_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='diffusion_coefficient must be a number.'): + with pytest.raises(TypeError, match=r'diffusion_coefficient must be a number.'): jump_diffusion_model.diffusion_coefficient = 'invalid' # Invalid type def test_diffusion_coefficient_setter_negative_raises(self, jump_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='diffusion_coefficient must be non-negative.'): + with pytest.raises(ValueError, match=r'diffusion_coefficient must be non-negative.'): jump_diffusion_model.diffusion_coefficient = -1.0 # Invalid negative value def test_relaxation_time_setter(self, jump_diffusion_model): @@ -101,16 +101,16 @@ def test_relaxation_time_setter(self, jump_diffusion_model): jump_diffusion_model.relaxation_time = 2.5 # THEN EXPECT - assert jump_diffusion_model.relaxation_time.value == 2.5 + assert jump_diffusion_model.relaxation_time.value == pytest.approx(2.5) def test_relaxation_time_setter_raises(self, jump_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='relaxation_time must be a number.'): + with pytest.raises(TypeError, match=r'relaxation_time must be a number.'): jump_diffusion_model.relaxation_time = 'invalid' # Invalid type def test_relaxation_time_setter_negative_raises(self, jump_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='relaxation_time must be non-negative.'): + with pytest.raises(ValueError, match=r'relaxation_time must be non-negative.'): jump_diffusion_model.relaxation_time = -1.0 # Invalid negative value def test_calculate_width_type_error(self, jump_diffusion_model): @@ -208,7 +208,7 @@ def test_create_component_collections_component_name_must_be_string( self, jump_diffusion_model ): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='component_name must be a string.'): + with pytest.raises(TypeError, match=r'component_name must be a string.'): jump_diffusion_model.create_component_collections( Q=np.array([0.1, 0.2, 0.3]), component_display_name=123 ) @@ -220,7 +220,7 @@ def test_create_component_collections_Q_type_error(self, jump_diffusion_model): def test_create_component_collections_Q_1dimensional_error(self, jump_diffusion_model): # WHEN THEN EXPECT - with pytest.raises(ValueError, match='Q must be a 1-dimensional array.'): + with pytest.raises(ValueError, match=r'Q must be a 1-dimensional array.'): jump_diffusion_model.create_component_collections( Q=np.array([[0.1, 0.2], [0.3, 0.4]]) ) # Invalid shape diff --git a/tests/unit/easydynamics/sample_model/test_component_collection.py b/tests/unit/easydynamics/sample_model/test_component_collection.py index 15ea38ba..c7d41aa1 100644 --- a/tests/unit/easydynamics/sample_model/test_component_collection.py +++ b/tests/unit/easydynamics/sample_model/test_component_collection.py @@ -66,7 +66,7 @@ def test_init_with_components(self): def test_init_with_invalid_components_raises(self): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='Component must be.'): + with pytest.raises(TypeError, match='Component must be'): ComponentCollection(components=['NotAComponent']) def test_init_with_invalid_list_of_components_raises(self): @@ -152,7 +152,7 @@ def test_component_setter(self, component_collection): def test_component_setter_invalid_raises(self, component_collection): # WHEN THEN EXPECT - with pytest.raises(TypeError, match=' must be instances of ModelComponent.'): + with pytest.raises(TypeError, match=r' must be instances of ModelComponent.'): component_collection.components = ['NotAComponent'] with pytest.raises(TypeError, match='components must be a list of'): @@ -174,7 +174,7 @@ def test_is_empty(self): def test_is_empty_setter(self, component_collection): # WHEN THEN EXPECT - with pytest.raises(AttributeError, match='is_empty is a read-only property.'): + with pytest.raises(AttributeError, match=r'is_empty is a read-only property.'): component_collection.is_empty = True def test_component_setter_empty_list(self, component_collection): @@ -204,7 +204,7 @@ def test_convert_unit(self, component_collection): def test_convert_unit_incorrect_unit_raises(self, component_collection): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='Unit must be a string or sc.Unit'): + with pytest.raises(TypeError, match=r'Unit must be a string or sc.Unit'): component_collection.convert_unit(123) def test_convert_unit_failure_rolls_back(self, component_collection): @@ -224,7 +224,7 @@ def convert_unit(self, _unit: str) -> None: } # EXPECT - with pytest.raises(RuntimeError, match='Conversion failed.'): + with pytest.raises(RuntimeError, match=r'Conversion failed.'): component_collection.convert_unit('eV') # Check that all components have their original units @@ -235,7 +235,7 @@ def test_set_unit(self, component_collection): # WHEN THEN EXPECT with pytest.raises( AttributeError, - match='Unit is read-only. Use convert_unit to change the unit', + match=r'Unit is read-only. Use convert_unit to change the unit', ): component_collection.unit = 'eV' @@ -255,7 +255,7 @@ def test_evaluate_no_components_returns_zero(self): x = np.linspace(-5, 5, 100) # EXPECT result = component_collection.evaluate(x) - assert np.all(result == 0.0) + assert np.all(result == pytest.approx(0.0)) assert result.shape == x.shape def test_evaluate_component(self, component_collection): @@ -283,7 +283,7 @@ def test_evaluate_component_no_components_raises(self): component_collection = ComponentCollection(display_name='EmptyModel') x = np.linspace(-5, 5, 100) # EXPECT - with pytest.raises(ValueError, match='No components in the model to evaluate.'): + with pytest.raises(ValueError, match=r'No components in the model to evaluate.'): component_collection.evaluate_component(x, 'AnyComponent') def test_evaluate_component_invalid_name_type_raises(self, component_collection): @@ -293,7 +293,7 @@ def test_evaluate_component_invalid_name_type_raises(self, component_collection) # THEN EXPECT with pytest.raises( TypeError, - match="Component unique name must be a string, got instead.", + match=r"Component unique name must be a string, got instead.", ): component_collection.evaluate_component(x, 123) @@ -312,7 +312,7 @@ def test_normalize_area_no_components_raises(self): # WHEN THEN component_collection = ComponentCollection(display_name='EmptyModel') # EXPECT - with pytest.raises(ValueError, match='No components in the model to normalize.'): + with pytest.raises(ValueError, match=r'No components in the model to normalize.'): component_collection.normalize_area() @pytest.mark.parametrize( @@ -326,7 +326,7 @@ def test_normalize_area_not_finite_area_raises(self, component_collection, area_ component_collection.components[1].area = area_value # EXPECT - with pytest.raises(ValueError, match='cannot normalize.'): + with pytest.raises(ValueError, match=r'cannot normalize'): component_collection.normalize_area() def test_normalize_area_non_area_component_warns(self, component_collection): diff --git a/tests/unit/easydynamics/sample_model/test_instrument_model.py b/tests/unit/easydynamics/sample_model/test_instrument_model.py index dad4ae9f..e8983b0d 100644 --- a/tests/unit/easydynamics/sample_model/test_instrument_model.py +++ b/tests/unit/easydynamics/sample_model/test_instrument_model.py @@ -172,7 +172,7 @@ def test_unit_setter_raises(self, instrument_model): # WHEN / THEN / EXPECT with pytest.raises( AttributeError, - match='Unit is read-only. Use convert_unit to change the unit between allowed types ', + match=r'Unit is read-only. Use convert_unit to change the unit between allowed types ', ): instrument_model.unit = 'meV' @@ -184,7 +184,7 @@ def test_energy_offset_setter(self, instrument_model): instrument_model.energy_offset = 1.0 # EXPECT - assert instrument_model.energy_offset.value == 1.0 + assert instrument_model.energy_offset.value == pytest.approx(1.0) instrument_model._on_energy_offset_change.assert_called_once() def test_energy_offset_setter_raises(self, instrument_model): diff --git a/tests/unit/easydynamics/sample_model/test_model_base.py b/tests/unit/easydynamics/sample_model/test_model_base.py index 41f1cf59..5ad066a2 100644 --- a/tests/unit/easydynamics/sample_model/test_model_base.py +++ b/tests/unit/easydynamics/sample_model/test_model_base.py @@ -268,7 +268,7 @@ def test_convert_unit_invalid_raises(self, model_base): def test_convert_unit_incorrect_unit_raises(self, model_base): # WHEN THEN EXPECT - with pytest.raises(TypeError, match='Unit must be a string or sc.Unit'): + with pytest.raises(TypeError, match=r'Unit must be a string or sc.Unit'): model_base.convert_unit(123) def test_components_setter(self, model_base): diff --git a/tests/unit/easydynamics/sample_model/test_sample_model.py b/tests/unit/easydynamics/sample_model/test_sample_model.py index 6c20f0b0..a093b1c1 100644 --- a/tests/unit/easydynamics/sample_model/test_sample_model.py +++ b/tests/unit/easydynamics/sample_model/test_sample_model.py @@ -65,7 +65,7 @@ def test_init(self, sample_model): assert isinstance(model.diffusion_models, list) assert len(model.diffusion_models) == 1 assert isinstance(model.diffusion_models[0], BrownianTranslationalDiffusion) - assert model.temperature.value == 10.0 + assert model.temperature.value == pytest.approx(10.0) assert model.divide_by_temperature is True np.testing.assert_array_equal(model.Q, np.array([1.0, 2.0, 3.0])) @@ -205,7 +205,7 @@ def test_temperature_setter(self, sample_model): model.temperature = 20.0 # EXPECT - assert model.temperature.value == 20.0 + assert model.temperature.value == pytest.approx(20.0) # THEN model.temperature = None @@ -217,7 +217,7 @@ def test_temperature_setter(self, sample_model): model.temperature = 0.0 # EXPECT - assert model.temperature.value == 0.0 + assert model.temperature.value == pytest.approx(0.0) @pytest.mark.parametrize( 'invalid_value', @@ -233,7 +233,7 @@ def test_temperature_setter_raises_with_invalid_type(self, invalid_value, sample # WHEN / THEN / EXPECT with pytest.raises( (TypeError, ValueError), - match='temperature must be a number or None|temperature must be non-negative', + match=r'temperature must be a number or None|temperature must be non-negative', ): sample_model.temperature = invalid_value @@ -347,9 +347,9 @@ def test_generate_component_collections(self, sample_model): assert isinstance(collection, ComponentCollection) assert len(collection.components) == 3 # 3 components assert collection.components[0].display_name == 'TestGaussian1' - assert collection.components[0].area.value == 1.0 + assert collection.components[0].area.value == pytest.approx(1.0) assert collection.components[1].display_name == 'TestLorentzian1' - assert collection.components[1].area.value == 2.0 + assert collection.components[1].area.value == pytest.approx(2.0) assert collection.components[2].display_name == 'Brownian diffusion' assert isinstance(collection.components[2], Lorentzian) diff --git a/tests/unit/easydynamics/utils/test_detailed_balance.py b/tests/unit/easydynamics/utils/test_detailed_balance.py index 7662dff3..bebf5407 100644 --- a/tests/unit/easydynamics/utils/test_detailed_balance.py +++ b/tests/unit/easydynamics/utils/test_detailed_balance.py @@ -21,7 +21,7 @@ def test_energy_unit_not_string_error(self): T = 100 energy_unit = 5 # Then Expect - with pytest.raises(TypeError, match='energy_unit must be a string.'): + with pytest.raises(TypeError, match=r'energy_unit must be a string.'): detailed_balance_factor(energy, T, energy_unit=energy_unit) @pytest.mark.parametrize('temperature_unit', [5, 5.0, {}, []]) @@ -30,7 +30,7 @@ def test_temperature_unit_not_string_error(self, temperature_unit): energy = 2.0 T = 100 # Then Expect - with pytest.raises(TypeError, match='temperature_unit must be a string.'): + with pytest.raises(TypeError, match=r'temperature_unit must be a string.'): detailed_balance_factor(energy, T, temperature_unit=temperature_unit) def test_divide_by_temperature_not_bool_error(self): @@ -39,7 +39,7 @@ def test_divide_by_temperature_not_bool_error(self): T = 100 divide_by_temperature = 'yes' # Then Expect - with pytest.raises(TypeError, match='divide_by_temperature must be True or False.'): + with pytest.raises(TypeError, match=r'divide_by_temperature must be True or False.'): detailed_balance_factor(energy, T, divide_by_temperature=divide_by_temperature) @pytest.mark.parametrize( @@ -219,7 +219,7 @@ def test_energy_unit_warning(self): # Then with pytest.warns( UserWarning, - match='Input energy has unit µeV, but energy_unit was set to meV. Using µeV.', + match='Input energy has unit [µμ]eV, but energy_unit was set to meV. Using [µμ]eV.', ): result = detailed_balance_factor( energy=energy, From 258132128afcd027d9e3b056c4eae39ca2633fb3 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Sun, 12 Apr 2026 20:21:06 +0200 Subject: [PATCH 03/12] Enable NPY rules --- pixi.lock | 4 ++-- pyproject.toml | 3 +-- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/pixi.lock b/pixi.lock index acba6db3..9e103bfa 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty6 - sha256: 3fe9c7ab629ba0adcbfc5c57698ae2c6f86a62dfae4f6d2c357bee3f83aa8931 + version: 0.4.0+devdirty7 + sha256: f0bef21aaba4f879615ec0f89074d9425c8a42b4451950109e7a2b1497b5b0c5 requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index 3eed6bcf..a17bfc67 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -202,7 +202,7 @@ select = [ #'FURB', # https://docs.astral.sh/ruff/rules/#refurb-furb 'I', # https://docs.astral.sh/ruff/rules/#isort-i #'N', # https://docs.astral.sh/ruff/rules/#pep8-naming-n - #'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy + 'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy #'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh #'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf @@ -272,7 +272,6 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ - # 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf ] 'docs/**' = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ From 7d9ae2f928af4d18e4c97c72a1547a6b1aeb8627 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Sun, 12 Apr 2026 20:21:36 +0200 Subject: [PATCH 04/12] pixi run fix --- pixi.lock | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pixi.lock b/pixi.lock index 9e103bfa..1e4bf425 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty7 - sha256: f0bef21aaba4f879615ec0f89074d9425c8a42b4451950109e7a2b1497b5b0c5 + version: 0.4.0+dev8 + sha256: cd9a1fab6d7b982f762860ccbfa822331d0eabf77a613326c12c1abe4e36f138 requires_dist: - darkdetect - easyscience From 19c5583f066d1e484a894726187868557aa966e4 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 07:09:36 +0200 Subject: [PATCH 05/12] Update link to data in tutorial --- docs/docs/tutorials/tutorial2_nanoparticles.ipynb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb index e7308dbf..da2a493a 100644 --- a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb +++ b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb @@ -78,7 +78,7 @@ "resolution_experiment = Experiment(display_name='Nanoparticles, 1.5 K')\n", "\n", "file_path = pooch.retrieve(\n", - " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/tutorial2/docs/docs/tutorials/data/nano_1p5K.h5',\n", + " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/master/docs/docs/tutorials/data/nano_150K.h5',\n", " known_hash='32935cfaa5e623137ea5f4ba972d0c921bca8d0e067486666cdcf23abf27823e',\n", ")\n", "\n", @@ -216,7 +216,7 @@ "resolution_experiment = Experiment(display_name='Nanoparticles, 1.5 K')\n", "\n", "file_path = pooch.retrieve(\n", - " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/tutorial2/docs/docs/tutorials/data/nano_150K.h5',\n", + " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/master/docs/docs/tutorials/data/nano_150K.h55',\n", " known_hash='dc3abdaf6d2165f4dbc5df4e0de1890af8d4cd290f2c79ac7b319ccbe44117b8',\n", ")\n", "\n", From 4fe4177df8934ae83432445584bb2b148a103371 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 07:14:25 +0200 Subject: [PATCH 06/12] Add PERF linting --- pixi.lock | 4 ++-- pyproject.toml | 4 ++-- src/easydynamics/convolution/convolution.py | 13 ++++++------- 3 files changed, 10 insertions(+), 11 deletions(-) diff --git a/pixi.lock b/pixi.lock index 1e4bf425..5db7e62d 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+dev8 - sha256: cd9a1fab6d7b982f762860ccbfa822331d0eabf77a613326c12c1abe4e36f138 + version: 0.4.0+devdirty10 + sha256: 1401671a88d3e43e0bfd4143e85663e467dce4cfab1881202ce5e88bbd75bc16 requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index a17bfc67..b10778f1 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -204,8 +204,8 @@ select = [ #'N', # https://docs.astral.sh/ruff/rules/#pep8-naming-n 'NPY', # https://docs.astral.sh/ruff/rules/#numpy-specific-rules-npy #'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh - #'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf - 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf + 'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf + 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try 'UP', # https://docs.astral.sh/ruff/rules/#pyupgrade-up # pycodestyle (E, W) rules diff --git a/src/easydynamics/convolution/convolution.py b/src/easydynamics/convolution/convolution.py index 51d3bfa1..33e27873 100644 --- a/src/easydynamics/convolution/convolution.py +++ b/src/easydynamics/convolution/convolution.py @@ -80,9 +80,9 @@ def __init__( The factor by which to extend the input data range before convolution. Default is 0.2. temperature : Parameter | Numeric | None, default=None The temperature to use for detailed balance correction. - temperature_unit : str | sc.Unit, default='K' + temperature_unit : str | sc.Unit, default="K" The unit of the temperature parameter. - energy_unit : str | sc.Unit, default='meV' + energy_unit : str | sc.Unit, default="meV" The unit of the energy. normalize_detailed_balance : bool, default=True Whether to normalize the detailed balance correction. Default is True. @@ -233,11 +233,10 @@ def _build_convolution_plan(self) -> None: # If temperature is not set, check if all # resolution components can be convolved analytically with # this sample component - pair_is_analytic = [] - for resolution_component in self._resolution_components.components: - pair_is_analytic.append( - self._check_if_pair_is_analytic(sample_component, resolution_component) - ) + pair_is_analytic = [ + self._check_if_pair_is_analytic(sample_component, resolution_component) + for resolution_component in self._resolution_components.components + ] # If all resolution components can be convolved analytically # with this sample component, add it to analytical # sample model. If not, it goes to numerical sample model. From f22fe35b132fc5cfde188b7d39f06d7458ddb4a7 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 09:31:50 +0200 Subject: [PATCH 07/12] fix notebook, more linting --- docs/docs/tutorials/tutorial2_nanoparticles.ipynb | 6 +++--- pixi.lock | 4 ++-- pyproject.toml | 6 +++--- src/easydynamics/analysis/analysis1d.py | 1 - src/easydynamics/convolution/convolution.py | 4 ++-- src/easydynamics/sample_model/sample_model.py | 2 -- 6 files changed, 10 insertions(+), 13 deletions(-) diff --git a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb index da2a493a..faa1990a 100644 --- a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb +++ b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb @@ -34,7 +34,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 4, "id": "bca91d3c", "metadata": {}, "outputs": [], @@ -70,7 +70,7 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 6, "id": "4fbb90da", "metadata": {}, "outputs": [], @@ -78,7 +78,7 @@ "resolution_experiment = Experiment(display_name='Nanoparticles, 1.5 K')\n", "\n", "file_path = pooch.retrieve(\n", - " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/master/docs/docs/tutorials/data/nano_150K.h5',\n", + " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/master/docs/docs/tutorials/data/nano_1p5K.h5',\n", " known_hash='32935cfaa5e623137ea5f4ba972d0c921bca8d0e067486666cdcf23abf27823e',\n", ")\n", "\n", diff --git a/pixi.lock b/pixi.lock index 5db7e62d..8467ccda 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty10 - sha256: 1401671a88d3e43e0bfd4143e85663e467dce4cfab1881202ce5e88bbd75bc16 + version: 0.4.0+devdirty11 + sha256: 30c0d149dcd8cc8977bbbc91b120e4e3e82dca640f752b558d7f6198bdc88c4e requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index b10778f1..3e52b77f 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -206,7 +206,7 @@ select = [ #'PGH', # https://docs.astral.sh/ruff/rules/#pygrep-hooks-pgh 'PERF', # https://docs.astral.sh/ruff/rules/#perflint-perf 'RUF', # https://docs.astral.sh/ruff/rules/#ruff-specific-rules-ruf - #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try + #'TRY', # https://docs.astral.sh/ruff/rules/#tryceratops-try # overly restrictive 'UP', # https://docs.astral.sh/ruff/rules/#pyupgrade-up # pycodestyle (E, W) rules 'E', # https://docs.astral.sh/ruff/rules/#error-e @@ -246,8 +246,8 @@ select = [ #'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf #'SLOT', # https://docs.astral.sh/ruff/rules/#flake8-slots-slot #'T20', # https://docs.astral.sh/ruff/rules/#flake8-print-t20 - 'TC', # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc - #'TD', # https://docs.astral.sh/ruff/rules/#flake8-todos-td + 'TC', # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc + 'TD', # https://docs.astral.sh/ruff/rules/#flake8-todos-td 'TID', # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid ] diff --git a/src/easydynamics/analysis/analysis1d.py b/src/easydynamics/analysis/analysis1d.py index 36cc7c8c..20f73da1 100644 --- a/src/easydynamics/analysis/analysis1d.py +++ b/src/easydynamics/analysis/analysis1d.py @@ -671,7 +671,6 @@ def _create_convolver( if resolution_components.is_empty: return None - # TODO: allow convolution options to be set. return Convolution( sample_components=sample_components, resolution_components=resolution_components, diff --git a/src/easydynamics/convolution/convolution.py b/src/easydynamics/convolution/convolution.py index 33e27873..c313d0cf 100644 --- a/src/easydynamics/convolution/convolution.py +++ b/src/easydynamics/convolution/convolution.py @@ -80,9 +80,9 @@ def __init__( The factor by which to extend the input data range before convolution. Default is 0.2. temperature : Parameter | Numeric | None, default=None The temperature to use for detailed balance correction. - temperature_unit : str | sc.Unit, default="K" + temperature_unit : str | sc.Unit, default='K' The unit of the temperature parameter. - energy_unit : str | sc.Unit, default="meV" + energy_unit : str | sc.Unit, default='meV' The unit of the energy. normalize_detailed_balance : bool, default=True Whether to normalize the detailed balance correction. Default is True. diff --git a/src/easydynamics/sample_model/sample_model.py b/src/easydynamics/sample_model/sample_model.py index c7ae1633..b89829ad 100644 --- a/src/easydynamics/sample_model/sample_model.py +++ b/src/easydynamics/sample_model/sample_model.py @@ -439,8 +439,6 @@ def _generate_component_collections(self) -> None: Generate ComponentCollections from the DiffusionModels for each Q and add the components from self._components. """ - # TODO regenerate automatically if Q, diffusion models - # or components have changed super()._generate_component_collections() if self._Q is None: From d0cd8734cc633d9dddaffe0138de3c0939d20f78 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 09:41:48 +0200 Subject: [PATCH 08/12] add more linting rules --- docs/docs/tutorials/analysis.ipynb | 10 ++-------- pixi.lock | 4 ++-- pyproject.toml | 21 +++++++++++---------- src/easydynamics/analysis/analysis.py | 18 +++++++++++------- src/easydynamics/analysis/analysis1d.py | 4 ++-- 5 files changed, 28 insertions(+), 29 deletions(-) diff --git a/docs/docs/tutorials/analysis.ipynb b/docs/docs/tutorials/analysis.ipynb index 623fd942..0d30fd4b 100644 --- a/docs/docs/tutorials/analysis.ipynb +++ b/docs/docs/tutorials/analysis.ipynb @@ -213,10 +213,7 @@ " instrument_model=instrument_model,\n", ")\n", "\n", - "# We need to hack in the resolution model from the vanadium analysis,\n", - "# since the setters and getters overwrite the model. This will be fixed\n", - "# asap.\n", - "diffusion_analysis.instrument_model._resolution_model = (\n", + "diffusion_analysis.instrument_model.resolution_model = (\n", " vanadium_analysis.instrument_model.resolution_model\n", ")\n", "\n", @@ -305,10 +302,7 @@ " instrument_model=instrument_model,\n", ")\n", "\n", - "# We again need to hack in the resolution model from the vanadium\n", - "# analysis, since the setters and getters overwrite the model. This will\n", - "# be fixed asap.\n", - "diffusion_model_analysis.instrument_model._resolution_model = (\n", + "diffusion_model_analysis.instrument_model.resolution_model = (\n", " vanadium_analysis.instrument_model.resolution_model\n", ")\n", "diffusion_model_analysis.instrument_model.resolution_model.fix_all_parameters()\n", diff --git a/pixi.lock b/pixi.lock index 8467ccda..9471cf27 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty11 - sha256: 30c0d149dcd8cc8977bbbc91b120e4e3e82dca640f752b558d7f6198bdc88c4e + version: 0.4.0+devdirty12 + sha256: 02964bf9734bf364e3cd7e32db158570873f2d9891c71e37f9e55b66c4f5e607 requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index 3e52b77f..bd154507 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -239,16 +239,16 @@ select = [ #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth #'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi - 'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret - #'RSE', # https://docs.astral.sh/ruff/rules/#flake8-raise-rse - 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s - 'SIM', # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim - #'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf - #'SLOT', # https://docs.astral.sh/ruff/rules/#flake8-slots-slot - #'T20', # https://docs.astral.sh/ruff/rules/#flake8-print-t20 - 'TC', # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc - 'TD', # https://docs.astral.sh/ruff/rules/#flake8-todos-td - 'TID', # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid + 'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret + 'RSE', # https://docs.astral.sh/ruff/rules/#flake8-raise-rse + 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s + 'SIM', # https://docs.astral.sh/ruff/rules/#flake8-simplify-sim + 'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf + 'SLOT', # https://docs.astral.sh/ruff/rules/#flake8-slots-slot + 'T20', # https://docs.astral.sh/ruff/rules/#flake8-print-t20 + 'TC', # https://docs.astral.sh/ruff/rules/#flake8-type-checking-tc + 'TD', # https://docs.astral.sh/ruff/rules/#flake8-todos-td + 'TID', # https://docs.astral.sh/ruff/rules/#flake8-tidy-imports-tid ] # Exceptions to the linting rules @@ -272,6 +272,7 @@ ignore = [ 'DOC', # https://docs.astral.sh/ruff/rules/#pydoclint-doc 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ 'S101', # https://docs.astral.sh/ruff/rules/assert/ + 'SLF', # https://docs.astral.sh/ruff/rules/#flake8-self-slf # may want to eventually enable it, but accessing private methods is quite useful in tests ] 'docs/**' = [ 'INP001', # https://docs.astral.sh/ruff/rules/implicit-namespace-package/ diff --git a/src/easydynamics/analysis/analysis.py b/src/easydynamics/analysis/analysis.py index bb937e34..f995fbd3 100644 --- a/src/easydynamics/analysis/analysis.py +++ b/src/easydynamics/analysis/analysis.py @@ -40,7 +40,7 @@ def __init__( Parameters ---------- - display_name : str | None, default='MyAnalysis' + display_name : str | None, default="MyAnalysis" Display name of the analysis. unique_name : str | None, default=None Unique name of the analysis. If None, a unique name is automatically generated. @@ -172,7 +172,7 @@ def fit( Parameters ---------- - fit_method : str, default='independent' + fit_method : str, default="independent" Method to use for fitting. Options are "independent" (fit each Q index independently, one after the other) or "simultaneous" (fit all Q indices simultaneously). Q_index : int | None, default=None @@ -567,14 +567,18 @@ def _fit_all_Q_simultaneously(self) -> FitResults: ys = [] ws = [] - for analysis in self.analysis_list: - x, y, weight, _ = self.experiment._extract_x_y_weights_only_finite(analysis.Q_index) + for analysis1d in self.analysis_list: + x, y, weight, _ = self.experiment._extract_x_y_weights_only_finite( # noqa: SLF001 + analysis1d.Q_index + ) xs.append(x) ys.append(y) ws.append(weight) # Make sure the convolver is up to date for this Q index - analysis._convolver = analysis._create_convolver(energy=x) + analysis1d._convolver = analysis1d._create_convolver( # noqa: SLF001 + energy=x + ) mf = MultiFitter( fit_objects=self.analysis_list, @@ -655,10 +659,10 @@ def _create_components_dataset( energy = self.energy datasets = [ - analysis._create_components_dataset_single_Q( + analysis1d._create_components_dataset_single_Q( # noqa: SLF001 add_background=add_background, energy=energy ) - for analysis in self.analysis_list + for analysis1d in self.analysis_list ] return sc.concat(datasets, dim='Q') diff --git a/src/easydynamics/analysis/analysis1d.py b/src/easydynamics/analysis/analysis1d.py index 20f73da1..b8804320 100644 --- a/src/easydynamics/analysis/analysis1d.py +++ b/src/easydynamics/analysis/analysis1d.py @@ -42,7 +42,7 @@ def __init__( Parameters ---------- - display_name : str | None, default='MyAnalysis' + display_name : str | None, default="MyAnalysis" Display name of the analysis. unique_name : str | None, default=None Unique name of the analysis. If None, a unique name is automatically generated. By @@ -195,7 +195,7 @@ def fit(self) -> FitResults: fit_function=self.as_fit_function(), ) - x, y, weights, _ = self.experiment._extract_x_y_weights_only_finite( + x, y, weights, _ = self.experiment._extract_x_y_weights_only_finite( # noqa: SLF001 Q_index=self._require_Q_index() ) fit_result = fitter.fit(x=x, y=y, weights=weights) From 66a986dbf8a660276aa1916b70d1ed18e2f00f14 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 09:44:59 +0200 Subject: [PATCH 09/12] Add PYI linting --- pixi.lock | 4 +- pyproject.toml | 4 +- src/easydynamics/analysis/analysis.py | 4 +- src/easydynamics/analysis/analysis1d.py | 2 +- src/easydynamics/utils/detailed_balance.py | 64 ++++++++++++---------- 5 files changed, 42 insertions(+), 36 deletions(-) diff --git a/pixi.lock b/pixi.lock index 9471cf27..0fb8e24b 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty12 - sha256: 02964bf9734bf364e3cd7e32db158570873f2d9891c71e37f9e55b66c4f5e607 + version: 0.4.0+devdirty13 + sha256: 964490e31e4f0a13708455d740c7303c07d7709f2198993de90542d682eb5af2 requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index bd154507..4b4e5037 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -237,8 +237,8 @@ select = [ #'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log #'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt - 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth - #'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi + 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth + 'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi 'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret 'RSE', # https://docs.astral.sh/ruff/rules/#flake8-raise-rse 'S', # https://docs.astral.sh/ruff/rules/#flake8-bandit-s diff --git a/src/easydynamics/analysis/analysis.py b/src/easydynamics/analysis/analysis.py index f995fbd3..666edfd1 100644 --- a/src/easydynamics/analysis/analysis.py +++ b/src/easydynamics/analysis/analysis.py @@ -40,7 +40,7 @@ def __init__( Parameters ---------- - display_name : str | None, default="MyAnalysis" + display_name : str | None, default='MyAnalysis' Display name of the analysis. unique_name : str | None, default=None Unique name of the analysis. If None, a unique name is automatically generated. @@ -172,7 +172,7 @@ def fit( Parameters ---------- - fit_method : str, default="independent" + fit_method : str, default='independent' Method to use for fitting. Options are "independent" (fit each Q index independently, one after the other) or "simultaneous" (fit all Q indices simultaneously). Q_index : int | None, default=None diff --git a/src/easydynamics/analysis/analysis1d.py b/src/easydynamics/analysis/analysis1d.py index b8804320..614cbebf 100644 --- a/src/easydynamics/analysis/analysis1d.py +++ b/src/easydynamics/analysis/analysis1d.py @@ -42,7 +42,7 @@ def __init__( Parameters ---------- - display_name : str | None, default="MyAnalysis" + display_name : str | None, default='MyAnalysis' Display name of the analysis. unique_name : str | None, default=None Unique name of the analysis. If None, a unique name is automatically generated. By diff --git a/src/easydynamics/utils/detailed_balance.py b/src/easydynamics/utils/detailed_balance.py index 796afaff..80a6cc55 100644 --- a/src/easydynamics/utils/detailed_balance.py +++ b/src/easydynamics/utils/detailed_balance.py @@ -23,10 +23,10 @@ def detailed_balance_factor( - energy: int | float | list | np.ndarray | sc.Variable, - temperature: int | float | sc.Variable | Parameter, - energy_unit: str | sc.Unit = 'meV', - temperature_unit: str | sc.Unit = 'K', + energy: float | list | np.ndarray | sc.Variable, + temperature: float | sc.Variable | Parameter, + energy_unit: str | sc.Unit = "meV", + temperature_unit: str | sc.Unit = "K", divide_by_temperature: bool = True, ) -> np.ndarray: r""" @@ -37,9 +37,9 @@ def detailed_balance_factor( Parameters ---------- - energy : int | float | list | np.ndarray | sc.Variable + energy : float | list | np.ndarray | sc.Variable The energy transfer. If number, assumed to be in meV unless energy_unit is set. - temperature : int | float | sc.Variable | Parameter + temperature : float | sc.Variable | Parameter The temperature. If number, assumed to be in K unless temperature_unit is set. energy_unit : str | sc.Unit, default='meV' Unit for energy if energy is given as a number or list. @@ -84,54 +84,54 @@ def detailed_balance_factor( # Input validation if not isinstance(divide_by_temperature, bool): - raise TypeError('divide_by_temperature must be True or False.') + raise TypeError("divide_by_temperature must be True or False.") if not isinstance(energy_unit, (str, sc.Unit)): - raise TypeError('energy_unit must be a string or scipp.Unit.') + raise TypeError("energy_unit must be a string or scipp.Unit.") if not isinstance(temperature_unit, (str, sc.Unit)): - raise TypeError('temperature_unit must be a string or scipp.Unit.') + raise TypeError("temperature_unit must be a string or scipp.Unit.") # Convert temperature and energy to sc variables # to make units easy to handle temperature = _convert_to_scipp_variable( - value=temperature, unit=temperature_unit, name='temperature' + value=temperature, unit=temperature_unit, name="temperature" ) if temperature.value < 0: - raise ValueError('Temperature must be non-negative.') + raise ValueError("Temperature must be non-negative.") - energy = _convert_to_scipp_variable(value=energy, unit=energy_unit, name='energy') + energy = _convert_to_scipp_variable(value=energy, unit=energy_unit, name="energy") # What if people give units that don't make sense? try: - sc.to_unit(energy, unit='meV') + sc.to_unit(energy, unit="meV") except Exception as e: raise UnitError( - f'The unit of energy is wrong: {energy.unit}: {e} Check that energy has a valid unit.' + f"The unit of energy is wrong: {energy.unit}: {e} Check that energy has a valid unit." ) from e # We give users the option to specify the unit of the energy, # but if the input has a unit, they might clash if energy.unit != energy_unit: warnings.warn( - f'Input energy has unit {energy.unit}, but energy_unit was set to {energy_unit}. ' - f'Using {energy.unit}.', + f"Input energy has unit {energy.unit}, but energy_unit was set to {energy_unit}. " + f"Using {energy.unit}.", stacklevel=2, ) # Same for temperature try: - sc.to_unit(temperature, unit='K') + sc.to_unit(temperature, unit="K") except Exception as e: raise UnitError( - f'The unit of temperature is wrong: {temperature.unit}: {e} ' - f'Check that temperature has a valid unit.' + f"The unit of temperature is wrong: {temperature.unit}: {e} " + f"Check that temperature has a valid unit." ) from e if temperature.unit != temperature_unit: warnings.warn( - f'Input temperature has unit {temperature.unit}, ' - f'but temperature_unit was set to {temperature_unit}. Using {temperature.unit}.', + f"Input temperature has unit {temperature.unit}, " + f"but temperature_unit was set to {temperature_unit}. Using {temperature.unit}.", stacklevel=2, ) @@ -139,7 +139,7 @@ def detailed_balance_factor( # Here, DBF is 0 for energy<0 and energy for energy>0 if temperature.value == 0: if divide_by_temperature: - raise ZeroDivisionError('Cannot divide by T when T = 0.') + raise ZeroDivisionError("Cannot divide by T when T = 0.") DBF = sc.where(energy < 0.0 * energy.unit, 0.0 * energy.unit, energy) return np.array([DBF.value]) if DBF.sizes == {} else DBF.values @@ -151,7 +151,7 @@ def detailed_balance_factor( x = energy / (kB * temperature) - x = sc.to_unit(x, unit='1') # Make sure the unit is 1 and not e.g. 1e3 + x = sc.to_unit(x, unit="1") # Make sure the unit is 1 and not e.g. 1e3 # Now compute DBF. First handle small and large x, then general. @@ -168,7 +168,9 @@ def detailed_balance_factor( # General case: exact formula mid = sc.logical_not(small) & sc.logical_not(large) - DBF = sc.where(mid, x / (1 - sc.exp(-x)), DBF) # zeros in x are handled by SMALL_THRESHOLD + DBF = sc.where( + mid, x / (1 - sc.exp(-x)), DBF + ) # zeros in x are handled by SMALL_THRESHOLD # if not divide_by_temperature: @@ -179,7 +181,7 @@ def detailed_balance_factor( def _convert_to_scipp_variable( - value: int | float | list | np.ndarray | Parameter | sc.Variable, + value: float | list | np.ndarray | Parameter | sc.Variable, name: str, unit: str | None = None, ) -> sc.Variable: @@ -222,9 +224,13 @@ def _convert_to_scipp_variable( array_value = np.array(value.value) unit = value.unit else: - if name == 'energy': - raise TypeError(f'{name} must be a number, list, numpy array or scipp Variable') - raise TypeError(f'{name} must be a number, list, numpy array, Parameter or scipp Variable') + if name == "energy": + raise TypeError( + f"{name} must be a number, list, numpy array or scipp Variable" + ) + raise TypeError( + f"{name} must be a number, list, numpy array, Parameter or scipp Variable" + ) # Create appropriate scipp variable based on shape if array_value.shape == () or (array_value.shape == (1,)): @@ -236,6 +242,6 @@ def _convert_to_scipp_variable( else: # Multi-element array try: - return sc.array(dims=['x'], values=array_value, unit=unit) + return sc.array(dims=["x"], values=array_value, unit=unit) except UnitError as e: raise UnitError(f"Invalid unit string '{unit}' for {name}: {e}") from e From 3905db5de58aa534c4624931d69b723b348f541a Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 09:48:43 +0200 Subject: [PATCH 10/12] another linting rule --- pixi.lock | 4 +- pyproject.toml | 4 +- .../components/model_component.py | 1 - src/easydynamics/utils/detailed_balance.py | 56 +++++++++---------- 4 files changed, 29 insertions(+), 36 deletions(-) diff --git a/pixi.lock b/pixi.lock index 0fb8e24b..d8bfd542 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty13 - sha256: 964490e31e4f0a13708455d740c7303c07d7709f2198993de90542d682eb5af2 + version: 0.4.0+devdirty14 + sha256: fe06a4305715447ec132abc4bb392e74192538a015f0a2054712b18bfa769c39 requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index 4b4e5037..b168cbde 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -235,8 +235,8 @@ select = [ #'INP', # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp #'ISC', # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc #'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log - #'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie - #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt + 'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie + #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt # Should eventually be enabled, but quite a few errors to fix 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth 'PYI', # https://docs.astral.sh/ruff/rules/#flake8-pyi-pyi 'RET', # https://docs.astral.sh/ruff/rules/#flake8-return-ret diff --git a/src/easydynamics/sample_model/components/model_component.py b/src/easydynamics/sample_model/components/model_component.py index 4322c5da..743a1aeb 100644 --- a/src/easydynamics/sample_model/components/model_component.py +++ b/src/easydynamics/sample_model/components/model_component.py @@ -235,7 +235,6 @@ def evaluate(self, x: Numeric | list | np.ndarray | sc.Variable | sc.DataArray) np.ndarray Evaluated function values. """ - pass def __repr__(self) -> str: """ diff --git a/src/easydynamics/utils/detailed_balance.py b/src/easydynamics/utils/detailed_balance.py index 80a6cc55..6ee9e19f 100644 --- a/src/easydynamics/utils/detailed_balance.py +++ b/src/easydynamics/utils/detailed_balance.py @@ -25,8 +25,8 @@ def detailed_balance_factor( energy: float | list | np.ndarray | sc.Variable, temperature: float | sc.Variable | Parameter, - energy_unit: str | sc.Unit = "meV", - temperature_unit: str | sc.Unit = "K", + energy_unit: str | sc.Unit = 'meV', + temperature_unit: str | sc.Unit = 'K', divide_by_temperature: bool = True, ) -> np.ndarray: r""" @@ -84,54 +84,54 @@ def detailed_balance_factor( # Input validation if not isinstance(divide_by_temperature, bool): - raise TypeError("divide_by_temperature must be True or False.") + raise TypeError('divide_by_temperature must be True or False.') if not isinstance(energy_unit, (str, sc.Unit)): - raise TypeError("energy_unit must be a string or scipp.Unit.") + raise TypeError('energy_unit must be a string or scipp.Unit.') if not isinstance(temperature_unit, (str, sc.Unit)): - raise TypeError("temperature_unit must be a string or scipp.Unit.") + raise TypeError('temperature_unit must be a string or scipp.Unit.') # Convert temperature and energy to sc variables # to make units easy to handle temperature = _convert_to_scipp_variable( - value=temperature, unit=temperature_unit, name="temperature" + value=temperature, unit=temperature_unit, name='temperature' ) if temperature.value < 0: - raise ValueError("Temperature must be non-negative.") + raise ValueError('Temperature must be non-negative.') - energy = _convert_to_scipp_variable(value=energy, unit=energy_unit, name="energy") + energy = _convert_to_scipp_variable(value=energy, unit=energy_unit, name='energy') # What if people give units that don't make sense? try: - sc.to_unit(energy, unit="meV") + sc.to_unit(energy, unit='meV') except Exception as e: raise UnitError( - f"The unit of energy is wrong: {energy.unit}: {e} Check that energy has a valid unit." + f'The unit of energy is wrong: {energy.unit}: {e} Check that energy has a valid unit.' ) from e # We give users the option to specify the unit of the energy, # but if the input has a unit, they might clash if energy.unit != energy_unit: warnings.warn( - f"Input energy has unit {energy.unit}, but energy_unit was set to {energy_unit}. " - f"Using {energy.unit}.", + f'Input energy has unit {energy.unit}, but energy_unit was set to {energy_unit}. ' + f'Using {energy.unit}.', stacklevel=2, ) # Same for temperature try: - sc.to_unit(temperature, unit="K") + sc.to_unit(temperature, unit='K') except Exception as e: raise UnitError( - f"The unit of temperature is wrong: {temperature.unit}: {e} " - f"Check that temperature has a valid unit." + f'The unit of temperature is wrong: {temperature.unit}: {e} ' + f'Check that temperature has a valid unit.' ) from e if temperature.unit != temperature_unit: warnings.warn( - f"Input temperature has unit {temperature.unit}, " - f"but temperature_unit was set to {temperature_unit}. Using {temperature.unit}.", + f'Input temperature has unit {temperature.unit}, ' + f'but temperature_unit was set to {temperature_unit}. Using {temperature.unit}.', stacklevel=2, ) @@ -139,7 +139,7 @@ def detailed_balance_factor( # Here, DBF is 0 for energy<0 and energy for energy>0 if temperature.value == 0: if divide_by_temperature: - raise ZeroDivisionError("Cannot divide by T when T = 0.") + raise ZeroDivisionError('Cannot divide by T when T = 0.') DBF = sc.where(energy < 0.0 * energy.unit, 0.0 * energy.unit, energy) return np.array([DBF.value]) if DBF.sizes == {} else DBF.values @@ -151,7 +151,7 @@ def detailed_balance_factor( x = energy / (kB * temperature) - x = sc.to_unit(x, unit="1") # Make sure the unit is 1 and not e.g. 1e3 + x = sc.to_unit(x, unit='1') # Make sure the unit is 1 and not e.g. 1e3 # Now compute DBF. First handle small and large x, then general. @@ -168,9 +168,7 @@ def detailed_balance_factor( # General case: exact formula mid = sc.logical_not(small) & sc.logical_not(large) - DBF = sc.where( - mid, x / (1 - sc.exp(-x)), DBF - ) # zeros in x are handled by SMALL_THRESHOLD + DBF = sc.where(mid, x / (1 - sc.exp(-x)), DBF) # zeros in x are handled by SMALL_THRESHOLD # if not divide_by_temperature: @@ -190,7 +188,7 @@ def _convert_to_scipp_variable( Parameters ---------- - value : int | float | list | np.ndarray | Parameter | sc.Variable + value : float | list | np.ndarray | Parameter | sc.Variable The value to convert. Can be a number, list, numpy array, Parameter, or scipp Variable. If a number or list, the unit must be specified in the unit argument. name : str @@ -224,13 +222,9 @@ def _convert_to_scipp_variable( array_value = np.array(value.value) unit = value.unit else: - if name == "energy": - raise TypeError( - f"{name} must be a number, list, numpy array or scipp Variable" - ) - raise TypeError( - f"{name} must be a number, list, numpy array, Parameter or scipp Variable" - ) + if name == 'energy': + raise TypeError(f'{name} must be a number, list, numpy array or scipp Variable') + raise TypeError(f'{name} must be a number, list, numpy array, Parameter or scipp Variable') # Create appropriate scipp variable based on shape if array_value.shape == () or (array_value.shape == (1,)): @@ -242,6 +236,6 @@ def _convert_to_scipp_variable( else: # Multi-element array try: - return sc.array(dims=["x"], values=array_value, unit=unit) + return sc.array(dims=['x'], values=array_value, unit=unit) except UnitError as e: raise UnitError(f"Invalid unit string '{unit}' for {name}: {e}") from e From e50bbb2e1f90299087ecb0d8f16941943fb015be Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 09:58:30 +0200 Subject: [PATCH 11/12] add a bunch of small linting rules+ --- pixi.lock | 4 ++-- pyproject.toml | 32 ++++++++++++++++---------------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/pixi.lock b/pixi.lock index d8bfd542..5f682ecf 100644 --- a/pixi.lock +++ b/pixi.lock @@ -3888,8 +3888,8 @@ packages: requires_python: '>=3.5' - pypi: ./ name: easydynamics - version: 0.4.0+devdirty14 - sha256: fe06a4305715447ec132abc4bb392e74192538a015f0a2054712b18bfa769c39 + version: 0.4.0+devdirty15 + sha256: 03e3e912335d4cddf2e70179da113fb35cb4cad133450b3a9e8583aaf5927e6e requires_dist: - darkdetect - easyscience diff --git a/pyproject.toml b/pyproject.toml index b168cbde..36950e9c 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -215,26 +215,26 @@ select = [ #'PLC', # https://docs.astral.sh/ruff/rules/#convention-plc #'PLE', # https://docs.astral.sh/ruff/rules/#error-ple #'PLR', # https://docs.astral.sh/ruff/rules/#refactor-plr - #'PLW', # https://docs.astral.sh/ruff/rules/#warning-plw + #'PLW', # https://docs.astral.sh/ruff/rules/#warning-plw # Good to enable # flake8 rules - 'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a - 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann - 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg - #'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async - 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b - #'BLE', # https://docs.astral.sh/ruff/rules/#flake8-blind-except-ble + 'A', # https://docs.astral.sh/ruff/rules/#flake8-builtins-a + 'ANN', # https://docs.astral.sh/ruff/rules/#flake8-annotations-ann + 'ARG', # https://docs.astral.sh/ruff/rules/#flake8-unused-arguments-arg + 'ASYNC', # https://docs.astral.sh/ruff/rules/#flake8-async-async + 'B', # https://docs.astral.sh/ruff/rules/#flake8-bugbear-b + #'BLE', # https://docs.astral.sh/ruff/rules/#flake8-blind-except-ble # enable when base classes have been merged in 'C4', # https://docs.astral.sh/ruff/rules/#flake8-comprehensions-c4 'COM', # https://docs.astral.sh/ruff/rules/#flake8-commas-com - #'DTZ', # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz - #'EM', # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em + 'DTZ', # https://docs.astral.sh/ruff/rules/#flake8-datetimez-dtz + #'EM', # https://docs.astral.sh/ruff/rules/#flake8-errmsg-em # Unsure if I want it - requires all error messages to be rewritten 'FA', # https://docs.astral.sh/ruff/rules/#flake8-future-annotations-fa - #'FBT', # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt - #'FIX', # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix - #'G', # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g - #'ICN', # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn - #'INP', # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp - #'ISC', # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc - #'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log + #'FBT', # https://docs.astral.sh/ruff/rules/#flake8-boolean-trap-fbt # should eventually be enabled, but may break serialization + 'FIX', # https://docs.astral.sh/ruff/rules/#flake8-fixme-fix + 'G', # https://docs.astral.sh/ruff/rules/#flake8-logging-format-g + 'ICN', # https://docs.astral.sh/ruff/rules/#flake8-import-conventions-icn + 'INP', # https://docs.astral.sh/ruff/rules/#flake8-no-pep420-inp + 'ISC', # https://docs.astral.sh/ruff/rules/#flake8-implicit-str-concat-isc + 'LOG', # https://docs.astral.sh/ruff/rules/#flake8-logging-log 'PIE', # https://docs.astral.sh/ruff/rules/#flake8-pie-pie #'PT', # https://docs.astral.sh/ruff/rules/#flake8-pytest-style-pt # Should eventually be enabled, but quite a few errors to fix 'PTH', # https://docs.astral.sh/ruff/rules/#flake8-use-pathlib-pth From c0df9869c6ff7fa5588e2269f6edcb114e04eaa3 Mon Sep 17 00:00:00 2001 From: henrikjacobsenfys Date: Mon, 13 Apr 2026 10:00:59 +0200 Subject: [PATCH 12/12] fix typo in tutorial --- docs/docs/tutorials/tutorial2_nanoparticles.ipynb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb index faa1990a..b79984df 100644 --- a/docs/docs/tutorials/tutorial2_nanoparticles.ipynb +++ b/docs/docs/tutorials/tutorial2_nanoparticles.ipynb @@ -216,7 +216,7 @@ "resolution_experiment = Experiment(display_name='Nanoparticles, 1.5 K')\n", "\n", "file_path = pooch.retrieve(\n", - " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/master/docs/docs/tutorials/data/nano_150K.h55',\n", + " url='https://github.com/easyscience/dynamics-lib/raw/refs/heads/master/docs/docs/tutorials/data/nano_150K.h5',\n", " known_hash='dc3abdaf6d2165f4dbc5df4e0de1890af8d4cd290f2c79ac7b319ccbe44117b8',\n", ")\n", "\n",