From ed7fa52d06bdf0d7a030e741d2dca0164a037117 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:28:24 +0100 Subject: [PATCH 01/11] Adds ruff and resolves linting errors on standard rule set --- RAT/__init__.py | 6 ++++-- RAT/examples/absorption/volume_thiol_bilayer.py | 4 ---- RAT/examples/domains/domains_XY_model.py | 12 +++++------- RAT/examples/languages/custom_bilayer.py | 1 - RAT/examples/non_polarised/custom_XY_DSPC.py | 11 +++++------ RAT/examples/non_polarised/custom_bilayer_DSPC.py | 1 - RAT/utils/plotting.py | 3 ++- pyproject.toml | 2 ++ tests/test_classlist.py | 2 +- tests/test_inputs.py | 2 +- tests/test_project.py | 6 +++--- 11 files changed, 23 insertions(+), 27 deletions(-) diff --git a/RAT/__init__.py b/RAT/__init__.py index fe0d597d..f5b8981c 100644 --- a/RAT/__init__.py +++ b/RAT/__init__.py @@ -1,9 +1,11 @@ import os from RAT.classlist import ClassList -from RAT.project import Project from RAT.controls import set_controls +from RAT.project import Project from RAT.run import run -import RAT.models +import RAT.models as models + +__all__ = ["ClassList", "Project", "run", "set_controls", "models"] dir_path = os.path.dirname(os.path.realpath(__file__)) os.environ["RAT_PATH"] = os.path.join(dir_path, '') diff --git a/RAT/examples/absorption/volume_thiol_bilayer.py b/RAT/examples/absorption/volume_thiol_bilayer.py index 754454ca..8896007d 100644 --- a/RAT/examples/absorption/volume_thiol_bilayer.py +++ b/RAT/examples/absorption/volume_thiol_bilayer.py @@ -53,7 +53,6 @@ def volume_thiol_bilayer(params, bulk_in, bulk_out, contrast): bh = -0.3739e-4 # Hydrogen bp = 0.513e-4 # Phosphorus bn = 0.936e-4 # Nitrogen - bd = 0.6671e-4 # Deuterium # Work out the total scattering length in each fragment # Define scattering lengths @@ -65,8 +64,6 @@ def volume_thiol_bilayer(params, bulk_in, bulk_out, contrast): CH2 = (1*bc) + (2*bh) CH = (1*bc) + (1*bh) CHOL = (5*bc) + (12*bh) + (1*bn) - H2O = (2*bh) + (1*bo) - D2O = (2*bd) + (1*bo) # And also volumes vCH3 = 52.7 # CH3 volume in the paper appears to be for 2 * CH3's @@ -75,7 +72,6 @@ def volume_thiol_bilayer(params, bulk_in, bulk_out, contrast): vGLYC = 68.8 vPO4 = 53.7 vCHOL = 120.4 - vWAT = 30.4 vCHCH = 42.14 vHead = vCHOL + vPO4 + vGLYC + 2*vCOO diff --git a/RAT/examples/domains/domains_XY_model.py b/RAT/examples/domains/domains_XY_model.py index 059d1112..89a53f67 100644 --- a/RAT/examples/domains/domains_XY_model.py +++ b/RAT/examples/domains/domains_XY_model.py @@ -55,18 +55,16 @@ def makeLayer(z, prevLaySurf, thickness, height, Sigma_L, Sigma_R): Each side of the layer has its own roughness value. """ # Find the edges - l = prevLaySurf - r = prevLaySurf + thickness + left = prevLaySurf + right = prevLaySurf + thickness # Make our heaviside - a = (z-l) / ((2**0.5) * Sigma_L) - b = (z-r) / ((2**0.5) * Sigma_R) + a = (z-left) / ((2**0.5) * Sigma_L) + b = (z-right) / ((2**0.5) * Sigma_R) erf_a = np.array([math.erf(value) for value in a]) erf_b = np.array([math.erf(value) for value in b]) VF = np.array((height / 2) * (erf_a - erf_b)) - thisLaySurf = r - - return VF, thisLaySurf + return VF, right diff --git a/RAT/examples/languages/custom_bilayer.py b/RAT/examples/languages/custom_bilayer.py index 4f61454c..e7181cf8 100644 --- a/RAT/examples/languages/custom_bilayer.py +++ b/RAT/examples/languages/custom_bilayer.py @@ -25,7 +25,6 @@ def custom_bilayer(params, bulk_in, bulk_out, contrast): bh = -0.3739e-4 # Hydrogen bp = 0.513e-4 # Phosphorus bn = 0.936e-4 # Nitrogen - bd = 0.6671e-4 # Deuterium # Now make the lipid groups COO = (4*bo) + (2*bc) diff --git a/RAT/examples/non_polarised/custom_XY_DSPC.py b/RAT/examples/non_polarised/custom_XY_DSPC.py index ebcf31d8..57b1301b 100644 --- a/RAT/examples/non_polarised/custom_XY_DSPC.py +++ b/RAT/examples/non_polarised/custom_XY_DSPC.py @@ -24,7 +24,6 @@ def custom_XY_DSPC(params, bulk_in, bulk_out, contrast): bh = -0.3739e-4 # Hydrogen bp = 0.513e-4 # Phosphorus bn = 0.936e-4 # Nitrogen - bd = 0.6671e-4 # Deuterium # Now make the lipid groups COO = (4*bo) + (2*bc) @@ -124,16 +123,16 @@ def layer(z, prevLaySurf, thickness, height, Sigma_L, Sigma_R): Each side of the layer has its own roughness value. """ # Find the edges - l = prevLaySurf - r = prevLaySurf + thickness + left = prevLaySurf + right = prevLaySurf + thickness # Make our heaviside - a = (z-l) / ((2**0.5) * Sigma_L) - b = (z-r) / ((2**0.5) * Sigma_R) + a = (z-left) / ((2**0.5) * Sigma_L) + b = (z-right) / ((2**0.5) * Sigma_R) erf_a = np.array([math.erf(value) for value in a]) erf_b = np.array([math.erf(value) for value in b]) VF = np.array((height / 2) * (erf_a - erf_b)) - return VF, r + return VF, right diff --git a/RAT/examples/non_polarised/custom_bilayer_DSPC.py b/RAT/examples/non_polarised/custom_bilayer_DSPC.py index 2aa3eb91..796eb4bb 100644 --- a/RAT/examples/non_polarised/custom_bilayer_DSPC.py +++ b/RAT/examples/non_polarised/custom_bilayer_DSPC.py @@ -45,7 +45,6 @@ def custom_bilayer_DSPC(params, bulk_in, bulk_out, contrast): bh = -0.3739e-4 # Hydrogen bp = 0.513e-4 # Phosphorus bn = 0.936e-4 # Nitrogen - bd = 0.6671e-4 # Deuterium # Now make the lipid groups COO = (4*bo) + (2*bc) diff --git a/RAT/utils/plotting.py b/RAT/utils/plotting.py index e79bac38..5cbc72f2 100644 --- a/RAT/utils/plotting.py +++ b/RAT/utils/plotting.py @@ -2,6 +2,7 @@ Plots using the matplotlib library """ from typing import Optional, Union +from matplotlib.axes._axes import Axes import matplotlib.pyplot as plt import numpy as np from RAT.rat_core import PlotEventData, makeSLDProfileXY @@ -60,7 +61,7 @@ def _close(self, _): self._close_clicked = True -def plot_errorbars(ax: 'matplotlib.axes._axes.Axes', x: np.ndarray, y: np.ndarray, err: np.ndarray, +def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, one_sided: bool, color: str): """ Plots the error bars. diff --git a/pyproject.toml b/pyproject.toml index b91bf7dc..1b050698 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -6,3 +6,5 @@ requires = [ ] build-backend = 'setuptools.build_meta' +[tool.ruff] +line-length = 120 diff --git a/tests/test_classlist.py b/tests/test_classlist.py index 7c71b6f7..badf4f21 100644 --- a/tests/test_classlist.py +++ b/tests/test_classlist.py @@ -169,7 +169,7 @@ def test_setitem_same_name_field(two_name_class_list: ClassList, new_item: Input ]) def test_setitem_different_classes(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" - with pytest.raises(ValueError, match=f"Input list contains elements of type other than 'InputAttributes'"): + with pytest.raises(ValueError, match="Input list contains elements of type other than 'InputAttributes'"): two_name_class_list[0] = new_values diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 3e9469a4..2aecb581 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -558,7 +558,7 @@ def test_check_indices(test_problem, request) -> None: ("domains_problem", "contrastResolutionParams", [0.0]), ("domains_problem", "contrastResolutionParams", [2.0]), ]) -def test_check_indices(test_problem, index_list, bad_value, request) -> None: +def test_check_indices_error(test_problem, index_list, bad_value, request) -> None: """The check_indices routine should raise an IndexError if a contrast list contains an index that is out of the range of the corresponding parameter list in a ProblemDefinition object.""" param_list = {'contrastBulkIns': 'bulkIn', diff --git a/tests/test_project.py b/tests/test_project.py index 9eb15163..3342b633 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -405,8 +405,8 @@ def test_check_protected_parameters(delete_operation) -> None: """If we try to remove a protected parameter, we should raise an error.""" project = RAT.Project() - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, Can\'t delete' - f' the protected parameters: Substrate Roughness'): + with pytest.raises(pydantic.ValidationError, match='1 validation error for Project\n Value error, Can\'t delete' + ' the protected parameters: Substrate Roughness'): eval(delete_operation) # Ensure model was not deleted @@ -659,7 +659,7 @@ def test_check_contrast_model_allowed_values(test_values: list[str]) -> None: ["Undefined Param"], ["Test Layer", "Undefined Param"], ]) -def test_check_allowed_values_not_on_list(test_values: list[str]) -> None: +def test_check_allowed_contrast_model_not_on_list(test_values: list[str]) -> None: """If string values are defined in a non-empty list and any of them are not included on the list of allowed values we should raise a ValueError. """ From 6437d7568b0e5441ca0ef544377d115db3cc703e Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 25 Jun 2024 14:42:52 +0100 Subject: [PATCH 02/11] Resolves automatically fixable linting errors on advanced rule set --- RAT/__init__.py | 3 +- RAT/classlist.py | 7 +-- RAT/controls.py | 7 +-- RAT/events.py | 5 +- RAT/examples/absorption/absorption.py | 3 +- RAT/examples/domains/domains_XY_model.py | 1 + .../languages/run_custom_file_languages.py | 7 +-- RAT/examples/languages/setup_problem.py | 4 +- RAT/examples/non_polarised/DSPC_custom_XY.py | 3 +- .../non_polarised/DSPC_custom_layers.py | 3 +- .../non_polarised/DSPC_standard_layers.py | 3 +- RAT/examples/non_polarised/custom_XY_DSPC.py | 1 + .../non_polarised/custom_bilayer_DSPC.py | 1 + RAT/inputs.py | 5 +- RAT/models.py | 5 +- RAT/outputs.py | 6 ++- RAT/project.py | 44 ++++++++--------- RAT/run.py | 4 +- RAT/utils/enums.py | 1 + RAT/utils/plotting.py | 5 +- RAT/wrappers.py | 2 + pyproject.toml | 3 ++ setup.py | 11 +++-- tests/test_classlist.py | 9 ++-- tests/test_controls.py | 47 ++++++++++--------- tests/test_custom_errors.py | 5 +- tests/test_events.py | 2 + tests/test_inputs.py | 22 ++++++--- tests/test_models.py | 7 +-- tests/test_outputs.py | 1 - tests/test_plotting.py | 10 ++-- tests/test_project.py | 16 +++---- tests/test_run.py | 4 +- tests/test_wrappers.py | 2 + tests/utils.py | 3 +- 35 files changed, 151 insertions(+), 111 deletions(-) diff --git a/RAT/__init__.py b/RAT/__init__.py index f5b8981c..39d9a5d0 100644 --- a/RAT/__init__.py +++ b/RAT/__init__.py @@ -1,9 +1,10 @@ import os + +import RAT.models as models from RAT.classlist import ClassList from RAT.controls import set_controls from RAT.project import Project from RAT.run import run -import RAT.models as models __all__ = ["ClassList", "Project", "run", "set_controls", "models"] diff --git a/RAT/classlist.py b/RAT/classlist.py index a1eb7dcd..c56c888f 100644 --- a/RAT/classlist.py +++ b/RAT/classlist.py @@ -2,11 +2,12 @@ """ import collections -from collections.abc import Iterable, Sequence import contextlib -import prettytable -from typing import Any, Union import warnings +from collections.abc import Iterable, Sequence +from typing import Any, Union + +import prettytable class ClassList(collections.UserList): diff --git a/RAT/controls.py b/RAT/controls.py index bc4c0bad..cf298718 100644 --- a/RAT/controls.py +++ b/RAT/controls.py @@ -1,10 +1,11 @@ from dataclasses import dataclass, field -import prettytable -from pydantic import BaseModel, Field, field_validator, ValidationError from typing import Literal, Union -from RAT.utils.enums import Parallel, Procedures, Display, BoundHandling, Strategies +import prettytable +from pydantic import BaseModel, Field, ValidationError, field_validator + from RAT.utils.custom_errors import custom_pydantic_validation_error +from RAT.utils.enums import BoundHandling, Display, Parallel, Procedures, Strategies @dataclass(frozen=True) diff --git a/RAT/events.py b/RAT/events.py index c4bc84f6..de8cbcae 100644 --- a/RAT/events.py +++ b/RAT/events.py @@ -1,4 +1,5 @@ -from typing import Callable, Union, List +from typing import Callable, List, Union + from RAT.rat_core import EventBridge, EventTypes, PlotEventData, ProgressEventData @@ -55,7 +56,7 @@ def register(event_type: EventTypes, callback: Callable[[Union[str, PlotEventDat def clear() -> None: """Clears all event callbacks.""" __event_impl.clear() - for key in __event_callbacks.keys(): + for key in __event_callbacks: __event_callbacks[key] = set() diff --git a/RAT/examples/absorption/absorption.py b/RAT/examples/absorption/absorption.py index 5e44e363..61c4007c 100644 --- a/RAT/examples/absorption/absorption.py +++ b/RAT/examples/absorption/absorption.py @@ -1,9 +1,10 @@ """Custom layers model including absorption""" -import numpy as np import os import pathlib +import numpy as np + import RAT import RAT.utils.plotting diff --git a/RAT/examples/domains/domains_XY_model.py b/RAT/examples/domains/domains_XY_model.py index 89a53f67..403c7e50 100644 --- a/RAT/examples/domains/domains_XY_model.py +++ b/RAT/examples/domains/domains_XY_model.py @@ -1,4 +1,5 @@ import math + import numpy as np diff --git a/RAT/examples/languages/run_custom_file_languages.py b/RAT/examples/languages/run_custom_file_languages.py index 54c51c71..e6e0e0de 100644 --- a/RAT/examples/languages/run_custom_file_languages.py +++ b/RAT/examples/languages/run_custom_file_languages.py @@ -1,10 +1,11 @@ """Test custom function languages.""" -import RAT.utils.plotting - import pathlib -import setup_problem import time +import setup_problem + +import RAT.utils.plotting + path = pathlib.Path(__file__).parent.resolve() project = setup_problem.make_example_problem() diff --git a/RAT/examples/languages/setup_problem.py b/RAT/examples/languages/setup_problem.py index 91d18e10..d2e362fe 100644 --- a/RAT/examples/languages/setup_problem.py +++ b/RAT/examples/languages/setup_problem.py @@ -4,12 +4,14 @@ Example of using custom layers to model a DSPC supported bilayer. """ -import numpy as np import os import pathlib +import numpy as np + import RAT + def make_example_problem(): problem = RAT.Project(name="Orso lipid example - custom layers", model="custom layers", geometry="substrate/liquid") diff --git a/RAT/examples/non_polarised/DSPC_custom_XY.py b/RAT/examples/non_polarised/DSPC_custom_XY.py index 51c42b7f..33cb0061 100644 --- a/RAT/examples/non_polarised/DSPC_custom_XY.py +++ b/RAT/examples/non_polarised/DSPC_custom_XY.py @@ -24,10 +24,11 @@ where VFn is the Volume Fraction of the n'th layer. """ -import numpy as np import os import pathlib +import numpy as np + import RAT import RAT.utils.plotting diff --git a/RAT/examples/non_polarised/DSPC_custom_layers.py b/RAT/examples/non_polarised/DSPC_custom_layers.py index 508de14f..9ccd487d 100644 --- a/RAT/examples/non_polarised/DSPC_custom_layers.py +++ b/RAT/examples/non_polarised/DSPC_custom_layers.py @@ -3,10 +3,11 @@ Example of using custom layers to model a DSPC supported bilayer. """ -import numpy as np import os import pathlib +import numpy as np + import RAT import RAT.utils.plotting diff --git a/RAT/examples/non_polarised/DSPC_standard_layers.py b/RAT/examples/non_polarised/DSPC_standard_layers.py index 07d91fcc..77cb3c0d 100644 --- a/RAT/examples/non_polarised/DSPC_standard_layers.py +++ b/RAT/examples/non_polarised/DSPC_standard_layers.py @@ -1,9 +1,10 @@ """Standard Layers fit of a DSPC floating bilayer""" -import numpy as np import os import pathlib +import numpy as np + import RAT import RAT.utils.plotting diff --git a/RAT/examples/non_polarised/custom_XY_DSPC.py b/RAT/examples/non_polarised/custom_XY_DSPC.py index 57b1301b..f4bfffcd 100644 --- a/RAT/examples/non_polarised/custom_XY_DSPC.py +++ b/RAT/examples/non_polarised/custom_XY_DSPC.py @@ -1,4 +1,5 @@ import math + import numpy as np diff --git a/RAT/examples/non_polarised/custom_bilayer_DSPC.py b/RAT/examples/non_polarised/custom_bilayer_DSPC.py index 796eb4bb..ead61e0c 100644 --- a/RAT/examples/non_polarised/custom_bilayer_DSPC.py +++ b/RAT/examples/non_polarised/custom_bilayer_DSPC.py @@ -1,5 +1,6 @@ import numpy as np + def custom_bilayer_DSPC(params, bulk_in, bulk_out, contrast): """ CUSTOMBILAYER RAT Custom Layer Model File. diff --git a/RAT/inputs.py b/RAT/inputs.py index b19c6898..795735eb 100644 --- a/RAT/inputs.py +++ b/RAT/inputs.py @@ -6,10 +6,9 @@ import RAT import RAT.controls -from RAT.utils.enums import Calculations, Languages, LayerModels, TypeOptions import RAT.wrappers - from RAT.rat_core import Cells, Checks, Control, Limits, Priors, ProblemDefinition +from RAT.utils.enums import Calculations, Languages, LayerModels, TypeOptions def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, @@ -225,7 +224,7 @@ def check_indices(problem: ProblemDefinition) -> None: } # Check the indices -- note we have switched to 1-based indexing at this point - for params in index_list.keys(): + for params in index_list: param_list = getattr(problem, params) if len(param_list) > 0 and not all((element > 0 or element == -1) and element <= len(param_list) for element in getattr(problem, index_list[params])): diff --git a/RAT/models.py b/RAT/models.py index 7970363b..8c858645 100644 --- a/RAT/models.py +++ b/RAT/models.py @@ -1,10 +1,11 @@ """The models module. Contains the pydantic models used by RAT to store project parameters.""" -import numpy as np -from pydantic import BaseModel, Field, ValidationInfo, field_validator, model_validator import pathlib from typing import Any, Union +import numpy as np +from pydantic import BaseModel, Field, ValidationInfo, field_validator, model_validator + from RAT.utils.enums import BackgroundActions, Hydration, Languages, Priors, TypeOptions try: diff --git a/RAT/outputs.py b/RAT/outputs.py index fee942a8..bc0daa30 100644 --- a/RAT/outputs.py +++ b/RAT/outputs.py @@ -1,10 +1,12 @@ """Converts outputs from the compiled RAT code to python dataclasses""" from dataclasses import dataclass -import numpy as np from typing import Optional, Union -from RAT.utils.enums import Procedures + +import numpy as np + import RAT.rat_core +from RAT.utils.enums import Procedures @dataclass diff --git a/RAT/project.py b/RAT/project.py index 8166419f..f1fa8664 100644 --- a/RAT/project.py +++ b/RAT/project.py @@ -3,17 +3,17 @@ import collections import copy import functools -import numpy as np import os -from pydantic import BaseModel, ValidationInfo, field_validator, model_validator, ValidationError from typing import Any, Callable -from RAT.classlist import ClassList +import numpy as np +from pydantic import BaseModel, ValidationError, ValidationInfo, field_validator, model_validator + import RAT.models +from RAT.classlist import ClassList from RAT.utils.custom_errors import custom_pydantic_validation_error from RAT.utils.enums import Calculations, Geometries, LayerModels, Priors, TypeOptions - # Map project fields to pydantic models model_in_classlist = {'parameters': 'Parameter', 'bulk_in': 'Parameter', @@ -157,24 +157,24 @@ def model_post_init(self, __context: Any) -> None: control revalidation. """ # Ensure all ClassLists have the correct _class_handle defined - layer_field = getattr(self, 'layers') + layer_field = self.layers if not hasattr(layer_field, "_class_handle"): if self.absorption: - setattr(layer_field, "_class_handle", getattr(RAT.models, 'AbsorptionLayer')) + layer_field._class_handle = RAT.models.AbsorptionLayer else: - setattr(layer_field, "_class_handle", getattr(RAT.models, 'Layer')) + layer_field._class_handle = RAT.models.Layer - contrast_field = getattr(self, 'contrasts') + contrast_field = self.contrasts if not hasattr(contrast_field, "_class_handle"): if self.calculation == Calculations.Domains: - setattr(contrast_field, "_class_handle", getattr(RAT.models, 'ContrastWithRatio')) + contrast_field._class_handle = RAT.models.ContrastWithRatio else: - setattr(contrast_field, "_class_handle", getattr(RAT.models, 'Contrast')) + contrast_field._class_handle = RAT.models.Contrast for field_name, model in model_in_classlist.items(): field = getattr(self, field_name) if not hasattr(field, "_class_handle"): - setattr(field, "_class_handle", getattr(RAT.models, model)) + field._class_handle = getattr(RAT.models, model) if 'Substrate Roughness' not in self.parameters.get_names(): self.parameters.insert(0, RAT.models.ProtectedParameter(name='Substrate Roughness', min=1.0, value=3.0, @@ -231,7 +231,7 @@ def set_layers(self) -> 'Project': def set_calculation(self) -> 'Project': """Apply the calc setting to the project.""" contrast_list = [] - handle = getattr(self.contrasts, '_class_handle').__name__ + handle = self.contrasts._class_handle.__name__ if self.calculation == Calculations.Domains and handle == 'Contrast': for contrast in self.contrasts: contrast_list.append(RAT.models.ContrastWithRatio(**contrast.model_dump())) @@ -239,14 +239,14 @@ def set_calculation(self) -> 'Project': self.domain_ratios.data = [RAT.models.Parameter(name='Domain Ratio 1', min=0.4, value=0.5, max=0.6, fit=False, prior_type=RAT.models.Priors.Uniform, mu=0.0, sigma=np.inf)] - setattr(self.contrasts, '_class_handle', getattr(RAT.models, 'ContrastWithRatio')) + self.contrasts._class_handle = RAT.models.ContrastWithRatio elif self.calculation != Calculations.Domains and handle == 'ContrastWithRatio': for contrast in self.contrasts: contrast_params = contrast.model_dump() del contrast_params['domain_ratio'] contrast_list.append(RAT.models.Contrast(**contrast_params)) self.contrasts.data = contrast_list - setattr(self.contrasts, '_class_handle', getattr(RAT.models, 'Contrast')) + self.contrasts._class_handle = RAT.models.Contrast return self @model_validator(mode='after') @@ -282,25 +282,25 @@ def check_contrast_model_length(self) -> 'Project': def set_absorption(self) -> 'Project': """Apply the absorption setting to the project.""" layer_list = [] - handle = getattr(self.layers, '_class_handle').__name__ + handle = self.layers._class_handle.__name__ if self.absorption and handle == 'Layer': for layer in self.layers: layer_list.append(RAT.models.AbsorptionLayer(**layer.model_dump())) self.layers.data = layer_list - setattr(self.layers, '_class_handle', getattr(RAT.models, 'AbsorptionLayer')) + self.layers._class_handle = RAT.models.AbsorptionLayer elif not self.absorption and handle == 'AbsorptionLayer': for layer in self.layers: layer_params = layer.model_dump() del layer_params['SLD_imaginary'] layer_list.append(RAT.models.Layer(**layer_params)) self.layers.data = layer_list - setattr(self.layers, '_class_handle', getattr(RAT.models, 'Layer')) + self.layers._class_handle = RAT.models.Layer return self @model_validator(mode='after') def update_renamed_models(self) -> 'Project': """When models defined in the ClassLists are renamed, we need to update that name elsewhere in the project.""" - for class_list in model_names_used_in.keys(): + for class_list in model_names_used_in: old_names = self._all_names[class_list] new_names = getattr(self, class_list).get_names() if len(old_names) == len(new_names): @@ -420,7 +420,7 @@ def check_contrast_model_allowed_values(self, contrast_attribute: str, allowed_v """ class_list = getattr(self, contrast_attribute) for contrast in class_list: - model_values = getattr(contrast, 'model') + model_values = contrast.model if model_values and not all(value in allowed_values for value in model_values): raise ValueError(f'The values: "{", ".join(str(i) for i in model_values)}" in the "model" field of ' f'"{contrast_attribute}" must be defined in "{allowed_field}".') @@ -499,17 +499,17 @@ def wrapped_func(*args, **kwargs): """Run the given function and then revalidate the "Project" model. If any exception is raised, restore the previous state of the given ClassList and report details of the exception. """ - previous_state = copy.deepcopy(getattr(class_list, 'data')) + previous_state = copy.deepcopy(class_list.data) return_value = None try: return_value = func(*args, **kwargs) Project.model_validate(self) except ValidationError as exc: - setattr(class_list, 'data', previous_state) + class_list.data = previous_state custom_error_list = custom_pydantic_validation_error(exc.errors()) raise ValidationError.from_exception_data(exc.title, custom_error_list) from None except (TypeError, ValueError): - setattr(class_list, 'data', previous_state) + class_list.data = previous_state raise finally: del previous_state diff --git a/RAT/run.py b/RAT/run.py index 52c0d82c..311d0fa1 100644 --- a/RAT/run.py +++ b/RAT/run.py @@ -1,6 +1,6 @@ +import RAT.rat_core from RAT.inputs import make_input from RAT.outputs import make_results -import RAT.rat_core def run(project, controls): @@ -25,6 +25,6 @@ def run(project, controls): # Update parameter values in project for class_list in RAT.project.parameter_class_lists: for (index, value) in enumerate(getattr(problem_definition, parameter_field[class_list])): - setattr(getattr(project, class_list)[index], 'value', value) + getattr(project, class_list)[index].value = value return project, results diff --git a/RAT/utils/enums.py b/RAT/utils/enums.py index c1f8e395..0e91eb15 100644 --- a/RAT/utils/enums.py +++ b/RAT/utils/enums.py @@ -1,4 +1,5 @@ from enum import Enum + try: from enum import StrEnum except ImportError: diff --git a/RAT/utils/plotting.py b/RAT/utils/plotting.py index 5cbc72f2..45b92c10 100644 --- a/RAT/utils/plotting.py +++ b/RAT/utils/plotting.py @@ -2,14 +2,15 @@ Plots using the matplotlib library """ from typing import Optional, Union -from matplotlib.axes._axes import Axes + import matplotlib.pyplot as plt import numpy as np -from RAT.rat_core import PlotEventData, makeSLDProfileXY +from matplotlib.axes._axes import Axes import RAT import RAT.inputs import RAT.outputs +from RAT.rat_core import PlotEventData, makeSLDProfileXY class Figure: diff --git a/RAT/wrappers.py b/RAT/wrappers.py index 87404efa..320502bb 100644 --- a/RAT/wrappers.py +++ b/RAT/wrappers.py @@ -1,7 +1,9 @@ import pathlib from typing import Callable, Tuple + import numpy as np from numpy.typing import ArrayLike + import RAT.rat_core diff --git a/pyproject.toml b/pyproject.toml index 1b050698..988bc3b0 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,3 +8,6 @@ build-backend = 'setuptools.build_meta' [tool.ruff] line-length = 120 + +[tool.ruff.lint] +select = ["E", "F", "UP", "B", "SIM", "I"] diff --git a/setup.py b/setup.py index af559726..670eb8ae 100644 --- a/setup.py +++ b/setup.py @@ -1,12 +1,12 @@ -from glob import glob -from pathlib import Path import platform import sys +from glob import glob +from pathlib import Path + import pybind11 -from setuptools import setup, Extension, find_packages -from setuptools.command.build_ext import build_ext +from setuptools import Extension, find_packages, setup from setuptools.command.build_clib import build_clib - +from setuptools.command.build_ext import build_ext __version__ = '0.0.0' @@ -33,6 +33,7 @@ # check whether compiler supports a flag def has_flag(compiler, flagname): import tempfile + from setuptools.errors import CompileError with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f: f.write('int main (int argc, char **argv) { return 0; }') diff --git a/tests/test_classlist.py b/tests/test_classlist.py index badf4f21..2d5c23e1 100644 --- a/tests/test_classlist.py +++ b/tests/test_classlist.py @@ -1,10 +1,11 @@ """Test the ClassList.""" -from collections.abc import Iterable, Sequence -import pytest import re -from typing import Any, Union import warnings +from collections.abc import Iterable, Sequence +from typing import Any, Union + +import pytest from RAT.classlist import ClassList from tests.utils import InputAttributes, SubInputAttributes @@ -41,7 +42,7 @@ def three_name_class_list(): return ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob'), InputAttributes(name='Eve')]) -class TestInitialisation(object): +class TestInitialisation: @pytest.mark.parametrize("input_object", [ (InputAttributes()), (InputAttributes(name='Alice')), diff --git a/tests/test_controls.py b/tests/test_controls.py index 27d48497..b6255c61 100644 --- a/tests/test_controls.py +++ b/tests/test_controls.py @@ -1,11 +1,12 @@ """Test the controls module.""" -import pytest +from typing import Any, Union + import pydantic -from typing import Union, Any +import pytest -from RAT.controls import Calculate, Simplex, DE, NS, Dream, set_controls -from RAT.utils.enums import Parallel, Procedures, Display, BoundHandling, Strategies +from RAT.controls import DE, NS, Calculate, Dream, Simplex, set_controls +from RAT.utils.enums import BoundHandling, Display, Parallel, Procedures, Strategies class TestCalculate: @@ -41,21 +42,21 @@ def test_calculate_property_setters(self, control_property: str, value: Any) -> def test_calculate_parallel_validation(self, value: Any) -> None: """Tests the parallel setter validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'parallel', value) + self.calculate.parallel = value assert exp.value.errors()[0]['msg'] == "Input should be 'single', 'points' or 'contrasts'" @pytest.mark.parametrize("value", [5.0, 12]) def test_calculate_calcSldDuringFit_validation(self, value: Union[int, float]) -> None: """Tests the calcSldDuringFit setter validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'calcSldDuringFit', value) + self.calculate.calcSldDuringFit = value assert exp.value.errors()[0]['msg'] == "Input should be a valid boolean, unable to interpret input" @pytest.mark.parametrize("value", ['test', 'iterate', "FINAL", True, 1, 3.0]) def test_calculate_display_validation(self, value: Any) -> None: """Tests the display setter validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'display', value) + self.calculate.display = value assert exp.value.errors()[0]['msg'] == "Input should be 'off', 'iter', 'notify' or 'final'" @pytest.mark.parametrize("value, msg", [ @@ -65,7 +66,7 @@ def test_calculate_display_validation(self, value: Any) -> None: def test_calculate_resampleParams_length_validation(self, value: list, msg: str) -> None: """Tests the resampleParams setter length validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'resampleParams', value) + self.calculate.resampleParams = value assert exp.value.errors()[0]['msg'] == msg @pytest.mark.parametrize("value, msg", [ @@ -75,13 +76,13 @@ def test_calculate_resampleParams_length_validation(self, value: list, msg: str) def test_calculate_resampleParams_value_validation(self, value: list, msg: str) -> None: """Tests the resampleParams setter value validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'resampleParams', value) + self.calculate.resampleParams = value assert exp.value.errors()[0]['msg'] == msg def test_calculate_extra_property_error(self) -> None: """Tests the extra property setter in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'test', 1) + self.calculate.test = 1 assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" def test_calculate_initialise_procedure_error(self) -> None: @@ -93,7 +94,7 @@ def test_calculate_initialise_procedure_error(self) -> None: def test_calculate_set_procedure_error(self) -> None: """Tests the procedure property is frozen in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.calculate, 'procedure', 'test') + self.calculate.procedure = "test" assert exp.value.errors()[0]['msg'] == "Input should be " def test_repr(self) -> None: @@ -169,7 +170,7 @@ def test_simplex_property_errors(self, control_property: str, value: Union[floa def test_simplex_extra_property_error(self) -> None: """Tests the extra property setter in Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.simplex, 'test', 1) + self.simplex.test = 1 assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" def test_simplex_initialise_procedure_error(self) -> None: @@ -181,7 +182,7 @@ def test_simplex_initialise_procedure_error(self) -> None: def test_simplex_set_procedure_error(self) -> None: """Tests the procedure property is frozen in Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.simplex, 'procedure', 'test') + self.simplex.procedure = "test" assert exp.value.errors()[0]['msg'] == "Input should be " def test_repr(self) -> None: @@ -255,7 +256,7 @@ def test_de_property_setters(self, control_property: str, value: Any) -> None: def test_de_crossoverProbability_error(self, value: int, msg: str) -> None: """Tests the crossoverProbability setter error in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.de, 'crossoverProbability', value) + self.de.crossoverProbability = value assert exp.value.errors()[0]['msg'] == msg @pytest.mark.parametrize("control_property, value", [ @@ -277,7 +278,7 @@ def test_de_targetValue_numGenerations_populationSize_error(self, def test_de_extra_property_error(self) -> None: """Tests the extra property setter in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.de, 'test', 1) + self.de.test = 1 assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" def test_de_initialise_procedure_error(self) -> None: @@ -289,7 +290,7 @@ def test_de_initialise_procedure_error(self) -> None: def test_de_set_procedure_error(self) -> None: """Tests the procedure property is frozen in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.de, 'procedure', 'test') + self.de.procedure = "test" assert exp.value.errors()[0]['msg'] == "Input should be " def test_repr(self) -> None: @@ -370,13 +371,13 @@ def test_ns_setter_error(self, control_property: str, value: Union[int, float], def test_ns_propScale_error(self, value: int, msg: str) -> None: """Tests the propScale error in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.ns, 'propScale', value) + self.ns.propScale = value assert exp.value.errors()[0]['msg'] == msg def test_ns_extra_property_error(self) -> None: """Tests the extra property setter in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.ns, 'test', 1) + self.ns.test = 1 assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" def test_ns_initialise_procedure_error(self) -> None: @@ -388,7 +389,7 @@ def test_ns_initialise_procedure_error(self) -> None: def test_ns_procedure_error(self) -> None: """Tests the procedure property is frozen in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.ns, 'procedure', 'test') + self.ns.procedure = "test" assert exp.value.errors()[0]['msg'] == "Input should be " def test_control_class_ns_repr(self) -> None: @@ -467,20 +468,20 @@ def test_dream_jumpProbability_pUnitGamma_error(self, control_property: str, val def test_dream_nSamples_error(self, value: int) -> None: """Tests the nSamples setter error in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.dream, 'nSamples', value) + self.dream.nSamples = value assert exp.value.errors()[0]['msg'] == "Input should be greater than or equal to 0" @pytest.mark.parametrize("value", [-5, 0]) def test_dream_nChains_error(self, value: int) -> None: """Tests the nChains setter error in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.dream, 'nChains', value) + self.dream.nChains = value assert exp.value.errors()[0]['msg'] == "Input should be greater than 0" def test_dream_extra_property_error(self) -> None: """Tests the extra property setter in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.dream, 'test', 1) + self.dream.test = 1 assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" def test_dream_initialise_procedure_error(self) -> None: @@ -492,7 +493,7 @@ def test_dream_initialise_procedure_error(self) -> None: def test_dream_procedure_error(self) -> None: """Tests the procedure property is frozen in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: - setattr(self.dream, 'procedure', 'test') + self.dream.procedure = "test" assert exp.value.errors()[0]['msg'] == "Input should be " def test_control_class_dream_repr(self) -> None: diff --git a/tests/test_custom_errors.py b/tests/test_custom_errors.py index 6c8bdabb..c6898cac 100644 --- a/tests/test_custom_errors.py +++ b/tests/test_custom_errors.py @@ -1,8 +1,9 @@ """Test the utils.custom_errors module.""" -from pydantic import create_model, ValidationError -import pytest import re +import pytest +from pydantic import ValidationError, create_model + import RAT.utils.custom_errors diff --git a/tests/test_events.py b/tests/test_events.py index 11c336c2..6308438d 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -1,5 +1,7 @@ import unittest.mock as mock + import pytest + import RAT.events diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 2aecb581..5877bf0b 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -1,19 +1,27 @@ """Test the inputs module.""" +import pathlib +import unittest.mock as mock from itertools import chain + import numpy as np -import pathlib import pytest -import unittest.mock as mock import RAT -from RAT.inputs import make_input, make_problem, make_cells, make_controls, check_indices -from RAT.utils.enums import (BoundHandling, Calculations, Display, Geometries, LayerModels, Parallel, Procedures, - TypeOptions) import RAT.wrappers -from tests.utils import dummy_function - +from RAT.inputs import check_indices, make_cells, make_controls, make_input, make_problem from RAT.rat_core import Cells, Checks, Control, Limits, Priors, ProblemDefinition +from RAT.utils.enums import ( + BoundHandling, + Calculations, + Display, + Geometries, + LayerModels, + Parallel, + Procedures, + TypeOptions, +) +from tests.utils import dummy_function @pytest.fixture diff --git a/tests/test_models.py b/tests/test_models.py index 94fd795f..77148ed2 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -1,10 +1,11 @@ """Test the pydantic models.""" +import re +from typing import Callable + import numpy as np import pydantic import pytest -import re -from typing import Callable import RAT.models @@ -47,7 +48,7 @@ def test_default_names(model: Callable, model_name: str, model_params: dict) -> (RAT.models.Parameter, {}), (RAT.models.Resolution, {}), ]) -class TestModels(object): +class TestModels: def test_initialise_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When initialising a model with the wrong type for the "name" field, we should raise a ValidationError.""" with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n Input should be a valid string'): diff --git a/tests/test_outputs.py b/tests/test_outputs.py index f5e79417..c96f845c 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -10,7 +10,6 @@ import RAT.outputs import RAT.rat_core from RAT.utils.enums import Procedures - from tests.utils import check_results_equal diff --git a/tests/test_plotting.py b/tests/test_plotting.py index f3980a2b..652281f4 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -1,14 +1,14 @@ import os -import re -import pytest import pickle -from unittest.mock import patch -from unittest.mock import MagicMock +import re +from unittest.mock import MagicMock, patch + import matplotlib.pyplot as plt +import pytest + from RAT.rat_core import PlotEventData from RAT.utils.plotting import Figure, plot_ref_sld_helper - TEST_DIR_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'test_data') diff --git a/tests/test_project.py b/tests/test_project.py index 3342b633..84220c5e 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -1,18 +1,18 @@ """Test the project module.""" import copy -import numpy as np -import pydantic import os -import pytest import shutil -from typing import Callable import tempfile +from typing import Callable + +import numpy as np +import pydantic +import pytest import RAT from RAT.utils.enums import Calculations, LayerModels - layer_params = {'thickness': 'Test Thickness', 'SLD': 'Test SLD', 'roughness': 'Test Roughness'} absorption_layer_params = {'thickness': 'Test Thickness', 'SLD_real': 'Test SLD', 'SLD_imaginary': 'Test SLD', 'roughness': 'Test Roughness'} @@ -238,7 +238,7 @@ def test_assign_wrong_layers(wrong_input_model: Callable, model_params: dict, ab with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\nlayers\n Value error, ' f'"layers" ClassList contains objects other than ' f'"{actual_model_name}"'): - setattr(project, 'layers', RAT.ClassList(wrong_input_model(**model_params))) + project.layers = RAT.ClassList(wrong_input_model(**model_params)) @pytest.mark.parametrize(["wrong_input_model", "calculation", "actual_model_name"], [ @@ -251,7 +251,7 @@ def test_assign_wrong_contrasts(wrong_input_model: Callable, calculation: Calcul with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\ncontrasts\n Value error, ' f'"contrasts" ClassList contains objects other than ' f'"{actual_model_name}"'): - setattr(project, 'contrasts', RAT.ClassList(wrong_input_model())) + project.contrasts = RAT.ClassList(wrong_input_model()) @pytest.mark.parametrize(["field", "model_params"], [ @@ -699,7 +699,7 @@ def test_write_script(test_project, temp_dir, test_project_script, input_filenam assert os.path.isfile(script_path) # Test the contents of the file are as expected - with open(script_path, 'r') as f: + with open(script_path) as f: script = f.read() assert script == test_project_script diff --git a/tests/test_run.py b/tests/test_run.py index 3fcaac2b..41f0cfae 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -4,15 +4,15 @@ We use the example for both a reflectivity calculation, and Bayesian analysis using the Dream algorithm. """ +import unittest.mock as mock + import numpy as np import pytest -import unittest.mock as mock import RAT import RAT.outputs import RAT.rat_core from RAT.utils.enums import Calculations, Geometries, LayerModels, Procedures - from tests.utils import check_results_equal diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index fc8ef7a3..0eda2727 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -1,6 +1,8 @@ import pathlib import unittest.mock as mock + import pytest + import RAT.wrappers diff --git a/tests/utils.py b/tests/utils.py index 18a4c376..f26449bd 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,6 +1,7 @@ -import numpy as np from typing import Any +import numpy as np + import RAT.outputs From 45453355fb477cef71c5abc8753d976d5e3f8c7b Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:32:41 +0100 Subject: [PATCH 03/11] Resolves most manually fixable linting errors on advanced rule set --- RAT/classlist.py | 6 +- RAT/examples/languages/setup_problem.py | 3 +- .../non_polarised/DSPC_custom_layers.py | 3 +- .../non_polarised/DSPC_standard_layers.py | 66 ++++++++++++------- RAT/inputs.py | 12 ++-- RAT/models.py | 4 +- RAT/project.py | 8 +-- RAT/utils/plotting.py | 3 +- RAT/wrappers.py | 2 +- pyproject.toml | 1 + setup.py | 4 +- tests/test_classlist.py | 4 +- tests/test_inputs.py | 18 +++-- tests/test_models.py | 12 ++-- tests/test_run.py | 6 +- tests/test_wrappers.py | 6 +- 16 files changed, 99 insertions(+), 59 deletions(-) diff --git a/RAT/classlist.py b/RAT/classlist.py index c56c888f..e2684f20 100644 --- a/RAT/classlist.py +++ b/RAT/classlist.py @@ -57,7 +57,7 @@ def __repr__(self): else: if any(model.__dict__ for model in self.data): table = prettytable.PrettyTable() - table.field_names = ['index'] + [key.replace('_', ' ') for key in self.data[0].__dict__.keys()] + table.field_names = ['index'] + [key.replace('_', ' ') for key in self.data[0].__dict__] table.add_rows([[index] + list(model.__dict__.values()) for index, model in enumerate(self.data)]) output = table.get_string() else: @@ -133,7 +133,7 @@ def append(self, obj: object = None, **kwargs) -> None: """ if obj and kwargs: warnings.warn('ClassList.append() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning) + 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) if obj: if not hasattr(self, '_class_handle'): self._class_handle = type(obj) @@ -173,7 +173,7 @@ def insert(self, index: int, obj: object = None, **kwargs) -> None: """ if obj and kwargs: warnings.warn('ClassList.insert() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning) + 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) if obj: if not hasattr(self, '_class_handle'): self._class_handle = type(obj) diff --git a/RAT/examples/languages/setup_problem.py b/RAT/examples/languages/setup_problem.py index d2e362fe..33313e03 100644 --- a/RAT/examples/languages/setup_problem.py +++ b/RAT/examples/languages/setup_problem.py @@ -29,7 +29,8 @@ def make_example_problem(): problem.parameters.set_fields(0, min=1.0, max=10.0) - # Need to add the relevant Bulk SLDs. Change the bulk in from air to silicon, and add two additional water contrasts: + # Need to add the relevant Bulk SLDs. + # Change the bulk in from air to silicon, and add two additional water contrasts: problem.bulk_in.set_fields(0, name="Silicon", min=2.07e-6, value=2.073e-6, max=2.08e-6, fit=False) problem.bulk_out.append(name="SLD SMW", min=1.0e-6, value=2.073e-6, max=3.0e-6, fit=True) diff --git a/RAT/examples/non_polarised/DSPC_custom_layers.py b/RAT/examples/non_polarised/DSPC_custom_layers.py index 9ccd487d..b279eb1a 100644 --- a/RAT/examples/non_polarised/DSPC_custom_layers.py +++ b/RAT/examples/non_polarised/DSPC_custom_layers.py @@ -53,7 +53,8 @@ path=pathlib.Path(__file__).parent.resolve()) # Also, add the relevant background parameters - one each for each contrast: -problem.background_parameters.set_fields(0, name="Background parameter D2O", fit=True, min=1.0e-10, max=1.0e-5, value=1.0e-07) +problem.background_parameters.set_fields(0, name="Background parameter D2O", min=1.0e-10, max=1.0e-5, + value=1.0e-07, fit=True) problem.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=1.0e-7, max=1.0e-5, fit=True) problem.background_parameters.append(name="Background parameter H2O", min=1.0e-10, value=1.0e-7, max=1.0e-5, fit=True) diff --git a/RAT/examples/non_polarised/DSPC_standard_layers.py b/RAT/examples/non_polarised/DSPC_standard_layers.py index 77cb3c0d..6212fff2 100644 --- a/RAT/examples/non_polarised/DSPC_standard_layers.py +++ b/RAT/examples/non_polarised/DSPC_standard_layers.py @@ -12,26 +12,46 @@ geometry="substrate/liquid", absorption=False) # Set up the relevant parameters -problem.parameters.append(name="Oxide Thickness", min=5.0, value=19.54, max=60.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Oxide SLD", min=3.39e-06, value=3.39e-06, max=3.41e-06, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Tails Thickness", min=15.0, value=22.66, max=35.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Tails SLD", min=-5e-07, value=-4.01e-07, max=-3e-07, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Tails Hydration", min=1.0, value=5.252, max=50.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Roughness", min=1.0, value=5.64, max=15.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="CW Thickness", min=10.0, value=17.12, max=28.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="CW SLD", min=0.0, value=0.0, max=1e-09, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Heads Thickness", min=5.0, value=8.56, max=17.0, fit=True, prior_type="gaussian", mu=10.0, sigma=2.0) -problem.parameters.append(name="SAM Heads SLD", min=1.0e-07, value=1.75e-06, max=2.0e-06, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Heads Hydration", min=10.0, value=45.45, max=50.0, fit=True, prior_type="uniform", mu=30.0, sigma=3.0) -problem.parameters.append(name="Bilayer Heads Thickness", min=7.0, value=10.7, max=17.0, fit=True, prior_type="gaussian", mu=10.0, sigma=2.0) -problem.parameters.append(name="Bilayer Heads SLD", min=5.0e-07, value=1.47e-06, max=1.5e-06, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Roughness", min=2.0, value=6.014, max=15.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Tails Thickness", min=14.0, value=17.82, max=22.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Tails SLD", min=-5.0e-07, value=-4.61e-07, max=0.0, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Tails Hydration", min=10.0, value=17.64, max=50.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Heads Hydration", min=10.0, value=36.15, max=50.0, fit=True, prior_type="gaussian", mu=30.0, sigma=3.0) -problem.parameters.append(name="CW Hydration", min=99.9, value=100.0, max=100.0, fit=False, prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Oxide Hydration", min=0.0, value=23.61, max=60.0, fit=True, prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="Oxide Thickness", min=5.0, value=19.54, max=60.0, fit=True, prior_type="uniform", + mu=0.0, sigma=np.inf) +problem.parameters.append(name="Oxide SLD", min=3.39e-06, value=3.39e-06, max=3.41e-06, fit=False, prior_type="uniform", + mu=0.0, sigma=np.inf) +problem.parameters.append(name="SAM Tails Thickness", min=15.0, value=22.66, max=35.0, fit=True, prior_type="uniform", + mu=0.0, sigma=np.inf) +problem.parameters.append(name="SAM Tails SLD", min=-5e-07, value=-4.01e-07, max=-3e-07, fit=False, + prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="SAM Tails Hydration", min=1.0, value=5.252, max=50.0, fit=True, prior_type="uniform", + mu=0.0, sigma=np.inf) +problem.parameters.append(name="SAM Roughness", min=1.0, value=5.64, max=15.0, fit=True, prior_type="uniform", mu=0.0, + sigma=np.inf) +problem.parameters.append(name="CW Thickness", min=10.0, value=17.12, max=28.0, fit=True, prior_type="uniform", mu=0.0, + sigma=np.inf) +problem.parameters.append(name="CW SLD", min=0.0, value=0.0, max=1e-09, fit=False, prior_type="uniform", mu=0.0, + sigma=np.inf) +problem.parameters.append(name="SAM Heads Thickness", min=5.0, value=8.56, max=17.0, fit=True, prior_type="gaussian", + mu=10.0, sigma=2.0) +problem.parameters.append(name="SAM Heads SLD", min=1.0e-07, value=1.75e-06, max=2.0e-06, fit=False, + prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="SAM Heads Hydration", min=10.0, value=45.45, max=50.0, fit=True, prior_type="uniform", + mu=30.0, sigma=3.0) +problem.parameters.append(name="Bilayer Heads Thickness", min=7.0, value=10.7, max=17.0, fit=True, + prior_type="gaussian", mu=10.0, sigma=2.0) +problem.parameters.append(name="Bilayer Heads SLD", min=5.0e-07, value=1.47e-06, max=1.5e-06, fit=False, + prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="Bilayer Roughness", min=2.0, value=6.014, max=15.0, fit=True, prior_type="uniform", + mu=0.0, sigma=np.inf) +problem.parameters.append(name="Bilayer Tails Thickness", min=14.0, value=17.82, max=22.0, fit=True, + prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="Bilayer Tails SLD", min=-5.0e-07, value=-4.61e-07, max=0.0, fit=False, + prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="Bilayer Tails Hydration", min=10.0, value=17.64, max=50.0, fit=True, + prior_type="uniform", mu=0.0, sigma=np.inf) +problem.parameters.append(name="Bilayer Heads Hydration", min=10.0, value=36.15, max=50.0, fit=True, + prior_type="gaussian", mu=30.0, sigma=3.0) +problem.parameters.append(name="CW Hydration", min=99.9, value=100.0, max=100.0, fit=False, prior_type="uniform", + mu=0.0, sigma=np.inf) +problem.parameters.append(name="Oxide Hydration", min=0.0, value=23.61, max=60.0, fit=True, prior_type="uniform", + mu=0.0, sigma=np.inf) problem.parameters.set_fields(0, max=10) @@ -70,8 +90,10 @@ # Now deal with the backgrounds del problem.backgrounds[0] del problem.background_parameters[0] -problem.background_parameters.append(name="Background parameter D2O", min=5.0e-10, value=2.23e-06, max=7.0e-06, fit=True) -problem.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=3.38e-06, max=4.99e-06, fit=True) +problem.background_parameters.append(name="Background parameter D2O", min=5.0e-10, value=2.23e-06, max=7.0e-06, + fit=True) +problem.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=3.38e-06, max=4.99e-06, + fit=True) problem.backgrounds.append(name="D2O Background", type="constant", value_1="Background parameter D2O") problem.backgrounds.append(name="SMW Background", type="constant", value_1="Background parameter SMW") diff --git a/RAT/inputs.py b/RAT/inputs.py index 795735eb..96f947ad 100644 --- a/RAT/inputs.py +++ b/RAT/inputs.py @@ -14,7 +14,8 @@ def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, RAT.controls.NS, RAT.controls.Dream] ) -> tuple[ProblemDefinition, Cells, Limits, Priors, Control]: - """Constructs the inputs required for the compiled RAT code using the data defined in the input project and controls. + """Constructs the inputs required for the compiled RAT code using the data defined in the input project and + controls. Parameters ---------- @@ -147,7 +148,8 @@ def make_problem(project: RAT.Project) -> ProblemDefinition: problem.contrastBulkIns = [project.bulk_in.index(contrast.bulk_in, True) for contrast in project.contrasts] problem.contrastBulkOuts = [project.bulk_out.index(contrast.bulk_out, True) for contrast in project.contrasts] problem.contrastQzshifts = [0] * len(project.contrasts) # This is marked as "to do" in RAT - problem.contrastScalefactors = [project.scalefactors.index(contrast.scalefactor, True) for contrast in project.contrasts] + problem.contrastScalefactors = [project.scalefactors.index(contrast.scalefactor, True) + for contrast in project.contrasts] problem.contrastDomainRatios = [project.domain_ratios.index(contrast.domain_ratio, True) if hasattr(contrast, 'domain_ratio') else 0 for contrast in project.contrasts] problem.contrastBackgroundParams = contrast_background_params @@ -203,7 +205,8 @@ def make_data_present(project: RAT.Project) -> list[int]: : list[int] The "dataPresent" field of the problem input used in the compiled RAT code. """ - return [1 if project.data[project.data.index(contrast.data)].data.size != 0 else 0 for contrast in project.contrasts] + return [1 if project.data[project.data.index(contrast.data)].data.size != 0 else 0 + for contrast in project.contrasts] def check_indices(problem: ProblemDefinition) -> None: @@ -257,7 +260,8 @@ def make_cells(project: RAT.Project) -> Cells: # Set contrast parameters according to model type if project.model == LayerModels.StandardLayers: if project.calculation == Calculations.Domains: - contrast_models = [[project.domain_contrasts.index(domain_contrast, True) for domain_contrast in contrast.model] + contrast_models = [[project.domain_contrasts.index(domain_contrast, True) + for domain_contrast in contrast.model] for contrast in project.contrasts] else: contrast_models = [[project.layers.index(layer, True) for layer in contrast.model] diff --git a/RAT/models.py b/RAT/models.py index 8c858645..993b2079 100644 --- a/RAT/models.py +++ b/RAT/models.py @@ -125,10 +125,10 @@ def check_data_dimension(cls, data: np.ndarray[float]) -> np.ndarray[float]: try: data.shape[1] except IndexError: - raise ValueError('"data" must have at least two dimensions') + raise ValueError('"data" must have at least two dimensions') from None else: if data.shape[1] < 3: - raise ValueError('"data" must have at least three columns') + raise ValueError('"data" must have at least three columns') from None return data @field_validator('data_range', 'simulation_range') diff --git a/RAT/project.py b/RAT/project.py index f1fa8664..b57c7b64 100644 --- a/RAT/project.py +++ b/RAT/project.py @@ -263,8 +263,8 @@ def set_contrast_model_field(self) -> 'Project': @model_validator(mode='after') def check_contrast_model_length(self) -> 'Project': - """Given certain values of the "calculation" and "model" defined in the project, the "model" field of "contrasts" - may be constrained in its length. + """Given certain values of the "calculation" and "model" defined in the project, the "model" field of + "contrasts" may be constrained in its length. """ if self.model == LayerModels.StandardLayers and self.calculation == Calculations.Domains: for contrast in self.contrasts: @@ -357,11 +357,9 @@ def __repr__(self): if value: output += f'{key.replace("_", " ").title() + ": " :-<100}\n\n' try: - value.value # For enums + output += value.value + '\n\n' # For enums except AttributeError: output += repr(value) + '\n\n' - else: - output += value.value + '\n\n' return output def get_all_names(self): diff --git a/RAT/utils/plotting.py b/RAT/utils/plotting.py index 45b92c10..f4ac32be 100644 --- a/RAT/utils/plotting.py +++ b/RAT/utils/plotting.py @@ -202,7 +202,8 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay return fig -def plot_ref_sld(project: RAT.Project, results: Union[RAT.outputs.Results, RAT.outputs.BayesResults], block: bool = False): +def plot_ref_sld(project: RAT.Project, results: Union[RAT.outputs.Results, RAT.outputs.BayesResults], + block: bool = False): """ Plots the reflectivity and SLD profiles. diff --git a/RAT/wrappers.py b/RAT/wrappers.py index 320502bb..1b485887 100644 --- a/RAT/wrappers.py +++ b/RAT/wrappers.py @@ -20,7 +20,7 @@ def __init__(self, filename: str) -> None: try: import matlab.engine except ImportError: - raise ImportError('matlabengine is required to use MatlabWrapper') + raise ImportError('matlabengine is required to use MatlabWrapper') from None self.engine = matlab.engine.start_matlab() path = pathlib.Path(filename) self.engine.cd(str(path.parent), nargout=0) diff --git a/pyproject.toml b/pyproject.toml index 988bc3b0..b81afc73 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,3 +11,4 @@ line-length = 120 [tool.ruff.lint] select = ["E", "F", "UP", "B", "SIM", "I"] +ignore = ["SIM108"] diff --git a/setup.py b/setup.py index 670eb8ae..09fc46bd 100644 --- a/setup.py +++ b/setup.py @@ -77,11 +77,11 @@ def build_extensions(self): if '-Wstrict-prototypes' in self.compiler.compiler_so: self.compiler.compiler_so.remove('-Wstrict-prototypes') - opts.append('-DVERSION_INFO="%s"' % self.distribution.get_version()) + opts.append(f'-DVERSION_INFO="{self.distribution.get_version()}"') if has_flag(self.compiler, '-fvisibility=hidden'): opts.append('-fvisibility=hidden') elif ct == 'msvc': - opts.append('/DVERSION_INFO=\\"%s\\"' % self.distribution.get_version()) + opts.append(f'/DVERSION_INFO=\\"{self.distribution.get_version()}\\"') for ext in self.extensions: ext.extra_compile_args = opts ext.extra_link_args = link_opts diff --git a/tests/test_classlist.py b/tests/test_classlist.py index 2d5c23e1..a06cad10 100644 --- a/tests/test_classlist.py +++ b/tests/test_classlist.py @@ -279,7 +279,7 @@ def test_append_object_and_kwargs(two_name_class_list: ClassList, class_list = two_name_class_list with pytest.warns(SyntaxWarning): warnings.warn('ClassList.append() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning) + 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) class_list.append(new_object, **new_values) assert class_list == three_name_class_list @@ -362,7 +362,7 @@ def test_insert_object_and_kwargs(two_name_class_list: ClassList, class_list = two_name_class_list with pytest.warns(SyntaxWarning): warnings.warn('ClassList.insert() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning) + 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) class_list.insert(1, new_object, **new_values) assert class_list == ClassList([InputAttributes(name='Alice'), InputAttributes(name='Eve'), diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 5877bf0b..af781a03 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -27,13 +27,15 @@ @pytest.fixture def standard_layers_project(): """Add parameters to the default project for a non polarised calculation.""" - test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name='Test Data', data=np.array([[1.0, 1.0, 1.0]]))])) + test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name='Test Data', data=np.array([[1.0, 1.0, 1.0]]))]) + ) test_project.parameters.append(name='Test Thickness') test_project.parameters.append(name='Test SLD') test_project.parameters.append(name='Test Roughness') test_project.custom_files.append(name='Test Custom File', filename='python_test.py', function_name='dummy_function', language='python') - test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness') + test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness' + ) test_project.contrasts.append(name='Test Contrast', data='Test Data', background='Background 1', bulk_in='SLD Air', bulk_out='SLD D2O', scalefactor='Scalefactor 1', resolution='Resolution 1', model=['Test Layer']) @@ -44,12 +46,14 @@ def standard_layers_project(): def domains_project(): """Add parameters to the default project for a domains calculation.""" test_project = RAT.Project(calculation=Calculations.Domains, - data=RAT.ClassList([RAT.models.Data(name='Test Data', data=np.array([[1.0, 1.0, 1.0]]))])) + data=RAT.ClassList([RAT.models.Data(name='Test Data', data=np.array([[1.0, 1.0, 1.0]]))]) + ) test_project.parameters.append(name='Test Thickness') test_project.parameters.append(name='Test SLD') test_project.parameters.append(name='Test Roughness') test_project.custom_files.append(name='Test Custom File', filename='matlab_test.m', language='matlab') - test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness') + test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness' + ) test_project.domain_contrasts.append(name='up', model=['Test Layer']) test_project.domain_contrasts.append(name='down', model=['Test Layer']) test_project.contrasts.append(name='Test Contrast', data='Test Data', background='Background 1', bulk_in='SLD Air', @@ -457,7 +461,8 @@ def test_checks(): return checks -@pytest.mark.parametrize(["test_project", "test_problem", "test_cells", "test_limits", "test_priors", "test_controls"], [ +@pytest.mark.parametrize(["test_project", "test_problem", "test_cells", "test_limits", "test_priors", "test_controls"], + [ ("standard_layers_project", "standard_layers_problem", "standard_layers_cells", "non_polarised_limits", "non_polarised_priors", "standard_layers_controls"), ("custom_xy_project", "custom_xy_problem", "custom_xy_cells", "non_polarised_limits", "non_polarised_priors", @@ -612,7 +617,8 @@ def test_make_cells(test_project, test_cells, request) -> None: def test_get_python_handle(): path = pathlib.Path(__file__).parent.resolve() - assert RAT.inputs.get_python_handle("utils.py", "dummy_function", path).__code__ == dummy_function.__code__ + assert (RAT.inputs.get_python_handle("utils.py", "dummy_function", path).__code__ == + dummy_function.__code__) def test_make_controls(standard_layers_controls, test_checks) -> None: diff --git a/tests/test_models.py b/tests/test_models.py index 77148ed2..ad142467 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -51,23 +51,27 @@ def test_default_names(model: Callable, model_name: str, model_params: dict) -> class TestModels: def test_initialise_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When initialising a model with the wrong type for the "name" field, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n Input should be a valid string'): + with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n ' + f'Input should be a valid string'): model(name=1, **model_params) def test_assignment_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When assigning the "name" field of a model with the wrong type, we should raise a ValidationError.""" test_model = model(**model_params) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n Input should be a valid string'): + with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n ' + f'Input should be a valid string'): test_model.name = 1 def test_initialise_with_zero_length_name(self, model: Callable, model_params: dict) -> None: """When initialising a model with a zero length name, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n String should have at least 1 character'): + with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n ' + f'String should have at least 1 character'): model(name='', **model_params) def test_initialise_with_extra_fields(self, model: Callable, model_params: dict) -> None: """When initialising a model with unspecified fields, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nnew_field\n Extra inputs are not permitted'): + with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nnew_field\n ' + f'Extra inputs are not permitted'): model(new_field=1, **model_params) diff --git a/tests/test_run.py b/tests/test_run.py index 41f0cfae..885a6431 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -244,8 +244,10 @@ def dream_problem(): return problem -@pytest.mark.parametrize(["test_procedure", "test_output_problem", "test_output_results", "test_bayes", "test_results"], [ - (Procedures.Calculate, "reflectivity_calculation_problem", "reflectivity_calculation_output_results", None, "reflectivity_calculation_results"), +@pytest.mark.parametrize(["test_procedure", "test_output_problem", "test_output_results", "test_bayes", "test_results"], + [ + (Procedures.Calculate, "reflectivity_calculation_problem", "reflectivity_calculation_output_results", None, + "reflectivity_calculation_results"), (Procedures.Dream, "dream_problem", "dream_output_results", "dream_bayes", "dream_results"), ]) def test_run(test_procedure, test_output_problem, test_output_results, test_bayes, test_results, request) -> None: diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index 0eda2727..e4d02c0f 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -7,9 +7,9 @@ def test_matlab_wrapper() -> None: - with mock.patch.dict('sys.modules', {'matlab': mock.MagicMock(side_effect=ImportError)}): - with pytest.raises(ImportError): - RAT.wrappers.MatlabWrapper('demo.m') + with mock.patch.dict('sys.modules', {'matlab': mock.MagicMock(side_effect=ImportError)}), \ + pytest.raises(ImportError): + RAT.wrappers.MatlabWrapper('demo.m') mocked_matlab_module = mock.MagicMock() mocked_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_engine From 7a030dd4f06de9710fb48fab8a5722d270d8810d Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:41:11 +0100 Subject: [PATCH 04/11] Switches code to use double quotes --- RAT/__init__.py | 2 +- RAT/classlist.py | 34 +- RAT/controls.py | 12 +- .../languages/run_custom_file_languages.py | 4 +- RAT/inputs.py | 52 +- RAT/models.py | 128 +-- RAT/project.py | 272 +++---- RAT/run.py | 14 +- RAT/utils/custom_errors.py | 8 +- RAT/utils/enums.py | 74 +- RAT/utils/plotting.py | 24 +- RAT/wrappers.py | 14 +- setup.py | 126 +-- tests/conftest.py | 40 +- tests/test_classlist.py | 244 +++--- tests/test_controls.py | 302 +++---- tests/test_custom_errors.py | 8 +- tests/test_inputs.py | 176 ++--- tests/test_models.py | 54 +- tests/test_plotting.py | 42 +- tests/test_project.py | 746 +++++++++--------- tests/test_wrappers.py | 20 +- tests/utils.py | 4 +- 23 files changed, 1200 insertions(+), 1200 deletions(-) diff --git a/RAT/__init__.py b/RAT/__init__.py index 39d9a5d0..684d492f 100644 --- a/RAT/__init__.py +++ b/RAT/__init__.py @@ -9,4 +9,4 @@ __all__ = ["ClassList", "Project", "run", "set_controls", "models"] dir_path = os.path.dirname(os.path.realpath(__file__)) -os.environ["RAT_PATH"] = os.path.join(dir_path, '') +os.environ["RAT_PATH"] = os.path.join(dir_path, "") diff --git a/RAT/classlist.py b/RAT/classlist.py index e2684f20..ae77e90f 100644 --- a/RAT/classlist.py +++ b/RAT/classlist.py @@ -57,7 +57,7 @@ def __repr__(self): else: if any(model.__dict__ for model in self.data): table = prettytable.PrettyTable() - table.field_names = ['index'] + [key.replace('_', ' ') for key in self.data[0].__dict__] + table.field_names = ["index"] + [key.replace("_", " ") for key in self.data[0].__dict__] table.add_rows([[index] + list(model.__dict__.values()) for index, model in enumerate(self.data)]) output = table.get_string() else: @@ -82,15 +82,15 @@ def _delitem(self, index: int) -> None: """Auxiliary routine of "__delitem__" used to enable wrapping.""" del self.data[index] - def __iadd__(self, other: Sequence[object]) -> 'ClassList': + def __iadd__(self, other: Sequence[object]) -> "ClassList": """Define in-place addition using the "+=" operator.""" return self._iadd(other) - def _iadd(self, other: Sequence[object]) -> 'ClassList': + def _iadd(self, other: Sequence[object]) -> "ClassList": """Auxiliary routine of "__iadd__" used to enable wrapping.""" if other and not (isinstance(other, Sequence) and not isinstance(other, str)): other = [other] - if not hasattr(self, '_class_handle'): + if not hasattr(self, "_class_handle"): self._class_handle = self._determine_class_handle(self + other) self._check_classes(self + other) self._check_unique_name_fields(self + other) @@ -132,18 +132,18 @@ def append(self, obj: object = None, **kwargs) -> None: appended to the ClassList and the keyword arguments are discarded. """ if obj and kwargs: - warnings.warn('ClassList.append() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) + warnings.warn("ClassList.append() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) if obj: - if not hasattr(self, '_class_handle'): + if not hasattr(self, "_class_handle"): self._class_handle = type(obj) self._check_classes(self + [obj]) self._check_unique_name_fields(self + [obj]) self.data.append(obj) else: - if not hasattr(self, '_class_handle'): - raise TypeError('ClassList.append() called with keyword arguments for a ClassList without a class ' - 'defined. Call ClassList.append() with an object to define the class.') + if not hasattr(self, "_class_handle"): + raise TypeError("ClassList.append() called with keyword arguments for a ClassList without a class " + "defined. Call ClassList.append() with an object to define the class.") self._validate_name_field(kwargs) self.data.append(self._class_handle(**kwargs)) @@ -172,18 +172,18 @@ def insert(self, index: int, obj: object = None, **kwargs) -> None: inserted into the ClassList and the keyword arguments are discarded. """ if obj and kwargs: - warnings.warn('ClassList.insert() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) + warnings.warn("ClassList.insert() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) if obj: - if not hasattr(self, '_class_handle'): + if not hasattr(self, "_class_handle"): self._class_handle = type(obj) self._check_classes(self + [obj]) self._check_unique_name_fields(self + [obj]) self.data.insert(index, obj) else: - if not hasattr(self, '_class_handle'): - raise TypeError('ClassList.insert() called with keyword arguments for a ClassList without a class ' - 'defined. Call ClassList.insert() with an object to define the class.') + if not hasattr(self, "_class_handle"): + raise TypeError("ClassList.insert() called with keyword arguments for a ClassList without a class " + "defined. Call ClassList.insert() with an object to define the class.") self._validate_name_field(kwargs) self.data.insert(index, self._class_handle(**kwargs)) @@ -210,7 +210,7 @@ def extend(self, other: Sequence[object]) -> None: """Extend the ClassList by adding another sequence.""" if other and not (isinstance(other, Sequence) and not isinstance(other, str)): other = [other] - if not hasattr(self, '_class_handle'): + if not hasattr(self, "_class_handle"): self._class_handle = self._determine_class_handle(self + other) self._check_classes(self + other) self._check_unique_name_fields(self + other) diff --git a/RAT/controls.py b/RAT/controls.py index cf298718..6df2f3a2 100644 --- a/RAT/controls.py +++ b/RAT/controls.py @@ -45,7 +45,7 @@ class Controls: adaptPCR: bool = False -class Calculate(BaseModel, validate_assignment=True, extra='forbid'): +class Calculate(BaseModel, validate_assignment=True, extra="forbid"): """Defines the class for the calculate procedure, which includes the properties used in all five procedures.""" procedure: Literal[Procedures.Calculate] = Procedures.Calculate parallel: Parallel = Parallel.Single @@ -57,14 +57,14 @@ class Calculate(BaseModel, validate_assignment=True, extra='forbid'): @classmethod def check_resample_params(cls, resampleParams): if not 0 < resampleParams[0] < 1: - raise ValueError('resampleParams[0] must be between 0 and 1') + raise ValueError("resampleParams[0] must be between 0 and 1") if resampleParams[1] < 0: - raise ValueError('resampleParams[1] must be greater than or equal to 0') + raise ValueError("resampleParams[1] must be greater than or equal to 0") return resampleParams def __repr__(self) -> str: table = prettytable.PrettyTable() - table.field_names = ['Property', 'Value'] + table.field_names = ["Property", "Value"] table.add_rows([[k, v] for k, v in self.__dict__.items()]) return table.get_string() @@ -127,9 +127,9 @@ def set_controls(procedure: Procedures = Procedures.Calculate, **properties)\ except KeyError: members = list(Procedures.__members__.values()) allowed_values = f'{", ".join([repr(member.value) for member in members[:-1]])} or {members[-1].value!r}' - raise ValueError(f'The controls procedure must be one of: {allowed_values}') from None + raise ValueError(f"The controls procedure must be one of: {allowed_values}") from None except ValidationError as exc: - custom_error_msgs = {'extra_forbidden': f'Extra inputs are not permitted. The fields for the {procedure}' + custom_error_msgs = {"extra_forbidden": f'Extra inputs are not permitted. The fields for the {procedure}' f' controls procedure are:\n ' f'{", ".join(controls[procedure].model_fields.keys())}\n' } diff --git a/RAT/examples/languages/run_custom_file_languages.py b/RAT/examples/languages/run_custom_file_languages.py index e6e0e0de..d47cfe28 100644 --- a/RAT/examples/languages/run_custom_file_languages.py +++ b/RAT/examples/languages/run_custom_file_languages.py @@ -20,7 +20,7 @@ RAT.utils.plotting.plot_ref_sld(project, results) # Matlab -project.custom_files.set_fields(0, filename='custom_bilayer.m', language='matlab', path=path) +project.custom_files.set_fields(0, filename="custom_bilayer.m", language="matlab", path=path) start = time.time() project, results = RAT.run(project, controls) @@ -30,7 +30,7 @@ RAT.utils.plotting.plot_ref_sld(project, results) # C++ -project.custom_files.set_fields(0, filename='custom_bilayer.dll', language='cpp', path=path) +project.custom_files.set_fields(0, filename="custom_bilayer.dll", language="cpp", path=path) start = time.time() project, results = RAT.run(project, controls) diff --git a/RAT/inputs.py b/RAT/inputs.py index 96f947ad..22e168cd 100644 --- a/RAT/inputs.py +++ b/RAT/inputs.py @@ -38,24 +38,24 @@ def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT The controls object used in the compiled RAT code. """ - parameter_field = {'parameters': 'param', - 'bulk_in': 'bulkIn', - 'bulk_out': 'bulkOut', - 'scalefactors': 'scalefactor', - 'domain_ratios': 'domainRatio', - 'background_parameters': 'backgroundParam', - 'resolution_parameters': 'resolutionParam', + parameter_field = {"parameters": "param", + "bulk_in": "bulkIn", + "bulk_out": "bulkOut", + "scalefactors": "scalefactor", + "domain_ratios": "domainRatio", + "background_parameters": "backgroundParam", + "resolution_parameters": "resolutionParam", } - checks_field = {'parameters': 'fitParam', - 'bulk_in': 'fitBulkIn', - 'bulk_out': 'fitBulkOut', - 'scalefactors': 'fitScalefactor', - 'domain_ratios': 'fitDomainRatio', - 'background_parameters': 'fitBackgroundParam', - 'resolution_parameters': 'fitResolutionParam', + checks_field = {"parameters": "fitParam", + "bulk_in": "fitBulkIn", + "bulk_out": "fitBulkOut", + "scalefactors": "fitScalefactor", + "domain_ratios": "fitDomainRatio", + "background_parameters": "fitBackgroundParam", + "resolution_parameters": "fitResolutionParam", } - prior_id = {'uniform': 1, 'gaussian': 2, 'jeffreys': 3} + prior_id = {"uniform": 1, "gaussian": 2, "jeffreys": 3} problem = make_problem(project) cells = make_cells(project) @@ -103,11 +103,11 @@ def make_problem(project: RAT.Project) -> ProblemDefinition: problem : RAT.rat_core.ProblemDefinition The problem input used in the compiled RAT code. """ - action_id = {'add': 1, 'subtract': 2} + action_id = {"add": 1, "subtract": 2} # Set contrast parameters according to model type if project.model == LayerModels.StandardLayers: - contrast_custom_files = [float('NaN')] * len(project.contrasts) + contrast_custom_files = [float("NaN")] * len(project.contrasts) else: contrast_custom_files = [project.custom_files.index(contrast.model[0], True) for contrast in project.contrasts] @@ -151,7 +151,7 @@ def make_problem(project: RAT.Project) -> ProblemDefinition: problem.contrastScalefactors = [project.scalefactors.index(contrast.scalefactor, True) for contrast in project.contrasts] problem.contrastDomainRatios = [project.domain_ratios.index(contrast.domain_ratio, True) - if hasattr(contrast, 'domain_ratio') else 0 for contrast in project.contrasts] + if hasattr(contrast, "domain_ratio") else 0 for contrast in project.contrasts] problem.contrastBackgroundParams = contrast_background_params problem.contrastBackgroundActions = [action_id[contrast.background_action] for contrast in project.contrasts] problem.contrastResolutionParams = contrast_resolution_params @@ -218,12 +218,12 @@ def check_indices(problem: ProblemDefinition) -> None: problem : RAT.rat_core.ProblemDefinition The problem input used in the compiled RAT code. """ - index_list = {'bulkIn': 'contrastBulkIns', - 'bulkOut': 'contrastBulkOuts', - 'scalefactors': 'contrastScalefactors', - 'domainRatio': 'contrastDomainRatios', - 'backgroundParams': 'contrastBackgroundParams', - 'resolutionParams': 'contrastResolutionParams', + index_list = {"bulkIn": "contrastBulkIns", + "bulkOut": "contrastBulkOuts", + "scalefactors": "contrastScalefactors", + "domainRatio": "contrastDomainRatios", + "backgroundParams": "contrastBackgroundParams", + "resolutionParams": "contrastResolutionParams", } # Check the indices -- note we have switched to 1-based indexing at this point @@ -255,7 +255,7 @@ def make_cells(project: RAT.Project) -> Cells: The set of inputs that are defined in MATLAB as cell arrays. """ - hydrate_id = {'bulk in': 1, 'bulk out': 2} + hydrate_id = {"bulk in": 1, "bulk out": 2} # Set contrast parameters according to model type if project.model == LayerModels.StandardLayers: @@ -275,7 +275,7 @@ def make_cells(project: RAT.Project) -> Cells: layer_params = [project.parameters.index(getattr(layer, attribute), True) for attribute in list(layer.model_fields.keys())[1:-2]] - layer_params.append(project.parameters.index(layer.hydration, True) if layer.hydration else float('NaN')) + layer_params.append(project.parameters.index(layer.hydration, True) if layer.hydration else float("NaN")) layer_params.append(hydrate_id[layer.hydrate_with]) layer_details.append(layer_params) diff --git a/RAT/models.py b/RAT/models.py index 993b2079..dd3b4092 100644 --- a/RAT/models.py +++ b/RAT/models.py @@ -33,65 +33,65 @@ def int_sequence(): resolution_number = int_sequence() -class RATModel(BaseModel, validate_assignment=True, extra='forbid'): +class RATModel(BaseModel, validate_assignment=True, extra="forbid"): """A BaseModel where enums are represented by their value.""" def __repr__(self): - fields_repr = (', '.join(repr(v) if a is None else - f'{a}={v.value!r}' if isinstance(v, StrEnum) else - f'{a}={v!r}' + fields_repr = (", ".join(repr(v) if a is None else + f"{a}={v.value!r}" if isinstance(v, StrEnum) else + f"{a}={v!r}" for a, v in self.__repr_args__() ) ) - return f'{self.__repr_name__()}({fields_repr})' + return f"{self.__repr_name__()}({fields_repr})" class Background(RATModel): """Defines the Backgrounds in RAT.""" - name: str = Field(default_factory=lambda: 'New Background ' + next(background_number), min_length=1) + name: str = Field(default_factory=lambda: "New Background " + next(background_number), min_length=1) type: TypeOptions = TypeOptions.Constant - value_1: str = '' - value_2: str = '' - value_3: str = '' - value_4: str = '' - value_5: str = '' + value_1: str = "" + value_2: str = "" + value_3: str = "" + value_4: str = "" + value_5: str = "" class Contrast(RATModel): """Groups together all of the components of the model.""" - name: str = Field(default_factory=lambda: 'New Contrast ' + next(contrast_number), min_length=1) - data: str = '' - background: str = '' + name: str = Field(default_factory=lambda: "New Contrast " + next(contrast_number), min_length=1) + data: str = "" + background: str = "" background_action: BackgroundActions = BackgroundActions.Add - bulk_in: str = '' - bulk_out: str = '' - scalefactor: str = '' - resolution: str = '' + bulk_in: str = "" + bulk_out: str = "" + scalefactor: str = "" + resolution: str = "" resample: bool = False model: list[str] = [] class ContrastWithRatio(RATModel): """Groups together all of the components of the model including domain terms.""" - name: str = Field(default_factory=lambda: 'New Contrast ' + next(contrast_number), min_length=1) - data: str = '' - background: str = '' + name: str = Field(default_factory=lambda: "New Contrast " + next(contrast_number), min_length=1) + data: str = "" + background: str = "" background_action: BackgroundActions = BackgroundActions.Add - bulk_in: str = '' - bulk_out: str = '' - scalefactor: str = '' - resolution: str = '' + bulk_in: str = "" + bulk_out: str = "" + scalefactor: str = "" + resolution: str = "" resample: bool = False - domain_ratio: str = '' + domain_ratio: str = "" model: list[str] = [] class CustomFile(RATModel): """Defines the files containing functions to run when using custom models.""" - name: str = Field(default_factory=lambda: 'New Custom File ' + next(custom_file_number), min_length=1) - filename: str = '' - function_name: str = '' + name: str = Field(default_factory=lambda: "New Custom File " + next(custom_file_number), min_length=1) + filename: str = "" + function_name: str = "" language: Languages = Languages.Python - path: Union[str, pathlib.Path] = '' + path: Union[str, pathlib.Path] = "" def model_post_init(self, __context: Any) -> None: """If a "filename" is supplied but the "function_name" field is not set, the "function_name" should be set to @@ -101,7 +101,7 @@ def model_post_init(self, __context: Any) -> None: if "filename" in self.model_fields_set and "function_name" not in self.model_fields_set: self.function_name = pathlib.Path(self.filename).stem - @model_validator(mode='after') + @model_validator(mode="after") def set_matlab_function_name(self): """If we have a matlab custom function, the "function_name" should be set to the filename without the extension. """ @@ -113,12 +113,12 @@ def set_matlab_function_name(self): class Data(RATModel, arbitrary_types_allowed=True): """Defines the dataset required for each contrast.""" - name: str = Field(default_factory=lambda: 'New Data ' + next(data_number), min_length=1) + name: str = Field(default_factory=lambda: "New Data " + next(data_number), min_length=1) data: np.ndarray[np.float64] = np.empty([0, 3]) data_range: list[float] = Field(default=[], min_length=2, max_length=2) simulation_range: list[float] = Field(default=[], min_length=2, max_length=2) - @field_validator('data') + @field_validator("data") @classmethod def check_data_dimension(cls, data: np.ndarray[float]) -> np.ndarray[float]: """The data must be a two-dimensional array containing at least three columns.""" @@ -131,7 +131,7 @@ def check_data_dimension(cls, data: np.ndarray[float]) -> np.ndarray[float]: raise ValueError('"data" must have at least three columns') from None return data - @field_validator('data_range', 'simulation_range') + @field_validator("data_range", "simulation_range") @classmethod def check_min_max(cls, limits: list[float], info: ValidationInfo) -> list[float]: """The data range and simulation range maximum must be greater than the minimum.""" @@ -150,8 +150,8 @@ def model_post_init(self, __context: Any) -> None: if field not in self.model_fields_set: getattr(self, field).extend([data_min, data_max]) - @model_validator(mode='after') - def check_ranges(self) -> 'Data': + @model_validator(mode="after") + def check_ranges(self) -> "Data": """The limits of the "data_range" field must lie within the range of the supplied data, whilst the limits of the "simulation_range" field must lie outside the range of the supplied data. """ @@ -160,12 +160,12 @@ def check_ranges(self) -> 'Data': data_max = np.max(self.data[:, 0]) if "data_range" in self.model_fields_set and (self.data_range[0] < data_min or self.data_range[1] > data_max): - raise ValueError(f'The data_range value of: {self.data_range} must lie within the min/max values of ' - f'the data: [{data_min}, {data_max}]') + raise ValueError(f"The data_range value of: {self.data_range} must lie within the min/max values of " + f"the data: [{data_min}, {data_max}]") if "simulation_range" in self.model_fields_set and (self.simulation_range[0] > data_min or self.simulation_range[1] < data_max): - raise ValueError(f'The simulation_range value of: {self.simulation_range} must lie outside of the ' - f'min/max values of the data: [{data_min}, {data_max}]') + raise ValueError(f"The simulation_range value of: {self.simulation_range} must lie outside of the " + f"min/max values of the data: [{data_min}, {data_max}]") return self def __eq__(self, other: Any) -> bool: @@ -173,8 +173,8 @@ def __eq__(self, other: Any) -> bool: # When comparing instances of generic types for equality, as long as all field values are equal, # only require their generic origin types to be equal, rather than exact type equality. # This prevents headaches like MyGeneric(x=1) != MyGeneric[Any](x=1). - self_type = self.__pydantic_generic_metadata__['origin'] or self.__class__ - other_type = other.__pydantic_generic_metadata__['origin'] or other.__class__ + self_type = self.__pydantic_generic_metadata__["origin"] or self.__class__ + other_type = other.__pydantic_generic_metadata__["origin"] or other.__class__ return ( self_type == other_type @@ -196,39 +196,39 @@ def __repr__(self): for a, v in self.__repr_args__() ) ) - return f'{self.__repr_name__()}({fields_repr})' + return f"{self.__repr_name__()}({fields_repr})" class DomainContrast(RATModel): """Groups together the layers required for each domain.""" - name: str = Field(default_factory=lambda: 'New Domain Contrast ' + next(domain_contrast_number), min_length=1) + name: str = Field(default_factory=lambda: "New Domain Contrast " + next(domain_contrast_number), min_length=1) model: list[str] = [] class Layer(RATModel, populate_by_name=True): """Combines parameters into defined layers.""" - name: str = Field(default_factory=lambda: 'New Layer ' + next(layer_number), min_length=1) + name: str = Field(default_factory=lambda: "New Layer " + next(layer_number), min_length=1) thickness: str - SLD: str = Field(validation_alias='SLD_real') + SLD: str = Field(validation_alias="SLD_real") roughness: str - hydration: str = '' + hydration: str = "" hydrate_with: Hydration = Hydration.BulkOut class AbsorptionLayer(RATModel, populate_by_name=True): """Combines parameters into defined layers including absorption terms.""" - name: str = Field(default_factory=lambda: 'New Layer ' + next(layer_number), min_length=1) + name: str = Field(default_factory=lambda: "New Layer " + next(layer_number), min_length=1) thickness: str - SLD_real: str = Field(validation_alias='SLD') - SLD_imaginary: str = '' + SLD_real: str = Field(validation_alias="SLD") + SLD_imaginary: str = "" roughness: str - hydration: str = '' + hydration: str = "" hydrate_with: Hydration = Hydration.BulkOut class Parameter(RATModel): """Defines parameters needed to specify the model.""" - name: str = Field(default_factory=lambda: 'New Parameter ' + next(parameter_number), min_length=1) + name: str = Field(default_factory=lambda: "New Parameter " + next(parameter_number), min_length=1) min: float = 0.0 value: float = 0.0 max: float = 0.0 @@ -237,18 +237,18 @@ class Parameter(RATModel): mu: float = 0.0 sigma: float = np.inf - @model_validator(mode='after') - def check_min_max(self) -> 'Parameter': + @model_validator(mode="after") + def check_min_max(self) -> "Parameter": """The maximum value of a parameter must be greater than the minimum.""" if self.min > self.max: - raise ValueError(f'The maximum value {self.max} must be greater than the minimum value {self.min}') + raise ValueError(f"The maximum value {self.max} must be greater than the minimum value {self.min}") return self - @model_validator(mode='after') - def check_value_in_range(self) -> 'Parameter': + @model_validator(mode="after") + def check_value_in_range(self) -> "Parameter": """The value of a parameter must lie within its defined bounds.""" if self.value < self.min or self.value > self.max: - raise ValueError(f'value {self.value} is not within the defined range: {self.min} <= value <= {self.max}') + raise ValueError(f"value {self.value} is not within the defined range: {self.min} <= value <= {self.max}") return self @@ -259,10 +259,10 @@ class ProtectedParameter(Parameter): class Resolution(RATModel): """Defines Resolutions in RAT.""" - name: str = Field(default_factory=lambda: 'New Resolution ' + next(resolution_number), min_length=1) + name: str = Field(default_factory=lambda: "New Resolution " + next(resolution_number), min_length=1) type: TypeOptions = TypeOptions.Constant - value_1: str = '' - value_2: str = '' - value_3: str = '' - value_4: str = '' - value_5: str = '' + value_1: str = "" + value_2: str = "" + value_3: str = "" + value_4: str = "" + value_5: str = "" diff --git a/RAT/project.py b/RAT/project.py index b57c7b64..842b688f 100644 --- a/RAT/project.py +++ b/RAT/project.py @@ -15,76 +15,76 @@ from RAT.utils.enums import Calculations, Geometries, LayerModels, Priors, TypeOptions # Map project fields to pydantic models -model_in_classlist = {'parameters': 'Parameter', - 'bulk_in': 'Parameter', - 'bulk_out': 'Parameter', - 'scalefactors': 'Parameter', - 'domain_ratios': 'Parameter', - 'background_parameters': 'Parameter', - 'resolution_parameters': 'Parameter', - 'backgrounds': 'Background', - 'resolutions': 'Resolution', - 'custom_files': 'CustomFile', - 'data': 'Data', - 'layers': 'Layer', - 'domain_contrasts': 'DomainContrast', - 'contrasts': 'Contrast', +model_in_classlist = {"parameters": "Parameter", + "bulk_in": "Parameter", + "bulk_out": "Parameter", + "scalefactors": "Parameter", + "domain_ratios": "Parameter", + "background_parameters": "Parameter", + "resolution_parameters": "Parameter", + "backgrounds": "Background", + "resolutions": "Resolution", + "custom_files": "CustomFile", + "data": "Data", + "layers": "Layer", + "domain_contrasts": "DomainContrast", + "contrasts": "Contrast", } -values_defined_in = {'backgrounds.value_1': 'background_parameters', - 'backgrounds.value_2': 'background_parameters', - 'backgrounds.value_3': 'background_parameters', - 'backgrounds.value_4': 'background_parameters', - 'backgrounds.value_5': 'background_parameters', - 'resolutions.value_1': 'resolution_parameters', - 'resolutions.value_2': 'resolution_parameters', - 'resolutions.value_3': 'resolution_parameters', - 'resolutions.value_4': 'resolution_parameters', - 'resolutions.value_5': 'resolution_parameters', - 'layers.thickness': 'parameters', - 'layers.SLD': 'parameters', - 'layers.SLD_real': 'parameters', - 'layers.SLD_imaginary': 'parameters', - 'layers.roughness': 'parameters', - 'contrasts.data': 'data', - 'contrasts.background': 'backgrounds', - 'contrasts.bulk_in': 'bulk_in', - 'contrasts.bulk_out': 'bulk_out', - 'contrasts.scalefactor': 'scalefactors', - 'contrasts.resolution': 'resolutions', - 'contrasts.domain_ratio': 'domain_ratios', +values_defined_in = {"backgrounds.value_1": "background_parameters", + "backgrounds.value_2": "background_parameters", + "backgrounds.value_3": "background_parameters", + "backgrounds.value_4": "background_parameters", + "backgrounds.value_5": "background_parameters", + "resolutions.value_1": "resolution_parameters", + "resolutions.value_2": "resolution_parameters", + "resolutions.value_3": "resolution_parameters", + "resolutions.value_4": "resolution_parameters", + "resolutions.value_5": "resolution_parameters", + "layers.thickness": "parameters", + "layers.SLD": "parameters", + "layers.SLD_real": "parameters", + "layers.SLD_imaginary": "parameters", + "layers.roughness": "parameters", + "contrasts.data": "data", + "contrasts.background": "backgrounds", + "contrasts.bulk_in": "bulk_in", + "contrasts.bulk_out": "bulk_out", + "contrasts.scalefactor": "scalefactors", + "contrasts.resolution": "resolutions", + "contrasts.domain_ratio": "domain_ratios", } -AllFields = collections.namedtuple('AllFields', ['attribute', 'fields']) -model_names_used_in = {'background_parameters': AllFields('backgrounds', ['value_1', 'value_2', 'value_3', 'value_4', - 'value_5']), - 'resolution_parameters': AllFields('resolutions', ['value_1', 'value_2', 'value_3', 'value_4', - 'value_5']), - 'parameters': AllFields('layers', ['thickness', 'SLD', 'SLD_real', 'SLD_imaginary', - 'roughness', 'hydration']), - 'data': AllFields('contrasts', ['data']), - 'backgrounds': AllFields('contrasts', ['background']), - 'bulk_in': AllFields('contrasts', ['bulk_in']), - 'bulk_out': AllFields('contrasts', ['bulk_out']), - 'scalefactors': AllFields('contrasts', ['scalefactor']), - 'domain_ratios': AllFields('contrasts', ['domain_ratio']), - 'resolutions': AllFields('contrasts', ['resolution']), +AllFields = collections.namedtuple("AllFields", ["attribute", "fields"]) +model_names_used_in = {"background_parameters": AllFields("backgrounds", ["value_1", "value_2", "value_3", "value_4", + "value_5"]), + "resolution_parameters": AllFields("resolutions", ["value_1", "value_2", "value_3", "value_4", + "value_5"]), + "parameters": AllFields("layers", ["thickness", "SLD", "SLD_real", "SLD_imaginary", + "roughness", "hydration"]), + "data": AllFields("contrasts", ["data"]), + "backgrounds": AllFields("contrasts", ["background"]), + "bulk_in": AllFields("contrasts", ["bulk_in"]), + "bulk_out": AllFields("contrasts", ["bulk_out"]), + "scalefactors": AllFields("contrasts", ["scalefactor"]), + "domain_ratios": AllFields("contrasts", ["domain_ratio"]), + "resolutions": AllFields("contrasts", ["resolution"]), } # Note that the order of these parameters is hard-coded into RAT -parameter_class_lists = ['parameters', 'background_parameters', 'scalefactors', 'bulk_in', 'bulk_out', - 'resolution_parameters', 'domain_ratios'] -class_lists = [*parameter_class_lists, 'backgrounds', 'resolutions', 'custom_files', 'data', 'layers', - 'domain_contrasts', 'contrasts'] +parameter_class_lists = ["parameters", "background_parameters", "scalefactors", "bulk_in", "bulk_out", + "resolution_parameters", "domain_ratios"] +class_lists = [*parameter_class_lists, "backgrounds", "resolutions", "custom_files", "data", "layers", + "domain_contrasts", "contrasts"] -class Project(BaseModel, validate_assignment=True, extra='forbid', arbitrary_types_allowed=True): +class Project(BaseModel, validate_assignment=True, extra="forbid", arbitrary_types_allowed=True): """Defines the input data for a reflectivity calculation in RAT. This class combines the data defined in each of the pydantic models included in "models.py" into the full set of inputs required for a reflectivity calculation. """ - name: str = '' + name: str = "" calculation: Calculations = Calculations.NonPolarised model: LayerModels = LayerModels.StandardLayers geometry: Geometries = Geometries.AirSubstrate @@ -92,36 +92,36 @@ class Project(BaseModel, validate_assignment=True, extra='forbid', arbitrary_typ parameters: ClassList = ClassList() - bulk_in: ClassList = ClassList(RAT.models.Parameter(name='SLD Air', min=0.0, value=0.0, max=0.0, fit=False, + bulk_in: ClassList = ClassList(RAT.models.Parameter(name="SLD Air", min=0.0, value=0.0, max=0.0, fit=False, prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - bulk_out: ClassList = ClassList(RAT.models.Parameter(name='SLD D2O', min=6.2e-6, value=6.35e-6, max=6.35e-6, + bulk_out: ClassList = ClassList(RAT.models.Parameter(name="SLD D2O", min=6.2e-6, value=6.35e-6, max=6.35e-6, fit=False, prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - scalefactors: ClassList = ClassList(RAT.models.Parameter(name='Scalefactor 1', min=0.02, value=0.23, max=0.25, + scalefactors: ClassList = ClassList(RAT.models.Parameter(name="Scalefactor 1", min=0.02, value=0.23, max=0.25, fit=False, prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - domain_ratios: ClassList = ClassList(RAT.models.Parameter(name='Domain Ratio 1', min=0.4, value=0.5, max=0.6, + domain_ratios: ClassList = ClassList(RAT.models.Parameter(name="Domain Ratio 1", min=0.4, value=0.5, max=0.6, fit=False, prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - background_parameters: ClassList = ClassList(RAT.models.Parameter(name='Background Param 1', min=1e-7, value=1e-6, + background_parameters: ClassList = ClassList(RAT.models.Parameter(name="Background Param 1", min=1e-7, value=1e-6, max=1e-5, fit=False, prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - backgrounds: ClassList = ClassList(RAT.models.Background(name='Background 1', type=TypeOptions.Constant, - value_1='Background Param 1')) + backgrounds: ClassList = ClassList(RAT.models.Background(name="Background 1", type=TypeOptions.Constant, + value_1="Background Param 1")) - resolution_parameters: ClassList = ClassList(RAT.models.Parameter(name='Resolution Param 1', min=0.01, value=0.03, + resolution_parameters: ClassList = ClassList(RAT.models.Parameter(name="Resolution Param 1", min=0.01, value=0.03, max=0.05, fit=False, prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - resolutions: ClassList = ClassList(RAT.models.Resolution(name='Resolution 1', type=TypeOptions.Constant, - value_1='Resolution Param 1')) + resolutions: ClassList = ClassList(RAT.models.Resolution(name="Resolution 1", type=TypeOptions.Constant, + value_1="Resolution Param 1")) custom_files: ClassList = ClassList() data: ClassList = ClassList() @@ -133,18 +133,18 @@ class Project(BaseModel, validate_assignment=True, extra='forbid', arbitrary_typ _contrast_model_field: str _protected_parameters: dict - @field_validator('parameters', 'bulk_in', 'bulk_out', 'scalefactors', 'background_parameters', - 'backgrounds', 'resolution_parameters', 'resolutions', 'custom_files', 'data', 'layers', - 'domain_contrasts', 'contrasts') + @field_validator("parameters", "bulk_in", "bulk_out", "scalefactors", "background_parameters", + "backgrounds", "resolution_parameters", "resolutions", "custom_files", "data", "layers", + "domain_contrasts", "contrasts") @classmethod def check_class(cls, value: ClassList, info: ValidationInfo) -> ClassList: """Each of the data fields should be a ClassList of the appropriate model.""" model_name = model_in_classlist[info.field_name] # Correct model name if necessary - if info.field_name == 'layers' and info.data['absorption']: - model_name = 'AbsorptionLayer' - if info.field_name == 'contrasts' and info.data['calculation'] == Calculations.Domains: - model_name = 'ContrastWithRatio' + if info.field_name == "layers" and info.data["absorption"]: + model_name = "AbsorptionLayer" + if info.field_name == "contrasts" and info.data["calculation"] == Calculations.Domains: + model_name = "ContrastWithRatio" model = getattr(RAT.models, model_name) if not all(isinstance(element, model) for element in value): @@ -176,19 +176,19 @@ def model_post_init(self, __context: Any) -> None: if not hasattr(field, "_class_handle"): field._class_handle = getattr(RAT.models, model) - if 'Substrate Roughness' not in self.parameters.get_names(): - self.parameters.insert(0, RAT.models.ProtectedParameter(name='Substrate Roughness', min=1.0, value=3.0, + if "Substrate Roughness" not in self.parameters.get_names(): + self.parameters.insert(0, RAT.models.ProtectedParameter(name="Substrate Roughness", min=1.0, value=3.0, max=5.0, fit=True, prior_type=RAT.models.Priors.Uniform, mu=0.0, sigma=np.inf)) - elif 'Substrate Roughness' not in self.get_all_protected_parameters().values(): + elif "Substrate Roughness" not in self.get_all_protected_parameters().values(): # If substrate roughness is included as a standard parameter replace it with a protected parameter - substrate_roughness_values = self.parameters[self.parameters.index('Substrate Roughness')].model_dump() - self.parameters.remove('Substrate Roughness') + substrate_roughness_values = self.parameters[self.parameters.index("Substrate Roughness")].model_dump() + self.parameters.remove("Substrate Roughness") self.parameters.insert(0, RAT.models.ProtectedParameter(**substrate_roughness_values)) - if 'Simulation' not in self.data.get_names(): - self.data.insert(0, RAT.models.Data(name='Simulation', simulation_range=[0.005, 0.7])) + if "Simulation" not in self.data.get_names(): + self.data.insert(0, RAT.models.Data(name="Simulation", simulation_range=[0.005, 0.7])) self._all_names = self.get_all_names() self._contrast_model_field = self.get_contrast_model_field() @@ -196,23 +196,23 @@ def model_post_init(self, __context: Any) -> None: # Wrap ClassList routines - when any of these routines are called, the wrapper will force revalidation of the # model, handle errors and reset previous values if necessary. - methods_to_wrap = ['_setitem', '_delitem', '_iadd', 'append', 'insert', 'pop', 'remove', 'clear', 'extend', - 'set_fields'] + methods_to_wrap = ["_setitem", "_delitem", "_iadd", "append", "insert", "pop", "remove", "clear", "extend", + "set_fields"] for class_list in class_lists: attribute = getattr(self, class_list) for methodName in methods_to_wrap: setattr(attribute, methodName, self._classlist_wrapper(attribute, getattr(attribute, methodName))) - @model_validator(mode='after') - def set_domain_ratios(self) -> 'Project': + @model_validator(mode="after") + def set_domain_ratios(self) -> "Project": """If we are not running a domains calculation, ensure the domain_ratios component of the model is empty.""" if self.calculation != Calculations.Domains: self.domain_ratios.data = [] return self - @model_validator(mode='after') - def set_domain_contrasts(self) -> 'Project': + @model_validator(mode="after") + def set_domain_contrasts(self) -> "Project": """If we are not running a domains calculation with standard layers, ensure the domain_contrasts component of the model is empty. """ @@ -220,37 +220,37 @@ def set_domain_contrasts(self) -> 'Project': self.domain_contrasts.data = [] return self - @model_validator(mode='after') - def set_layers(self) -> 'Project': + @model_validator(mode="after") + def set_layers(self) -> "Project": """If we are not using a standard layers model, ensure the layers component of the model is empty.""" if self.model != LayerModels.StandardLayers: self.layers.data = [] return self - @model_validator(mode='after') - def set_calculation(self) -> 'Project': + @model_validator(mode="after") + def set_calculation(self) -> "Project": """Apply the calc setting to the project.""" contrast_list = [] handle = self.contrasts._class_handle.__name__ - if self.calculation == Calculations.Domains and handle == 'Contrast': + if self.calculation == Calculations.Domains and handle == "Contrast": for contrast in self.contrasts: contrast_list.append(RAT.models.ContrastWithRatio(**contrast.model_dump())) self.contrasts.data = contrast_list - self.domain_ratios.data = [RAT.models.Parameter(name='Domain Ratio 1', min=0.4, value=0.5, max=0.6, + self.domain_ratios.data = [RAT.models.Parameter(name="Domain Ratio 1", min=0.4, value=0.5, max=0.6, fit=False, prior_type=RAT.models.Priors.Uniform, mu=0.0, sigma=np.inf)] self.contrasts._class_handle = RAT.models.ContrastWithRatio - elif self.calculation != Calculations.Domains and handle == 'ContrastWithRatio': + elif self.calculation != Calculations.Domains and handle == "ContrastWithRatio": for contrast in self.contrasts: contrast_params = contrast.model_dump() - del contrast_params['domain_ratio'] + del contrast_params["domain_ratio"] contrast_list.append(RAT.models.Contrast(**contrast_params)) self.contrasts.data = contrast_list self.contrasts._class_handle = RAT.models.Contrast return self - @model_validator(mode='after') - def set_contrast_model_field(self) -> 'Project': + @model_validator(mode="after") + def set_contrast_model_field(self) -> "Project": """The contents of the "model" field of "contrasts" depend on the values of the "calculation" and "model_type" defined in the project. If they have changed, clear the contrast models. """ @@ -261,8 +261,8 @@ def set_contrast_model_field(self) -> 'Project': self._contrast_model_field = model_field return self - @model_validator(mode='after') - def check_contrast_model_length(self) -> 'Project': + @model_validator(mode="after") + def check_contrast_model_length(self) -> "Project": """Given certain values of the "calculation" and "model" defined in the project, the "model" field of "contrasts" may be constrained in its length. """ @@ -278,27 +278,27 @@ def check_contrast_model_length(self) -> 'Project': 'more than one value.') return self - @model_validator(mode='after') - def set_absorption(self) -> 'Project': + @model_validator(mode="after") + def set_absorption(self) -> "Project": """Apply the absorption setting to the project.""" layer_list = [] handle = self.layers._class_handle.__name__ - if self.absorption and handle == 'Layer': + if self.absorption and handle == "Layer": for layer in self.layers: layer_list.append(RAT.models.AbsorptionLayer(**layer.model_dump())) self.layers.data = layer_list self.layers._class_handle = RAT.models.AbsorptionLayer - elif not self.absorption and handle == 'AbsorptionLayer': + elif not self.absorption and handle == "AbsorptionLayer": for layer in self.layers: layer_params = layer.model_dump() - del layer_params['SLD_imaginary'] + del layer_params["SLD_imaginary"] layer_list.append(RAT.models.Layer(**layer_params)) self.layers.data = layer_list self.layers._class_handle = RAT.models.Layer return self - @model_validator(mode='after') - def update_renamed_models(self) -> 'Project': + @model_validator(mode="after") + def update_renamed_models(self) -> "Project": """When models defined in the ClassLists are renamed, we need to update that name elsewhere in the project.""" for class_list in model_names_used_in: old_names = self._all_names[class_list] @@ -315,30 +315,30 @@ def update_renamed_models(self) -> 'Project': self._all_names = self.get_all_names() return self - @model_validator(mode='after') - def cross_check_model_values(self) -> 'Project': + @model_validator(mode="after") + def cross_check_model_values(self) -> "Project": """Certain model fields should contain values defined elsewhere in the project.""" - value_fields = ['value_1', 'value_2', 'value_3', 'value_4', 'value_5'] - self.check_allowed_values('backgrounds', value_fields, self.background_parameters.get_names()) - self.check_allowed_values('resolutions', value_fields, self.resolution_parameters.get_names()) - self.check_allowed_values('layers', ['thickness', 'SLD', 'SLD_real', 'SLD_imaginary', 'roughness'], + value_fields = ["value_1", "value_2", "value_3", "value_4", "value_5"] + self.check_allowed_values("backgrounds", value_fields, self.background_parameters.get_names()) + self.check_allowed_values("resolutions", value_fields, self.resolution_parameters.get_names()) + self.check_allowed_values("layers", ["thickness", "SLD", "SLD_real", "SLD_imaginary", "roughness"], self.parameters.get_names()) - self.check_allowed_values('contrasts', ['data'], self.data.get_names()) - self.check_allowed_values('contrasts', ['background'], self.backgrounds.get_names()) - self.check_allowed_values('contrasts', ['bulk_in'], self.bulk_in.get_names()) - self.check_allowed_values('contrasts', ['bulk_out'], self.bulk_out.get_names()) - self.check_allowed_values('contrasts', ['scalefactor'], self.scalefactors.get_names()) - self.check_allowed_values('contrasts', ['resolution'], self.resolutions.get_names()) - self.check_allowed_values('contrasts', ['domain_ratio'], self.domain_ratios.get_names()) + self.check_allowed_values("contrasts", ["data"], self.data.get_names()) + self.check_allowed_values("contrasts", ["background"], self.backgrounds.get_names()) + self.check_allowed_values("contrasts", ["bulk_in"], self.bulk_in.get_names()) + self.check_allowed_values("contrasts", ["bulk_out"], self.bulk_out.get_names()) + self.check_allowed_values("contrasts", ["scalefactor"], self.scalefactors.get_names()) + self.check_allowed_values("contrasts", ["resolution"], self.resolutions.get_names()) + self.check_allowed_values("contrasts", ["domain_ratio"], self.domain_ratios.get_names()) - self.check_contrast_model_allowed_values('contrasts', getattr(self, self._contrast_model_field).get_names(), + self.check_contrast_model_allowed_values("contrasts", getattr(self, self._contrast_model_field).get_names(), self._contrast_model_field) - self.check_contrast_model_allowed_values('domain_contrasts', self.layers.get_names(), 'layers') + self.check_contrast_model_allowed_values("domain_contrasts", self.layers.get_names(), "layers") return self - @model_validator(mode='after') - def check_protected_parameters(self) -> 'Project': + @model_validator(mode="after") + def check_protected_parameters(self) -> "Project": """Protected parameters should not be deleted. If this is attempted, raise an error.""" for class_list in parameter_class_lists: protected_parameters = [param.name for param in getattr(self, class_list) @@ -352,14 +352,14 @@ def check_protected_parameters(self) -> 'Project': return self def __repr__(self): - output = '' + output = "" for key, value in self.__dict__.items(): if value: output += f'{key.replace("_", " ").title() + ": " :-<100}\n\n' try: - output += value.value + '\n\n' # For enums + output += value.value + "\n\n" # For enums except AttributeError: - output += repr(value) + '\n\n' + output += repr(value) + "\n\n" return output def get_all_names(self): @@ -392,7 +392,7 @@ def check_allowed_values(self, attribute: str, field_list: list[str], allowed_va class_list = getattr(self, attribute) for model in class_list: for field in field_list: - value = getattr(model, field, '') + value = getattr(model, field, "") if value and value not in allowed_values: raise ValueError(f'The value "{value}" in the "{field}" field of "{attribute}" must be defined in ' f'"{values_defined_in[f"{attribute}.{field}"]}".') @@ -433,14 +433,14 @@ def get_contrast_model_field(self): """ if self.model == LayerModels.StandardLayers: if self.calculation == Calculations.Domains: - model_field = 'domain_contrasts' + model_field = "domain_contrasts" else: - model_field = 'layers' + model_field = "layers" else: - model_field = 'custom_files' + model_field = "custom_files" return model_field - def write_script(self, obj_name: str = 'problem', script: str = 'project_script.py'): + def write_script(self, obj_name: str = "problem", script: str = "project_script.py"): """Write a python script that can be run to reproduce this project object. Parameters @@ -454,19 +454,19 @@ def write_script(self, obj_name: str = 'problem', script: str = 'project_script. file_parts = os.path.splitext(script) if not file_parts[1]: - script += '.py' - elif file_parts[1] != '.py': + script += ".py" + elif file_parts[1] != ".py": raise ValueError('The script name provided to "write_script" must use the ".py" format') indent = 4 * " " - with open(script, 'w') as f: + with open(script, "w") as f: f.write('# THIS FILE IS GENERATED FROM RAT VIA THE "WRITE_SCRIPT" ROUTINE. IT IS NOT PART OF THE RAT CODE.' '\n\n') # Need imports - f.write('import RAT\nfrom RAT.models import *\nfrom numpy import array, inf\n\n') + f.write("import RAT\nfrom RAT.models import *\nfrom numpy import array, inf\n\n") f.write(f"{obj_name} = RAT.Project(\n{indent}name='{self.name}', calculation='{self.calculation}'," f" model='{self.model}', geometry='{self.geometry}', absorption={self.absorption},\n") @@ -474,8 +474,8 @@ def write_script(self, obj_name: str = 'problem', script: str = 'project_script. for class_list in class_lists: contents = getattr(self, class_list).data if contents: - f.write(f'{indent}{class_list}=RAT.ClassList({contents}),\n') - f.write(f'{indent})\n') + f.write(f"{indent}{class_list}=RAT.ClassList({contents}),\n") + f.write(f"{indent})\n") def _classlist_wrapper(self, class_list: ClassList, func: Callable): """Defines the function used to wrap around ClassList routines to force revalidation. diff --git a/RAT/run.py b/RAT/run.py index 311d0fa1..01be091e 100644 --- a/RAT/run.py +++ b/RAT/run.py @@ -6,13 +6,13 @@ def run(project, controls): """Run RAT for the given project and controls inputs.""" - parameter_field = {'parameters': 'params', - 'bulk_in': 'bulkIn', - 'bulk_out': 'bulkOut', - 'scalefactors': 'scalefactors', - 'domain_ratios': 'domainRatio', - 'background_parameters': 'backgroundParams', - 'resolution_parameters': 'resolutionParams', + parameter_field = {"parameters": "params", + "bulk_in": "bulkIn", + "bulk_out": "bulkOut", + "scalefactors": "scalefactors", + "domain_ratios": "domainRatio", + "background_parameters": "backgroundParams", + "resolution_parameters": "resolutionParams", } problem_definition, cells, limits, priors, cpp_controls = make_input(project, controls) diff --git a/RAT/utils/custom_errors.py b/RAT/utils/custom_errors.py index 22a4f3b8..4615724f 100644 --- a/RAT/utils/custom_errors.py +++ b/RAT/utils/custom_errors.py @@ -27,11 +27,11 @@ def custom_pydantic_validation_error(error_list: list[pydantic_core.ErrorDetails custom_error_msgs = {} custom_error_list = [] for error in error_list: - if error['type'] in custom_error_msgs: - custom_error = pydantic_core.PydanticCustomError(error['type'], custom_error_msgs[error['type']]) + if error["type"] in custom_error_msgs: + custom_error = pydantic_core.PydanticCustomError(error["type"], custom_error_msgs[error["type"]]) else: - custom_error = pydantic_core.PydanticCustomError(error['type'], error['msg']) - error['type'] = custom_error + custom_error = pydantic_core.PydanticCustomError(error["type"], error["msg"]) + error["type"] = custom_error custom_error_list.append(error) return custom_error_list diff --git a/RAT/utils/enums.py b/RAT/utils/enums.py index 0e91eb15..306768c6 100644 --- a/RAT/utils/enums.py +++ b/RAT/utils/enums.py @@ -9,34 +9,34 @@ # Controls class Parallel(StrEnum): """Defines the available options for parallelization""" - Single = 'single' - Points = 'points' - Contrasts = 'contrasts' + Single = "single" + Points = "points" + Contrasts = "contrasts" class Procedures(StrEnum): """Defines the available options for procedures""" - Calculate = 'calculate' - Simplex = 'simplex' - DE = 'de' - NS = 'ns' - Dream = 'dream' + Calculate = "calculate" + Simplex = "simplex" + DE = "de" + NS = "ns" + Dream = "dream" class Display(StrEnum): """Defines the available options for display""" - Off = 'off' - Iter = 'iter' - Notify = 'notify' - Final = 'final' + Off = "off" + Iter = "iter" + Notify = "notify" + Final = "final" class BoundHandling(StrEnum): """Defines the available options for bound handling""" - Off = 'off' - Reflect = 'reflect' - Bound = 'bound' - Fold = 'fold' + Off = "off" + Reflect = "reflect" + Bound = "bound" + Fold = "fold" class Strategies(Enum): @@ -51,46 +51,46 @@ class Strategies(Enum): # Models class Hydration(StrEnum): - None_ = 'none' - BulkIn = 'bulk in' - BulkOut = 'bulk out' - Oil = 'oil' + None_ = "none" + BulkIn = "bulk in" + BulkOut = "bulk out" + Oil = "oil" class Languages(StrEnum): - Cpp = 'cpp' - Python = 'python' - Matlab = 'matlab' + Cpp = "cpp" + Python = "python" + Matlab = "matlab" class Priors(StrEnum): - Uniform = 'uniform' - Gaussian = 'gaussian' + Uniform = "uniform" + Gaussian = "gaussian" class TypeOptions(StrEnum): - Constant = 'constant' - Data = 'data' - Function = 'function' + Constant = "constant" + Data = "data" + Function = "function" class BackgroundActions(StrEnum): - Add = 'add' - Subtract = 'subtract' + Add = "add" + Subtract = "subtract" # Project class Calculations(StrEnum): - NonPolarised = 'non polarised' - Domains = 'domains' + NonPolarised = "non polarised" + Domains = "domains" class Geometries(StrEnum): - AirSubstrate = 'air/substrate' - SubstrateLiquid = 'substrate/liquid' + AirSubstrate = "air/substrate" + SubstrateLiquid = "substrate/liquid" class LayerModels(StrEnum): - CustomLayers = 'custom layers' - CustomXY = 'custom xy' - StandardLayers = 'standard layers' + CustomLayers = "custom layers" + CustomXY = "custom xy" + StandardLayers = "standard layers" diff --git a/RAT/utils/plotting.py b/RAT/utils/plotting.py index f4ac32be..8f381431 100644 --- a/RAT/utils/plotting.py +++ b/RAT/utils/plotting.py @@ -36,7 +36,7 @@ def __init__(self, row: int = 1, col: int = 1): self._close_clicked = False self._fig.canvas.mpl_connect("key_press_event", self._process_button_press) - self._fig.canvas.mpl_connect('close_event', + self._fig.canvas.mpl_connect("close_event", self._close) def wait_for_close(self): @@ -52,7 +52,7 @@ def _process_button_press(self, event): """ Process the key_press_event. """ - if event.key == 'escape': + if event.key == "escape": self._esc_pressed = True def _close(self, _): @@ -86,7 +86,7 @@ def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, ax.errorbar(x=x, y=y, yerr=y_error, - fmt='none', + fmt="none", ecolor=color, elinewidth=1, capsize=0) @@ -135,7 +135,7 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay # Plot the reflectivity on plot (1,1) ref_plot.plot(r[:, 0], r[:, 1]/div, - label=f'ref {i+1}', + label=f"ref {i+1}", linewidth=2) color = ref_plot.get_lines()[-1].get_color() @@ -162,10 +162,10 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay for j in range(len(sld)): sld_plot.plot(sld[j][:, 0], sld[j][:, 1], - label=f'sld {i+1}', + label=f"sld {i+1}", linewidth=1) - if data.resample[i] == 1 or data.modelType == 'custom xy': + if data.resample[i] == 1 or data.modelType == "custom xy": layers = data.resampledLayers[i][0] for j in range(len(data.resampledLayers[i])): layer = data.resampledLayers[i][j] @@ -184,15 +184,15 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay linewidth=1) # Format the axis - ref_plot.set_yscale('log') - ref_plot.set_xscale('log') - ref_plot.set_xlabel('Qz') - ref_plot.set_ylabel('Ref') + ref_plot.set_yscale("log") + ref_plot.set_xscale("log") + ref_plot.set_xlabel("Qz") + ref_plot.set_ylabel("Ref") ref_plot.legend() ref_plot.grid() - sld_plot.set_xlabel('Z') - sld_plot.set_ylabel('SLD') + sld_plot.set_xlabel("Z") + sld_plot.set_ylabel("SLD") sld_plot.legend() sld_plot.grid() diff --git a/RAT/wrappers.py b/RAT/wrappers.py index 1b485887..53cb5846 100644 --- a/RAT/wrappers.py +++ b/RAT/wrappers.py @@ -20,7 +20,7 @@ def __init__(self, filename: str) -> None: try: import matlab.engine except ImportError: - raise ImportError('matlabengine is required to use MatlabWrapper') from None + raise ImportError("matlabengine is required to use MatlabWrapper") from None self.engine = matlab.engine.start_matlab() path = pathlib.Path(filename) self.engine.cd(str(path.parent), nargout=0) @@ -40,14 +40,14 @@ def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tup """ def handle(params, bulk_in, bulk_out, contrast, domain=-1): if domain == -1: - output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, 'float'), - np.array(bulk_in, 'float'), - np.array(bulk_out, 'float'), + output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, "float"), + np.array(bulk_in, "float"), + np.array(bulk_out, "float"), float(contrast + 1), nargout=2) else: - output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, 'float'), - np.array(bulk_in, 'float'), - np.array(bulk_out, 'float'), + output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, "float"), + np.array(bulk_in, "float"), + np.array(bulk_out, "float"), float(contrast + 1), float(domain + 1), nargout=2) return output, sub_rough diff --git a/setup.py b/setup.py index 09fc46bd..4b16947a 100644 --- a/setup.py +++ b/setup.py @@ -8,24 +8,24 @@ from setuptools.command.build_clib import build_clib from setuptools.command.build_ext import build_ext -__version__ = '0.0.0' +__version__ = "0.0.0" -libevent = ('eventManager', {'sources': ['cpp/RAT/events/eventManager.cpp'], - 'include_dirs': ['cpp/RAT/events/']}) +libevent = ("eventManager", {"sources": ["cpp/RAT/events/eventManager.cpp"], + "include_dirs": ["cpp/RAT/events/"]}) ext_modules = [ Extension( - 'RAT.rat_core', - sources=['cpp/rat.cpp', *glob('cpp/RAT/*.c*')], + "RAT.rat_core", + sources=["cpp/rat.cpp", *glob("cpp/RAT/*.c*")], include_dirs=[ # Path to pybind11 headers pybind11.get_include(), pybind11.get_include(True), - 'cpp/RAT/', + "cpp/RAT/", ], - language='c++' + language="c++" ), ] @@ -35,8 +35,8 @@ def has_flag(compiler, flagname): import tempfile from setuptools.errors import CompileError - with tempfile.NamedTemporaryFile('w', suffix='.cpp') as f: - f.write('int main (int argc, char **argv) { return 0; }') + with tempfile.NamedTemporaryFile("w", suffix=".cpp") as f: + f.write("int main (int argc, char **argv) { return 0; }") try: compiler.compile([f.name], extra_postargs=[flagname]) except CompileError: @@ -45,42 +45,42 @@ def has_flag(compiler, flagname): def get_shared_object_name(lib_name): - if platform.system() == 'Windows': - return f'{lib_name}.dll' - elif platform.system() == 'Darwin': - return f'{lib_name}.dylib' + if platform.system() == "Windows": + return f"{lib_name}.dll" + elif platform.system() == "Darwin": + return f"{lib_name}.dylib" else: - return f'{lib_name}.so' + return f"{lib_name}.so" class BuildExt(build_ext): """A custom build extension for adding compiler-specific options.""" c_opts = { - 'msvc': ['/EHsc'], - 'unix': ['-fopenmp', '-std=c++11'], + "msvc": ["/EHsc"], + "unix": ["-fopenmp", "-std=c++11"], } l_opts = { - 'msvc': [], - 'unix': ['-fopenmp'], + "msvc": [], + "unix": ["-fopenmp"], } - if sys.platform == 'darwin': - darwin_opts = ['-stdlib=libc++', '-mmacosx-version-min=10.9'] - c_opts['unix'] = [*darwin_opts, '-fopenmp'] - l_opts['unix'] = [*darwin_opts, '-lomp'] + if sys.platform == "darwin": + darwin_opts = ["-stdlib=libc++", "-mmacosx-version-min=10.9"] + c_opts["unix"] = [*darwin_opts, "-fopenmp"] + l_opts["unix"] = [*darwin_opts, "-lomp"] def build_extensions(self): ct = self.compiler.compiler_type opts = self.c_opts.get(ct, []) link_opts = self.l_opts.get(ct, []) - if ct == 'unix': - if '-Wstrict-prototypes' in self.compiler.compiler_so: - self.compiler.compiler_so.remove('-Wstrict-prototypes') + if ct == "unix": + if "-Wstrict-prototypes" in self.compiler.compiler_so: + self.compiler.compiler_so.remove("-Wstrict-prototypes") opts.append(f'-DVERSION_INFO="{self.distribution.get_version()}"') - if has_flag(self.compiler, '-fvisibility=hidden'): - opts.append('-fvisibility=hidden') - elif ct == 'msvc': + if has_flag(self.compiler, "-fvisibility=hidden"): + opts.append("-fvisibility=hidden") + elif ct == "msvc": opts.append(f'/DVERSION_INFO=\\"{self.distribution.get_version()}\\"') for ext in self.extensions: ext.extra_compile_args = opts @@ -89,15 +89,15 @@ def build_extensions(self): def run(self): super().run() - build_py = self.get_finalized_command('build_py') - package_dir = f'{build_py.build_lib}/RAT/' + build_py = self.get_finalized_command("build_py") + package_dir = f"{build_py.build_lib}/RAT/" for p in Path(package_dir).glob("**/*"): if p.suffix in {".exp", ".a", ".lib"}: p.unlink() if self.inplace: obj_name = get_shared_object_name(libevent[0]) - src = f'{build_py.build_lib}/RAT/{obj_name}' + src = f"{build_py.build_lib}/RAT/{obj_name}" dest = f'{build_py.get_package_dir("RAT")}/{obj_name}' build_py.copy_file(src, dest) @@ -105,28 +105,28 @@ def run(self): class BuildClib(build_clib): def initialize_options(self): super().initialize_options() - build_py = self.get_finalized_command('build_py') - self.build_clib = f'{build_py.build_lib}/RAT' + build_py = self.get_finalized_command("build_py") + self.build_clib = f"{build_py.build_lib}/RAT" def build_libraries(self, libraries): # bug in distutils: flag not valid for c++ - flag = '-Wstrict-prototypes' - if (hasattr(self.compiler, 'compiler_so') + flag = "-Wstrict-prototypes" + if (hasattr(self.compiler, "compiler_so") and flag in self.compiler.compiler_so): self.compiler.compiler_so.remove(flag) compiler_type = self.compiler.compiler_type - if compiler_type == 'msvc': - compile_args = ['/EHsc', '/LD'] + if compiler_type == "msvc": + compile_args = ["/EHsc", "/LD"] else: - compile_args = ['-std=c++11', '-fPIC'] + compile_args = ["-std=c++11", "-fPIC"] for (lib_name, build_info) in libraries: - build_info['cflags'] = compile_args - macros = build_info.get('macros') - include_dirs = build_info.get('include_dirs') - cflags = build_info.get('cflags') - sources = list(build_info.get('sources')) + build_info["cflags"] = compile_args + macros = build_info.get("macros") + include_dirs = build_info.get("include_dirs") + cflags = build_info.get("cflags") + sources = list(build_info.get("sources")) objects = self.compiler.compile( sources, output_dir=self.build_temp, @@ -147,30 +147,30 @@ def build_libraries(self, libraries): setup( - name='RAT', + name="RAT", version=__version__, - author='', - author_email='', - url='https://github.com/RascalSoftware/python-RAT', - description='Python extension for the Reflectivity Analysis Toolbox (RAT)', - long_description=open('README.md').read(), - long_description_content_type='text/markdown', + author="", + author_email="", + url="https://github.com/RascalSoftware/python-RAT", + description="Python extension for the Reflectivity Analysis Toolbox (RAT)", + long_description = open("README.md").read(), + long_description_content_type = "text/markdown", packages=find_packages(), include_package_data=True, - package_data={'': [get_shared_object_name(libevent[0])], 'RAT.examples': ["data/*.dat"]}, - cmdclass={'build_clib': BuildClib, 'build_ext': BuildExt}, + package_data={"": [get_shared_object_name(libevent[0])], "RAT.examples": ["data/*.dat"]}, + cmdclass={"build_clib": BuildClib, "build_ext": BuildExt}, libraries=[libevent], ext_modules=ext_modules, - python_requires='>=3.9', - install_requires=['numpy >= 1.20', 'prettytable >= 3.9.0', 'pydantic >= 2.7.2', 'matplotlib >= 3.8.3'], - extras_require={':python_version < "3.11"': ['StrEnum >= 0.4.15'], - 'Dev': ['pytest>=7.4.0', 'pytest-cov>=4.1.0'], - 'Matlab_latest': ['matlabengine'], - 'Matlab_2023b': ['matlabengine == 23.2.1'], - 'Matlab_2023a': ['matlabengine == 9.14.3'], - 'Matlab-2022b': ['matlabengine == 9.13.9'], - 'Matlab_2022a': ['matlabengine == 9.12.19'], - 'Matlab_2021b': ['matlabengine == 9.11.21'], - 'Matlab_2021a': ['matlabengine == 9.10.3']}, + python_requires=">=3.9", + install_requires=["numpy >= 1.20", "prettytable >= 3.9.0", "pydantic >= 2.7.2", "matplotlib >= 3.8.3"], + extras_require={':python_version < "3.11"': ["StrEnum >= 0.4.15"], + "Dev": ["pytest>=7.4.0", "pytest-cov>=4.1.0"], + "Matlab_latest": ["matlabengine"], + "Matlab_2023b": ["matlabengine == 23.2.1"], + "Matlab_2023a": ["matlabengine == 9.14.3"], + "Matlab-2022b": ["matlabengine == 9.13.9"], + "Matlab_2022a": ["matlabengine == 9.12.19"], + "Matlab_2021b": ["matlabengine == 9.11.21"], + "Matlab_2021a": ["matlabengine == 9.10.3"]}, zip_safe=False, ) diff --git a/tests/conftest.py b/tests/conftest.py index 3c162486..d9c2e698 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -217,11 +217,11 @@ def reflectivity_calculation_output_results(): results.fitParams = np.array([3.000e+00, 1.954e+01, 2.266e+01, 5.252e+00, 5.640e+00, 1.712e+01, 8.560e+00, 4.545e+01, 1.070e+01, 6.014e+00, 1.782e+01, 1.764e+01, 3.615e+01, 2.361e+01, 2.230e-06, 3.380e-06, 5.980e-06, 2.210e-06]) - results.fitNames = ['Substrate Roughness', 'Oxide Thickness', 'SAM Tails Thickness', 'SAM Tails Hydration', - 'SAM Roughness', 'CW Thickness', 'SAM Heads Thickness', 'SAM Heads Hydration', - 'Bilayer Heads Thickness', 'Bilayer Roughness', 'Bilayer Tails Thickness', - 'Bilayer Tails Hydration', 'Bilayer Heads Hydration', 'Oxide Hydration', - 'Background parameter D2O', 'Background parameter SMW', 'D2O', 'SMW'] + results.fitNames = ["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", + "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", + "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", + "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", + "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"] return results @@ -435,11 +435,11 @@ def reflectivity_calculation_results(): fitParams=np.array([3.000e+00, 1.954e+01, 2.266e+01, 5.252e+00, 5.640e+00, 1.712e+01, 8.560e+00, 4.545e+01, 1.070e+01, 6.014e+00, 1.782e+01, 1.764e+01, 3.615e+01, 2.361e+01, 2.230e-06, 3.380e-06, 5.980e-06, 2.210e-06]), - fitNames=['Substrate Roughness', 'Oxide Thickness', 'SAM Tails Thickness', 'SAM Tails Hydration', - 'SAM Roughness', 'CW Thickness', 'SAM Heads Thickness', 'SAM Heads Hydration', - 'Bilayer Heads Thickness', 'Bilayer Roughness', 'Bilayer Tails Thickness', - 'Bilayer Tails Hydration', 'Bilayer Heads Hydration', 'Oxide Hydration', - 'Background parameter D2O', 'Background parameter SMW', 'D2O', 'SMW'] + fitNames=["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", + "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", + "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", + "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", + "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"] ) @@ -668,11 +668,11 @@ def dream_output_results(): 1.09906265e+01, 5.71005361e+00, 1.67933822e+01, 1.72009856e+01, 3.00260126e+01, 2.94448999e+01, 2.37113128e-06, 1.99006694e-06, 6.01489149e-06, 1.59371685e-06]) - results.fitNames = ['Substrate Roughness', 'Oxide Thickness', 'SAM Tails Thickness', 'SAM Tails Hydration', - 'SAM Roughness', 'CW Thickness', 'SAM Heads Thickness', 'SAM Heads Hydration', - 'Bilayer Heads Thickness', 'Bilayer Roughness', 'Bilayer Tails Thickness', - 'Bilayer Tails Hydration', 'Bilayer Heads Hydration', 'Oxide Hydration', - 'Background parameter D2O', 'Background parameter SMW', 'D2O', 'SMW'] + results.fitNames = ["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", + "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", + "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", + "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", + "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"] return results @@ -1349,11 +1349,11 @@ def dream_results(): 1.09906265e+01, 5.71005361e+00, 1.67933822e+01, 1.72009856e+01, 3.00260126e+01, 2.94448999e+01, 2.37113128e-06, 1.99006694e-06, 6.01489149e-06, 1.59371685e-06]), - fitNames=['Substrate Roughness', 'Oxide Thickness', 'SAM Tails Thickness', 'SAM Tails Hydration', - 'SAM Roughness', 'CW Thickness', 'SAM Heads Thickness', 'SAM Heads Hydration', - 'Bilayer Heads Thickness', 'Bilayer Roughness', 'Bilayer Tails Thickness', 'Bilayer Tails Hydration', - 'Bilayer Heads Hydration', 'Oxide Hydration', 'Background parameter D2O', 'Background parameter SMW', - 'D2O', 'SMW'], + fitNames=["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", + "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", + "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", "Bilayer Tails Hydration", + "Bilayer Heads Hydration", "Oxide Hydration", "Background parameter D2O", "Background parameter SMW", + "D2O", "SMW"], predictionIntervals=RAT.outputs.PredictionIntervals( reflectivity=[np.array([[1.00000560e+00, 7.07687612e-01, 2.08315160e-02, 2.40787966e-03, 2.17627660e-03, 2.54301700e-03, 1.76309827e-03, 6.15269679e-04, diff --git a/tests/test_classlist.py b/tests/test_classlist.py index a06cad10..a57017d9 100644 --- a/tests/test_classlist.py +++ b/tests/test_classlist.py @@ -14,40 +14,40 @@ @pytest.fixture def one_name_class_list(): """A ClassList of InputAttributes, containing one element with a name defined.""" - return ClassList([InputAttributes(name='Alice')]) + return ClassList([InputAttributes(name="Alice")]) @pytest.fixture def two_name_class_list(): """A ClassList of InputAttributes, containing two elements with names defined.""" - return ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob')]) + return ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]) @pytest.fixture def two_name_class_list_table(): """The table representation of the ClassList defined in the "two_name_class_list" fixture.""" return( - '+-------+-------+\n' - '| index | name |\n' - '+-------+-------+\n' - '| 0 | Alice |\n' - '| 1 | Bob |\n' - '+-------+-------+' + "+-------+-------+\n" + "| index | name |\n" + "+-------+-------+\n" + "| 0 | Alice |\n" + "| 1 | Bob |\n" + "+-------+-------+" ) @pytest.fixture def three_name_class_list(): """A ClassList of InputAttributes, containing three elements with names defined.""" - return ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob'), InputAttributes(name='Eve')]) + return ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob"), InputAttributes(name="Eve")]) class TestInitialisation: @pytest.mark.parametrize("input_object", [ (InputAttributes()), - (InputAttributes(name='Alice')), - (InputAttributes(surname='Morgan')), - 'Alice', + (InputAttributes(name="Alice")), + (InputAttributes(surname="Morgan")), + "Alice", ]) def test_input_object(self, input_object: Any) -> None: """For an input of an object, the ClassList should contain a one-element list containing the object and @@ -59,13 +59,13 @@ def test_input_object(self, input_object: Any) -> None: @pytest.mark.parametrize("input_sequence", [ ([InputAttributes()]), - ([InputAttributes(name='Alice')]), - ([InputAttributes(surname='Morgan')]), - ([InputAttributes(name='Alice'), InputAttributes(name='Bob')]), + ([InputAttributes(name="Alice")]), + ([InputAttributes(surname="Morgan")]), + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), (InputAttributes(),), - (InputAttributes(name='Alice'),), - (InputAttributes(surname='Morgan'),), - (InputAttributes(name='Alice'), InputAttributes(name='Bob')), + (InputAttributes(name="Alice"),), + (InputAttributes(surname="Morgan"),), + (InputAttributes(name="Alice"), InputAttributes(name="Bob")), ]) def test_input_sequence(self, input_sequence: Sequence[object]) -> None: """For an input of a sequence, the ClassList should be a list equal to the input sequence, and _class_handle @@ -77,8 +77,8 @@ def test_input_sequence(self, input_sequence: Sequence[object]) -> None: assert isinstance(element, class_list._class_handle) @pytest.mark.parametrize("input_sequence", [ - ([InputAttributes(name='Alice'), SubInputAttributes(name='Bob')]), - ([SubInputAttributes(name='Alice'), InputAttributes(name='Bob')]), + ([InputAttributes(name="Alice"), SubInputAttributes(name="Bob")]), + ([SubInputAttributes(name="Alice"), InputAttributes(name="Bob")]), ]) def test_input_sequence_subclass(self, input_sequence: Sequence[object]) -> None: """For an input of a sequence containing objects of a class and its subclasses, the ClassList should be a list @@ -95,10 +95,10 @@ def test_empty_input(self, empty_input: Sequence[object]) -> None: _class_handle should be unset.""" class_list = ClassList(empty_input) assert class_list.data == [] - assert not hasattr(class_list, '_class_handle') + assert not hasattr(class_list, "_class_handle") @pytest.mark.parametrize("input_list", [ - ([InputAttributes(name='Alice'), dict(name='Bob')]), + ([InputAttributes(name="Alice"), dict(name="Bob")]), ]) def test_different_classes(self, input_list: Sequence[object]) -> None: """If we initialise a ClassList with an input containing multiple classes, we should raise a ValueError.""" @@ -107,8 +107,8 @@ def test_different_classes(self, input_list: Sequence[object]) -> None: ClassList(input_list) @pytest.mark.parametrize("input_list, name_field", [ - ([InputAttributes(name='Alice'), InputAttributes(name='Alice')], 'name'), - ([InputAttributes(id='Alice'), InputAttributes(id='Alice')], 'id'), + ([InputAttributes(name="Alice"), InputAttributes(name="Alice")], "name"), + ([InputAttributes(id="Alice"), InputAttributes(id="Alice")], "id"), ]) def test_identical_name_fields(self, input_list: Sequence[object], name_field: str) -> None: """If we initialise a ClassList with input objects with identical values of the name_field, @@ -131,7 +131,7 @@ def test_repr_empty_table() -> None: @pytest.mark.parametrize("input_list", [ - (['Alice', 'Bob']), + (["Alice", "Bob"]), ]) def test_repr_list(input_list: list[str]) -> None: """For classes without the __dict__ attribute, we should be able to print the ClassList as a list.""" @@ -145,9 +145,9 @@ def test_repr_empty_classlist() -> None: @pytest.mark.parametrize(["new_item", "expected_classlist"], [ - (InputAttributes(name='Eve'), ClassList([InputAttributes(name='Eve'), InputAttributes(name='Bob')])), - (InputAttributes(name='John', surname='Luther'), - ClassList([InputAttributes(name='John', surname='Luther'), InputAttributes(name='Bob')])), + (InputAttributes(name="Eve"), ClassList([InputAttributes(name="Eve"), InputAttributes(name="Bob")])), + (InputAttributes(name="John", surname="Luther"), + ClassList([InputAttributes(name="John", surname="Luther"), InputAttributes(name="Bob")])), ]) def test_setitem(two_name_class_list: ClassList, new_item: InputAttributes, expected_classlist: ClassList) -> None: """We should be able to set values in an element of a ClassList using a new object.""" @@ -157,7 +157,7 @@ def test_setitem(two_name_class_list: ClassList, new_item: InputAttributes, expe @pytest.mark.parametrize("new_item", [ - (InputAttributes(name='Bob')), + (InputAttributes(name="Bob")), ]) def test_setitem_same_name_field(two_name_class_list: ClassList, new_item: InputAttributes) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" @@ -166,7 +166,7 @@ def test_setitem_same_name_field(two_name_class_list: ClassList, new_item: Input @pytest.mark.parametrize("new_values", [ - 'Bob', + "Bob", ]) def test_setitem_different_classes(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" @@ -189,10 +189,10 @@ def test_delitem_not_present(two_name_class_list: ClassList) -> None: @pytest.mark.parametrize("added_list", [ - (ClassList(InputAttributes(name='Eve'))), - ([InputAttributes(name='Eve')]), - (InputAttributes(name='Eve'),), - (InputAttributes(name='Eve')), + (ClassList(InputAttributes(name="Eve"))), + ([InputAttributes(name="Eve")]), + (InputAttributes(name="Eve"),), + (InputAttributes(name="Eve")), ]) def test_iadd(two_name_class_list: ClassList, added_list: Iterable, three_name_class_list: ClassList) -> None: """We should be able to use the "+=" operator to add iterables to a ClassList. Individual objects should be wrapped @@ -203,9 +203,9 @@ def test_iadd(two_name_class_list: ClassList, added_list: Iterable, three_name_c @pytest.mark.parametrize("added_list", [ - (ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob')])), - ([InputAttributes(name='Alice'), InputAttributes(name='Bob')]), - (InputAttributes(name='Alice'), InputAttributes(name='Bob')), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")])), + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), + (InputAttributes(name="Alice"), InputAttributes(name="Bob")), ]) def test_iadd_empty_classlist(added_list: Sequence, two_name_class_list: ClassList) -> None: """We should be able to use the "+=" operator to add iterables to an empty ClassList, whilst also setting @@ -244,7 +244,7 @@ def test_imul(two_name_class_list: ClassList) -> None: @pytest.mark.parametrize("new_object", [ - (InputAttributes(name='Eve')), + (InputAttributes(name="Eve")), ]) def test_append_object(two_name_class_list: ClassList, new_object: object, @@ -256,7 +256,7 @@ def test_append_object(two_name_class_list: ClassList, @pytest.mark.parametrize("new_values", [ - ({'name': 'Eve'}), + ({"name": "Eve"}), ]) def test_append_kwargs(two_name_class_list: ClassList, new_values: dict[str, Any], @@ -268,7 +268,7 @@ def test_append_kwargs(two_name_class_list: ClassList, @pytest.mark.parametrize(["new_object", "new_values"], [ - (InputAttributes(name='Eve'), {'name': 'John'}), + (InputAttributes(name="Eve"), {"name": "John"}), ]) def test_append_object_and_kwargs(two_name_class_list: ClassList, new_object: object, @@ -278,14 +278,14 @@ def test_append_object_and_kwargs(two_name_class_list: ClassList, discarding the keyword arguments.""" class_list = two_name_class_list with pytest.warns(SyntaxWarning): - warnings.warn('ClassList.append() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) + warnings.warn("ClassList.append() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) class_list.append(new_object, **new_values) assert class_list == three_name_class_list @pytest.mark.parametrize("new_object", [ - (InputAttributes(name='Alice')), + (InputAttributes(name="Alice")), ]) def test_append_object_empty_classlist(new_object: object, one_name_class_list: ClassList) -> None: """We should be able to append to an empty ClassList using a new object, whilst also setting _class_handle.""" @@ -296,19 +296,19 @@ def test_append_object_empty_classlist(new_object: object, one_name_class_list: @pytest.mark.parametrize("new_values", [ - ({'name': 'Alice'}), + ({"name": "Alice"}), ]) def test_append_kwargs_empty_classlist(new_values: dict[str, Any]) -> None: """If we append to an empty ClassList using keyword arguments we should raise a TypeError.""" class_list = ClassList() - with pytest.raises(TypeError, match=re.escape('ClassList.append() called with keyword arguments for a ClassList ' - 'without a class defined. Call ClassList.append() with an object to ' - 'define the class.')): + with pytest.raises(TypeError, match=re.escape("ClassList.append() called with keyword arguments for a ClassList " + "without a class defined. Call ClassList.append() with an object to " + "define the class.")): class_list.append(**new_values) @pytest.mark.parametrize("new_object", [ - (InputAttributes(name='Alice')), + (InputAttributes(name="Alice")), ]) def test_append_object_same_name_field(two_name_class_list: ClassList, new_object: object) -> None: """If we append an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" @@ -318,7 +318,7 @@ def test_append_object_same_name_field(two_name_class_list: ClassList, new_objec @pytest.mark.parametrize("new_values", [ - ({'name': 'Alice'}), + ({"name": "Alice"}), ]) def test_append_kwargs_same_name_field(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we append an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" @@ -329,29 +329,29 @@ def test_append_kwargs_same_name_field(two_name_class_list: ClassList, new_value @pytest.mark.parametrize("new_object", [ - (InputAttributes(name='Eve')) + (InputAttributes(name="Eve")) ]) def test_insert_object(two_name_class_list: ClassList, new_object: object) -> None: """We should be able to insert an object within a ClassList using a new object.""" two_name_class_list.insert(1, new_object) - assert two_name_class_list == ClassList([InputAttributes(name='Alice'), - InputAttributes(name='Eve'), - InputAttributes(name='Bob')]) + assert two_name_class_list == ClassList([InputAttributes(name="Alice"), + InputAttributes(name="Eve"), + InputAttributes(name="Bob")]) @pytest.mark.parametrize("new_values", [ - ({'name': 'Eve'}) + ({"name": "Eve"}) ]) def test_insert_kwargs(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """We should be able to insert an object within a ClassList using keyword arguments.""" two_name_class_list.insert(1, **new_values) - assert two_name_class_list == ClassList([InputAttributes(name='Alice'), - InputAttributes(name='Eve'), - InputAttributes(name='Bob')]) + assert two_name_class_list == ClassList([InputAttributes(name="Alice"), + InputAttributes(name="Eve"), + InputAttributes(name="Bob")]) @pytest.mark.parametrize(["new_object", "new_values"], [ - (InputAttributes(name='Eve'), {'name': 'John'}), + (InputAttributes(name="Eve"), {"name": "John"}), ]) def test_insert_object_and_kwargs(two_name_class_list: ClassList, new_object: object, @@ -361,16 +361,16 @@ def test_insert_object_and_kwargs(two_name_class_list: ClassList, object, discarding the keyword arguments.""" class_list = two_name_class_list with pytest.warns(SyntaxWarning): - warnings.warn('ClassList.insert() called with both an object and keyword arguments. ' - 'The keyword arguments will be ignored.', SyntaxWarning, stacklevel=2) + warnings.warn("ClassList.insert() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) class_list.insert(1, new_object, **new_values) - assert class_list == ClassList([InputAttributes(name='Alice'), - InputAttributes(name='Eve'), - InputAttributes(name='Bob')]) + assert class_list == ClassList([InputAttributes(name="Alice"), + InputAttributes(name="Eve"), + InputAttributes(name="Bob")]) @pytest.mark.parametrize("new_object", [ - (InputAttributes(name='Alice')), + (InputAttributes(name="Alice")), ]) def test_insert_object_empty_classlist(new_object: object, one_name_class_list: ClassList) -> None: """We should be able to insert a new object into an empty ClassList, whilst also setting _class_handle.""" @@ -381,19 +381,19 @@ def test_insert_object_empty_classlist(new_object: object, one_name_class_list: @pytest.mark.parametrize("new_values", [ - ({'name': 'Alice'}), + ({"name": "Alice"}), ]) def test_insert_kwargs_empty_classlist(new_values: dict[str, Any]) -> None: """If we append to an empty ClassList using keyword arguments we should raise a TypeError.""" class_list = ClassList() - with pytest.raises(TypeError, match=re.escape('ClassList.insert() called with keyword arguments for a ClassList ' - 'without a class defined. Call ClassList.insert() with an object to ' - 'define the class.')): + with pytest.raises(TypeError, match=re.escape("ClassList.insert() called with keyword arguments for a ClassList " + "without a class defined. Call ClassList.insert() with an object to " + "define the class.")): class_list.insert(0, **new_values) @pytest.mark.parametrize("new_object", [ - (InputAttributes(name='Alice')) + (InputAttributes(name="Alice")) ]) def test_insert_object_same_name(two_name_class_list: ClassList, new_object: object) -> None: """If we insert an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" @@ -403,7 +403,7 @@ def test_insert_object_same_name(two_name_class_list: ClassList, new_object: obj @pytest.mark.parametrize("new_values", [ - ({'name': 'Alice'}) + ({"name": "Alice"}) ]) def test_insert_kwargs_same_name(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we insert an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" @@ -415,18 +415,18 @@ def test_insert_kwargs_same_name(two_name_class_list: ClassList, new_values: dic @pytest.mark.parametrize("remove_value", [ "Bob", - (InputAttributes(name='Bob')), + (InputAttributes(name="Bob")), ]) def test_remove(two_name_class_list: ClassList, remove_value: Union[object, str]) -> None: """We should be able to remove an object either by the value of the name_field or by specifying the object itself.""" two_name_class_list.remove(remove_value) - assert two_name_class_list == ClassList([InputAttributes(name='Alice')]) + assert two_name_class_list == ClassList([InputAttributes(name="Alice")]) @pytest.mark.parametrize("remove_value", [ - 'Eve', - (InputAttributes(name='Eve')), + "Eve", + (InputAttributes(name="Eve")), ]) def test_remove_not_present(two_name_class_list: ClassList, remove_value: Union[object, str]) -> None: """If we remove an object not included in the ClassList we should raise a ValueError.""" @@ -435,10 +435,10 @@ def test_remove_not_present(two_name_class_list: ClassList, remove_value: Union[ @pytest.mark.parametrize(["count_value", "expected_count"], [ - ('Bob', 1), - (InputAttributes(name='Bob'), 1), - ('Eve', 0), - (InputAttributes(name='Eve'), 0), + ("Bob", 1), + (InputAttributes(name="Bob"), 1), + ("Eve", 0), + (InputAttributes(name="Eve"), 0), ]) def test_count(two_name_class_list: ClassList, count_value: Union[object, str], expected_count: int) -> None: """We should be able to determine the number of times an object is in the ClassList using either the object itself @@ -448,8 +448,8 @@ def test_count(two_name_class_list: ClassList, count_value: Union[object, str], @pytest.mark.parametrize(["index_value", "expected_index"], [ - ('Bob', 1), - (InputAttributes(name='Bob'), 1), + ("Bob", 1), + (InputAttributes(name="Bob"), 1), ]) def test_index(two_name_class_list: ClassList, index_value: Union[object, str], expected_index: int) -> None: """We should be able to find the index of an object in the ClassList either by its name_field value or by @@ -459,8 +459,8 @@ def test_index(two_name_class_list: ClassList, index_value: Union[object, str], @pytest.mark.parametrize(["index_value", "offset", "expected_index"], [ - ('Bob', 1, 2), - (InputAttributes(name='Bob'), -3, -2), + ("Bob", 1, 2), + (InputAttributes(name="Bob"), -3, -2), ]) def test_index_offset(two_name_class_list: ClassList, index_value: Union[object, str], offset: int, expected_index: int) -> None: @@ -470,8 +470,8 @@ def test_index_offset(two_name_class_list: ClassList, index_value: Union[object, assert two_name_class_list.index(index_value, offset) == expected_index @pytest.mark.parametrize("index_value", [ - 'Eve', - (InputAttributes(name='Eve')), + "Eve", + (InputAttributes(name="Eve")), ]) def test_index_not_present(two_name_class_list: ClassList, index_value: Union[object, str]) -> None: """If we try to find the index of an object not included in the ClassList we should raise a ValueError.""" @@ -481,10 +481,10 @@ def test_index_not_present(two_name_class_list: ClassList, index_value: Union[ob @pytest.mark.parametrize("extended_list", [ - (ClassList(InputAttributes(name='Eve'))), - ([InputAttributes(name='Eve')]), - (InputAttributes(name='Eve'),), - (InputAttributes(name='Eve')), + (ClassList(InputAttributes(name="Eve"))), + ([InputAttributes(name="Eve")]), + (InputAttributes(name="Eve"),), + (InputAttributes(name="Eve")), ]) def test_extend(two_name_class_list: ClassList, extended_list: Sequence, three_name_class_list: ClassList) -> None: """We should be able to extend a ClassList using another ClassList or a sequence. Individual objects should be @@ -495,9 +495,9 @@ def test_extend(two_name_class_list: ClassList, extended_list: Sequence, three_n @pytest.mark.parametrize("extended_list", [ - (ClassList(InputAttributes(name='Alice'))), - ([InputAttributes(name='Alice')]), - (InputAttributes(name='Alice'),), + (ClassList(InputAttributes(name="Alice"))), + ([InputAttributes(name="Alice")]), + (InputAttributes(name="Alice"),), ]) def test_extend_empty_classlist(extended_list: Sequence, one_name_class_list: ClassList) -> None: """We should be able to extend a ClassList using another ClassList or a sequence""" @@ -508,9 +508,9 @@ def test_extend_empty_classlist(extended_list: Sequence, one_name_class_list: Cl @pytest.mark.parametrize(["new_values", "expected_classlist"], [ - ({'name': 'Eve'}, ClassList([InputAttributes(name='Eve'), InputAttributes(name='Bob')])), - ({'name': 'John', 'surname': 'Luther'}, - ClassList([InputAttributes(name='John', surname='Luther'), InputAttributes(name='Bob')])), + ({"name": "Eve"}, ClassList([InputAttributes(name="Eve"), InputAttributes(name="Bob")])), + ({"name": "John", "surname": "Luther"}, + ClassList([InputAttributes(name="John", surname="Luther"), InputAttributes(name="Bob")])), ]) def test_set_fields(two_name_class_list: ClassList, new_values: dict[str, Any], expected_classlist: ClassList)\ -> None: @@ -521,7 +521,7 @@ def test_set_fields(two_name_class_list: ClassList, new_values: dict[str, Any], @pytest.mark.parametrize("new_values", [ - ({'name': 'Bob'}), + ({"name": "Bob"}), ]) def test_set_fields_same_name_field(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" @@ -532,13 +532,13 @@ def test_set_fields_same_name_field(two_name_class_list: ClassList, new_values: @pytest.mark.parametrize(["class_list", "expected_names"], [ - (ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob')]), ["Alice", "Bob"]), - (ClassList([InputAttributes(id='Alice'), InputAttributes(id='Bob')], name_field='id'), ["Alice", "Bob"]), - (ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob')], name_field='id'), []), - (ClassList([InputAttributes(surname='Morgan'), InputAttributes(surname='Terwilliger')]), []), - (ClassList([InputAttributes(name='Alice', surname='Morgan'), InputAttributes(surname='Terwilliger')]), ["Alice"]), - (ClassList([InputAttributes(name='Alice', surname='Morgan'), InputAttributes(surname='Terwilliger')], - name_field='surname'), ["Morgan", "Terwilliger"]), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), ["Alice", "Bob"]), + (ClassList([InputAttributes(id="Alice"), InputAttributes(id="Bob")], name_field="id"), ["Alice", "Bob"]), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")], name_field="id"), []), + (ClassList([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), []), + (ClassList([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")]), ["Alice"]), + (ClassList([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")], + name_field="surname"), ["Morgan", "Terwilliger"]), (ClassList(InputAttributes()), []), ]) def test_get_names(class_list: ClassList, expected_names: list[str]) -> None: @@ -548,9 +548,9 @@ def test_get_names(class_list: ClassList, expected_names: list[str]) -> None: @pytest.mark.parametrize(["class_list", "expected_matches"], [ - (ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob')]), [(0, 'name')]), - (ClassList([InputAttributes(name='Alice'), InputAttributes(name='Bob', id='Alice')]), [(0, 'name'), (1, 'id')]), - (ClassList([InputAttributes(surname='Morgan'), InputAttributes(surname='Terwilliger')]), []), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), [(0, "name")]), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob", id="Alice")]), [(0, "name"), (1, "id")]), + (ClassList([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), []), (ClassList(InputAttributes()), []), ]) def test_get_all_matches(class_list: ClassList, expected_matches: list[tuple]) -> None: @@ -559,8 +559,8 @@ def test_get_all_matches(class_list: ClassList, expected_matches: list[tuple]) - @pytest.mark.parametrize("input_dict", [ - ({'name': 'Eve'}), - ({'surname': 'Polastri'}), + ({"name": "Eve"}), + ({"surname": "Polastri"}), ]) def test__validate_name_field(two_name_class_list: ClassList, input_dict: dict[str, Any]) -> None: """We should not raise an error if the input values do not contain a name_field value defined in an object in the @@ -569,7 +569,7 @@ def test__validate_name_field(two_name_class_list: ClassList, input_dict: dict[s @pytest.mark.parametrize("input_dict", [ - ({'name': 'Alice'}), + ({"name": "Alice"}), ]) def test__validate_name_field_not_unique(two_name_class_list: ClassList, input_dict: dict[str, Any]) -> None: """We should raise a ValueError if we input values containing a name_field defined in an object in the ClassList.""" @@ -580,9 +580,9 @@ def test__validate_name_field_not_unique(two_name_class_list: ClassList, input_d @pytest.mark.parametrize("input_list", [ - ([InputAttributes(name='Alice'), InputAttributes(name='Bob')]), - ([InputAttributes(surname='Morgan'), InputAttributes(surname='Terwilliger')]), - ([InputAttributes(name='Alice', surname='Morgan'), InputAttributes(surname='Terwilliger')]), + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), + ([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), + ([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")]), ([InputAttributes()]), ([]), ]) @@ -593,7 +593,7 @@ def test__check_unique_name_fields(two_name_class_list: ClassList, input_list: I @pytest.mark.parametrize("input_list", [ - ([InputAttributes(name='Alice'), InputAttributes(name='Alice')]), + ([InputAttributes(name="Alice"), InputAttributes(name="Alice")]), ]) def test__check_unique_name_fields_not_unique(two_name_class_list: ClassList, input_list: Iterable) -> None: """We should raise a ValueError if an input list contains multiple objects with matching name_field values @@ -604,7 +604,7 @@ def test__check_unique_name_fields_not_unique(two_name_class_list: ClassList, in @pytest.mark.parametrize("input_list", [ - ([InputAttributes(name='Alice'), InputAttributes(name='Bob')]), + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), ]) def test__check_classes(input_list: Iterable) -> None: """We should not raise an error all objects in the ClassList are of the same type.""" @@ -613,7 +613,7 @@ def test__check_classes(input_list: Iterable) -> None: @pytest.mark.parametrize("input_list", [ - ([InputAttributes(name='Alice'), dict(name='Bob')]), + ([InputAttributes(name="Alice"), dict(name="Bob")]), ]) def test__check_classes_different_classes(input_list: Iterable) -> None: """We should raise a ValueError if an input list contains objects of different types.""" @@ -624,7 +624,7 @@ def test__check_classes_different_classes(input_list: Iterable) -> None: @pytest.mark.parametrize(["value", "expected_output"], [ - ("Alice", InputAttributes(name='Alice')), + ("Alice", InputAttributes(name="Alice")), ("Eve", "Eve"), ]) def test__get_item_from_name_field(two_name_class_list: ClassList, @@ -637,13 +637,13 @@ def test__get_item_from_name_field(two_name_class_list: ClassList, @pytest.mark.parametrize(["input_list", "expected_type"], [ - ([InputAttributes(name='Alice')], InputAttributes), - ([InputAttributes(name='Alice'), SubInputAttributes(name='Bob')], InputAttributes), - ([SubInputAttributes(name='Alice'), InputAttributes(name='Bob')], InputAttributes), - ([SubInputAttributes(name='Alice'), SubInputAttributes(name='Bob')], SubInputAttributes), - ([SubInputAttributes(name='Alice'), SubInputAttributes(name='Bob'), InputAttributes(name='Eve')], InputAttributes), - ([InputAttributes(name='Alice'), dict(name='Bob')], InputAttributes), - ([dict(name='Alice'), InputAttributes(name='Bob')], dict), + ([InputAttributes(name="Alice")], InputAttributes), + ([InputAttributes(name="Alice"), SubInputAttributes(name="Bob")], InputAttributes), + ([SubInputAttributes(name="Alice"), InputAttributes(name="Bob")], InputAttributes), + ([SubInputAttributes(name="Alice"), SubInputAttributes(name="Bob")], SubInputAttributes), + ([SubInputAttributes(name="Alice"), SubInputAttributes(name="Bob"), InputAttributes(name="Eve")], InputAttributes), + ([InputAttributes(name="Alice"), dict(name="Bob")], InputAttributes), + ([dict(name="Alice"), InputAttributes(name="Bob")], dict), ]) def test_determine_class_handle(input_list: ClassList, expected_type: type) -> None: """The _class_handle for the ClassList should be the type that satisfies the condition "isinstance(element, type)" diff --git a/tests/test_controls.py b/tests/test_controls.py index b6255c61..17b79381 100644 --- a/tests/test_controls.py +++ b/tests/test_controls.py @@ -17,47 +17,47 @@ def setup_class(self): self.calculate = Calculate() @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Single), - ('calcSldDuringFit', False), - ('resampleParams', [0.9, 50]), - ('display', Display.Iter), - ('procedure', Procedures.Calculate) + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.Calculate) ]) def test_calculate_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Calculate class.""" assert getattr(self.calculate, control_property) == value @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Points), - ('calcSldDuringFit', True), - ('resampleParams', [0.2, 1]), - ('display', Display.Notify) + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify) ]) def test_calculate_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of Calculate class.""" setattr(self.calculate, control_property, value) assert getattr(self.calculate, control_property) == value - @pytest.mark.parametrize("value", ['test', 'ALL', 'Contrast', True, 1, 3.0]) + @pytest.mark.parametrize("value", ["test", "ALL", "Contrast", True, 1, 3.0]) def test_calculate_parallel_validation(self, value: Any) -> None: """Tests the parallel setter validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.parallel = value - assert exp.value.errors()[0]['msg'] == "Input should be 'single', 'points' or 'contrasts'" + assert exp.value.errors()[0]["msg"] == "Input should be 'single', 'points' or 'contrasts'" @pytest.mark.parametrize("value", [5.0, 12]) def test_calculate_calcSldDuringFit_validation(self, value: Union[int, float]) -> None: """Tests the calcSldDuringFit setter validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.calcSldDuringFit = value - assert exp.value.errors()[0]['msg'] == "Input should be a valid boolean, unable to interpret input" + assert exp.value.errors()[0]["msg"] == "Input should be a valid boolean, unable to interpret input" - @pytest.mark.parametrize("value", ['test', 'iterate', "FINAL", True, 1, 3.0]) + @pytest.mark.parametrize("value", ["test", "iterate", "FINAL", True, 1, 3.0]) def test_calculate_display_validation(self, value: Any) -> None: """Tests the display setter validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.display = value - assert exp.value.errors()[0]['msg'] == "Input should be 'off', 'iter', 'notify' or 'final'" + assert exp.value.errors()[0]["msg"] == "Input should be 'off', 'iter', 'notify' or 'final'" @pytest.mark.parametrize("value, msg", [ ([5.0], "List should have at least 2 items after validation, not 1"), @@ -67,7 +67,7 @@ def test_calculate_resampleParams_length_validation(self, value: list, msg: str) """Tests the resampleParams setter length validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.resampleParams = value - assert exp.value.errors()[0]['msg'] == msg + assert exp.value.errors()[0]["msg"] == msg @pytest.mark.parametrize("value, msg", [ ([1.0, 2], "Value error, resampleParams[0] must be between 0 and 1"), @@ -77,25 +77,25 @@ def test_calculate_resampleParams_value_validation(self, value: list, msg: str) """Tests the resampleParams setter value validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.resampleParams = value - assert exp.value.errors()[0]['msg'] == msg + assert exp.value.errors()[0]["msg"] == msg def test_calculate_extra_property_error(self) -> None: """Tests the extra property setter in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.test = 1 - assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" + assert exp.value.errors()[0]["msg"] == "Object has no attribute 'test'" def test_calculate_initialise_procedure_error(self) -> None: """Tests the procedure property can only be initialised as "calculate" in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: - Calculate(procedure='test') - assert exp.value.errors()[0]['msg'] == "Input should be " + Calculate(procedure="test") + assert exp.value.errors()[0]["msg"] == "Input should be " def test_calculate_set_procedure_error(self) -> None: """Tests the procedure property is frozen in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.procedure = "test" - assert exp.value.errors()[0]['msg'] == "Input should be " + assert exp.value.errors()[0]["msg"] == "Input should be " def test_repr(self) -> None: """Tests the Calculate model __repr__.""" @@ -122,33 +122,33 @@ def setup_class(self): self.simplex = Simplex() @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Single), - ('calcSldDuringFit', False), - ('resampleParams', [0.9, 50]), - ('display', Display.Iter), - ('procedure', Procedures.Simplex), - ('xTolerance', 1e-6), - ('funcTolerance', 1e-6), - ('maxFuncEvals', 10000), - ('maxIterations', 1000), - ('updateFreq', -1), - ('updatePlotFreq', 1) + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.Simplex), + ("xTolerance", 1e-6), + ("funcTolerance", 1e-6), + ("maxFuncEvals", 10000), + ("maxIterations", 1000), + ("updateFreq", -1), + ("updatePlotFreq", 1) ]) def test_simplex_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Simplex class.""" assert getattr(self.simplex, control_property) == value @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Points), - ('calcSldDuringFit', True), - ('resampleParams', [0.2, 1]), - ('display', Display.Notify), - ('xTolerance', 4e-6), - ('funcTolerance', 3e-4), - ('maxFuncEvals', 100), - ('maxIterations', 50), - ('updateFreq', 4), - ('updatePlotFreq', 3) + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("xTolerance", 4e-6), + ("funcTolerance", 3e-4), + ("maxFuncEvals", 100), + ("maxIterations", 50), + ("updateFreq", 4), + ("updatePlotFreq", 3) ]) def test_simplex_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of Simplex class.""" @@ -156,34 +156,34 @@ def test_simplex_property_setters(self, control_property: str, value: Any) -> N assert getattr(self.simplex, control_property) == value @pytest.mark.parametrize("control_property, value", [ - ('xTolerance', -4e-6), - ('funcTolerance', -3e-4), - ('maxFuncEvals', -100), - ('maxIterations', -50) + ("xTolerance", -4e-6), + ("funcTolerance", -3e-4), + ("maxFuncEvals", -100), + ("maxIterations", -50) ]) def test_simplex_property_errors(self, control_property: str, value: Union[float, int]) -> None: """Tests the property errors of Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.simplex, control_property, value) - assert exp.value.errors()[0]['msg'] == "Input should be greater than 0" + assert exp.value.errors()[0]["msg"] == "Input should be greater than 0" def test_simplex_extra_property_error(self) -> None: """Tests the extra property setter in Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: self.simplex.test = 1 - assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" + assert exp.value.errors()[0]["msg"] == "Object has no attribute 'test'" def test_simplex_initialise_procedure_error(self) -> None: """Tests the procedure property can only be initialised as "simplex" in Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: - Simplex(procedure='test') - assert exp.value.errors()[0]['msg'] == "Input should be " + Simplex(procedure="test") + assert exp.value.errors()[0]["msg"] == "Input should be " def test_simplex_set_procedure_error(self) -> None: """Tests the procedure property is frozen in Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: self.simplex.procedure = "test" - assert exp.value.errors()[0]['msg'] == "Input should be " + assert exp.value.errors()[0]["msg"] == "Input should be " def test_repr(self) -> None: """Tests the Simplex model __repr__.""" @@ -216,33 +216,33 @@ def setup_class(self): self.de = DE() @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Single), - ('calcSldDuringFit', False), - ('resampleParams', [0.9, 50]), - ('display', Display.Iter), - ('procedure', Procedures.DE), - ('populationSize', 20), - ('fWeight', 0.5), - ('crossoverProbability', 0.8), - ('strategy', Strategies.RandomWithPerVectorDither), - ('targetValue', 1), - ('numGenerations', 500) + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.DE), + ("populationSize", 20), + ("fWeight", 0.5), + ("crossoverProbability", 0.8), + ("strategy", Strategies.RandomWithPerVectorDither), + ("targetValue", 1), + ("numGenerations", 500) ]) def test_de_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of DE class.""" assert getattr(self.de, control_property) == value @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Points), - ('calcSldDuringFit', True), - ('resampleParams', [0.2, 1]), - ('display', Display.Notify), - ('populationSize', 20), - ('fWeight', 0.3), - ('crossoverProbability', 0.4), - ('strategy', Strategies.BestWithJitter), - ('targetValue', 2.0), - ('numGenerations', 50) + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("populationSize", 20), + ("fWeight", 0.3), + ("crossoverProbability", 0.4), + ("strategy", Strategies.BestWithJitter), + ("targetValue", 2.0), + ("numGenerations", 50) ]) def test_de_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of DE class.""" @@ -257,15 +257,15 @@ def test_de_crossoverProbability_error(self, value: int, msg: str) -> None: """Tests the crossoverProbability setter error in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: self.de.crossoverProbability = value - assert exp.value.errors()[0]['msg'] == msg + assert exp.value.errors()[0]["msg"] == msg @pytest.mark.parametrize("control_property, value", [ - ('targetValue', 0), - ('targetValue', 0.999), - ('numGenerations', -500), - ('numGenerations', 0), - ('populationSize', 0), - ('populationSize', -1) + ("targetValue", 0), + ("targetValue", 0.999), + ("numGenerations", -500), + ("numGenerations", 0), + ("populationSize", 0), + ("populationSize", -1) ]) def test_de_targetValue_numGenerations_populationSize_error(self, control_property: str, @@ -273,25 +273,25 @@ def test_de_targetValue_numGenerations_populationSize_error(self, """Tests the targetValue, numGenerations, populationSize setter error in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.de, control_property, value) - assert exp.value.errors()[0]['msg'] == "Input should be greater than or equal to 1" + assert exp.value.errors()[0]["msg"] == "Input should be greater than or equal to 1" def test_de_extra_property_error(self) -> None: """Tests the extra property setter in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: self.de.test = 1 - assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" + assert exp.value.errors()[0]["msg"] == "Object has no attribute 'test'" def test_de_initialise_procedure_error(self) -> None: """Tests the procedure property can only be initialised as "de" in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: - DE(procedure='test') - assert exp.value.errors()[0]['msg'] == "Input should be " + DE(procedure="test") + assert exp.value.errors()[0]["msg"] == "Input should be " def test_de_set_procedure_error(self) -> None: """Tests the procedure property is frozen in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: self.de.procedure = "test" - assert exp.value.errors()[0]['msg'] == "Input should be " + assert exp.value.errors()[0]["msg"] == "Input should be " def test_repr(self) -> None: """Tests the DE model __repr__.""" @@ -324,29 +324,29 @@ def setup_class(self): self.ns = NS() @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Single), - ('calcSldDuringFit', False), - ('resampleParams', [0.9, 50]), - ('display', Display.Iter), - ('procedure', Procedures.NS), - ('nLive', 150), - ('nMCMC', 0), - ('propScale', 0.1), - ('nsTolerance', 0.1) + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.NS), + ("nLive", 150), + ("nMCMC", 0), + ("propScale", 0.1), + ("nsTolerance", 0.1) ]) def test_ns_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of NS class.""" assert getattr(self.ns, control_property) == value @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Points), - ('calcSldDuringFit', True), - ('resampleParams', [0.2, 1]), - ('display', Display.Notify), - ('nLive', 1500), - ('nMCMC', 1), - ('propScale', 0.5), - ('nsTolerance', 0.8) + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("nLive", 1500), + ("nMCMC", 1), + ("propScale", 0.5), + ("nsTolerance", 0.8) ]) def test_ns_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of NS class.""" @@ -354,15 +354,15 @@ def test_ns_property_setters(self, control_property: str, value: Any) -> None: assert getattr(self.ns, control_property) == value @pytest.mark.parametrize("control_property, value, bound", [ - ('nMCMC', -0.6, 0), - ('nsTolerance', -500, 0), - ('nLive', -500, 1) + ("nMCMC", -0.6, 0), + ("nsTolerance", -500, 0), + ("nLive", -500, 1) ]) def test_ns_setter_error(self, control_property: str, value: Union[int, float], bound: int) -> None: """Tests the nMCMC, nsTolerance, nLive setter error in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.ns, control_property, value) - assert exp.value.errors()[0]['msg'] == f"Input should be greater than or equal to {bound}" + assert exp.value.errors()[0]["msg"] == f"Input should be greater than or equal to {bound}" @pytest.mark.parametrize("value, msg", [ (0, "Input should be greater than 0"), @@ -372,25 +372,25 @@ def test_ns_propScale_error(self, value: int, msg: str) -> None: """Tests the propScale error in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: self.ns.propScale = value - assert exp.value.errors()[0]['msg'] == msg + assert exp.value.errors()[0]["msg"] == msg def test_ns_extra_property_error(self) -> None: """Tests the extra property setter in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: self.ns.test = 1 - assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" + assert exp.value.errors()[0]["msg"] == "Object has no attribute 'test'" def test_ns_initialise_procedure_error(self) -> None: """Tests the procedure property can only be initialised as "ns" in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: - NS(procedure='test') - assert exp.value.errors()[0]['msg'] == "Input should be " + NS(procedure="test") + assert exp.value.errors()[0]["msg"] == "Input should be " def test_ns_procedure_error(self) -> None: """Tests the procedure property is frozen in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: self.ns.procedure = "test" - assert exp.value.errors()[0]['msg'] == "Input should be " + assert exp.value.errors()[0]["msg"] == "Input should be " def test_control_class_ns_repr(self) -> None: """Tests the NS model __repr__.""" @@ -421,31 +421,31 @@ def setup_class(self): self.dream = Dream() @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Single), - ('calcSldDuringFit', False), - ('resampleParams', [0.9, 50]), - ('display', Display.Iter), - ('procedure', Procedures.Dream), - ('nSamples', 50000), - ('nChains', 10), - ('jumpProbability', 0.5), - ('pUnitGamma', 0.2), - ('boundHandling', BoundHandling.Fold) + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.Dream), + ("nSamples", 50000), + ("nChains", 10), + ("jumpProbability", 0.5), + ("pUnitGamma", 0.2), + ("boundHandling", BoundHandling.Fold) ]) def test_dream_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Dream class.""" assert getattr(self.dream, control_property) == value @pytest.mark.parametrize("control_property, value", [ - ('parallel', Parallel.Points), - ('calcSldDuringFit', True), - ('resampleParams', [0.2, 1]), - ('display', Display.Notify), - ('nSamples', 500), - ('nChains', 1000), - ('jumpProbability', 0.7), - ('pUnitGamma', 0.3), - ('boundHandling', BoundHandling.Reflect) + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("nSamples", 500), + ("nChains", 1000), + ("jumpProbability", 0.7), + ("pUnitGamma", 0.3), + ("boundHandling", BoundHandling.Reflect) ]) def test_dream_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters in Dream class.""" @@ -453,48 +453,48 @@ def test_dream_property_setters(self, control_property: str, value: Any) -> Non assert getattr(self.dream, control_property) == value @pytest.mark.parametrize("control_property, value, msg", [ - ('jumpProbability', 0, "Input should be greater than 0"), - ('jumpProbability', 2, "Input should be less than 1"), - ('pUnitGamma', -5, "Input should be greater than 0"), - ('pUnitGamma', 20, "Input should be less than 1") + ("jumpProbability", 0, "Input should be greater than 0"), + ("jumpProbability", 2, "Input should be less than 1"), + ("pUnitGamma", -5, "Input should be greater than 0"), + ("pUnitGamma", 20, "Input should be less than 1") ]) def test_dream_jumpProbability_pUnitGamma_error(self, control_property: str, value: int, msg: str) -> None: """Tests the jumpProbability and pUnitGamma setter errors in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.dream, control_property, value) - assert exp.value.errors()[0]['msg'] == msg + assert exp.value.errors()[0]["msg"] == msg @pytest.mark.parametrize("value", [-80, -2]) def test_dream_nSamples_error(self, value: int) -> None: """Tests the nSamples setter error in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: self.dream.nSamples = value - assert exp.value.errors()[0]['msg'] == "Input should be greater than or equal to 0" + assert exp.value.errors()[0]["msg"] == "Input should be greater than or equal to 0" @pytest.mark.parametrize("value", [-5, 0]) def test_dream_nChains_error(self, value: int) -> None: """Tests the nChains setter error in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: self.dream.nChains = value - assert exp.value.errors()[0]['msg'] == "Input should be greater than 0" + assert exp.value.errors()[0]["msg"] == "Input should be greater than 0" def test_dream_extra_property_error(self) -> None: """Tests the extra property setter in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: self.dream.test = 1 - assert exp.value.errors()[0]['msg'] == "Object has no attribute 'test'" + assert exp.value.errors()[0]["msg"] == "Object has no attribute 'test'" def test_dream_initialise_procedure_error(self) -> None: """Tests the procedure property can only be initialised as "dream" in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: - Dream(procedure='test') - assert exp.value.errors()[0]['msg'] == "Input should be " + Dream(procedure="test") + assert exp.value.errors()[0]["msg"] == "Input should be " def test_dream_procedure_error(self) -> None: """Tests the procedure property is frozen in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: self.dream.procedure = "test" - assert exp.value.errors()[0]['msg'] == "Input should be " + assert exp.value.errors()[0]["msg"] == "Input should be " def test_control_class_dream_repr(self) -> None: """Tests the Dream model __repr__.""" @@ -519,11 +519,11 @@ def test_control_class_dream_repr(self) -> None: assert table == table_str @pytest.mark.parametrize(["procedure", "expected_model"], [ - ('calculate', Calculate), - ('simplex', Simplex), - ('de', DE), - ('ns', NS), - ('dream', Dream) + ("calculate", Calculate), + ("simplex", Simplex), + ("de", DE), + ("ns", NS), + ("dream", Dream) ]) def test_set_controls(procedure: Procedures, expected_model: Union[Calculate, Simplex, DE, NS, Dream]) -> None: """We should return the correct model given the value of procedure.""" @@ -541,15 +541,15 @@ def test_set_controls_invalid_procedure() -> None: """We should return the default model when we call "set_controls" without specifying a procedure.""" with pytest.raises(ValueError, match="The controls procedure must be one of: 'calculate', 'simplex', 'de', 'ns' " "or 'dream'"): - set_controls('invalid') + set_controls("invalid") @pytest.mark.parametrize(["procedure", "expected_model"], [ - ('calculate', Calculate), - ('simplex', Simplex), - ('de', DE), - ('ns', NS), - ('dream', Dream) + ("calculate", Calculate), + ("simplex", Simplex), + ("de", DE), + ("ns", NS), + ("dream", Dream) ]) def test_set_controls_extra_fields(procedure: Procedures, expected_model: Union[Calculate, Simplex, DE, NS, Dream])\ -> None: @@ -560,4 +560,4 @@ def test_set_controls_extra_fields(procedure: Procedures, expected_model: Union[ f'extra_field\n Extra inputs are not permitted. The fields for ' f'the {procedure} controls procedure are:\n ' f'{", ".join(expected_model.model_fields.keys())}\n'): - set_controls(procedure, extra_field='invalid') + set_controls(procedure, extra_field="invalid") diff --git a/tests/test_custom_errors.py b/tests/test_custom_errors.py index c6898cac..3d7a5f40 100644 --- a/tests/test_custom_errors.py +++ b/tests/test_custom_errors.py @@ -10,7 +10,7 @@ @pytest.fixture def TestModel(): """Create a custom pydantic model for the tests.""" - TestModel = create_model('TestModel', int_field=(int, 1), str_field=(str, 'a'), __config__={'extra': 'forbid'}) + TestModel = create_model("TestModel", int_field=(int, 1), str_field=(str, "a"), __config__={"extra": "forbid"}) return TestModel @@ -19,7 +19,7 @@ def TestModel(): "2 validation errors for TestModel\nint_field\n Input should be a valid integer, unable to parse string as an " "integer [type=int_parsing, input_value='string', input_type=str]\nstr_field\n Input should be a valid string " "[type=string_type, input_value=5, input_type=int]"), - ({'int_parsing': 'This is a custom error message', 'string_type': 'This is another custom error message'}, + ({"int_parsing": "This is a custom error message", "string_type": "This is another custom error message"}, "2 validation errors for TestModel\nint_field\n This is a custom error message [type=int_parsing, " "input_value='string', input_type=str]\nstr_field\n This is another custom error message [type=string_type, " "input_value=5, input_type=int]"), @@ -30,9 +30,9 @@ def test_custom_pydantic_validation_error(TestModel, custom_errors: dict[str, st PydanticCustomErrors, otherwise we return the original set of errors. """ try: - TestModel(int_field='string', str_field=5) + TestModel(int_field="string", str_field=5) except ValidationError as exc: custom_error_list = RAT.utils.custom_errors.custom_pydantic_validation_error(exc.errors(), custom_errors) with pytest.raises(ValidationError, match=re.escape(expected_error_message)): - raise ValidationError.from_exception_data('TestModel', custom_error_list) + raise ValidationError.from_exception_data("TestModel", custom_error_list) diff --git a/tests/test_inputs.py b/tests/test_inputs.py index af781a03..dd3ea218 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -27,18 +27,18 @@ @pytest.fixture def standard_layers_project(): """Add parameters to the default project for a non polarised calculation.""" - test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name='Test Data', data=np.array([[1.0, 1.0, 1.0]]))]) + test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]) ) - test_project.parameters.append(name='Test Thickness') - test_project.parameters.append(name='Test SLD') - test_project.parameters.append(name='Test Roughness') - test_project.custom_files.append(name='Test Custom File', filename='python_test.py', function_name='dummy_function', - language='python') - test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness' + test_project.parameters.append(name="Test Thickness") + test_project.parameters.append(name="Test SLD") + test_project.parameters.append(name="Test Roughness") + test_project.custom_files.append(name="Test Custom File", filename="python_test.py", function_name="dummy_function", + language="python") + test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness" ) - test_project.contrasts.append(name='Test Contrast', data='Test Data', background='Background 1', bulk_in='SLD Air', - bulk_out='SLD D2O', scalefactor='Scalefactor 1', resolution='Resolution 1', - model=['Test Layer']) + test_project.contrasts.append(name="Test Contrast", data="Test Data", background="Background 1", bulk_in="SLD Air", + bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", + model=["Test Layer"]) return test_project @@ -46,19 +46,19 @@ def standard_layers_project(): def domains_project(): """Add parameters to the default project for a domains calculation.""" test_project = RAT.Project(calculation=Calculations.Domains, - data=RAT.ClassList([RAT.models.Data(name='Test Data', data=np.array([[1.0, 1.0, 1.0]]))]) + data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]) ) - test_project.parameters.append(name='Test Thickness') - test_project.parameters.append(name='Test SLD') - test_project.parameters.append(name='Test Roughness') - test_project.custom_files.append(name='Test Custom File', filename='matlab_test.m', language='matlab') - test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness' + test_project.parameters.append(name="Test Thickness") + test_project.parameters.append(name="Test SLD") + test_project.parameters.append(name="Test Roughness") + test_project.custom_files.append(name="Test Custom File", filename="matlab_test.m", language="matlab") + test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness" ) - test_project.domain_contrasts.append(name='up', model=['Test Layer']) - test_project.domain_contrasts.append(name='down', model=['Test Layer']) - test_project.contrasts.append(name='Test Contrast', data='Test Data', background='Background 1', bulk_in='SLD Air', - bulk_out='SLD D2O', scalefactor='Scalefactor 1', resolution='Resolution 1', - domain_ratio='Domain Ratio 1', model=['down', 'up']) + test_project.domain_contrasts.append(name="up", model=["Test Layer"]) + test_project.domain_contrasts.append(name="down", model=["Test Layer"]) + test_project.contrasts.append(name="Test Contrast", data="Test Data", background="Background 1", bulk_in="SLD Air", + bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", + domain_ratio="Domain Ratio 1", model=["down", "up"]) return test_project @@ -66,13 +66,13 @@ def domains_project(): def custom_xy_project(): """Add parameters to the default project for a non polarised calculation and use the custom xy model.""" test_project = RAT.Project(model=LayerModels.CustomXY) - test_project.parameters.append(name='Test Thickness') - test_project.parameters.append(name='Test SLD') - test_project.parameters.append(name='Test Roughness') - test_project.custom_files.append(name='Test Custom File', filename='cpp_test.dll', language='cpp') - test_project.contrasts.append(name='Test Contrast', data='Simulation', background='Background 1', bulk_in='SLD Air', - bulk_out='SLD D2O', scalefactor='Scalefactor 1', resolution='Resolution 1', - model=['Test Custom File']) + test_project.parameters.append(name="Test Thickness") + test_project.parameters.append(name="Test SLD") + test_project.parameters.append(name="Test Roughness") + test_project.custom_files.append(name="Test Custom File", filename="cpp_test.dll", language="cpp") + test_project.contrasts.append(name="Test Contrast", data="Simulation", background="Background 1", bulk_in="SLD Air", + bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", + model=["Test Custom File"]) return test_project @@ -99,7 +99,7 @@ def standard_layers_problem(): problem.contrastBackgroundParams = [1] problem.contrastBackgroundActions = [1] problem.contrastResolutionParams = [1] - problem.contrastCustomFiles = [float('NaN')] + problem.contrastCustomFiles = [float("NaN")] problem.contrastDomainRatios = [0] problem.resample = [False] problem.dataPresent = [1] @@ -139,7 +139,7 @@ def domains_problem(): problem.contrastBackgroundParams = [1] problem.contrastBackgroundActions = [1] problem.contrastResolutionParams = [1] - problem.contrastCustomFiles = [float('NaN')] + problem.contrastCustomFiles = [float("NaN")] problem.contrastDomainRatios = [1] problem.resample = [False] problem.dataPresent = [1] @@ -205,14 +205,14 @@ def standard_layers_cells(): cells.f3 = [[1.0, 1.0]] cells.f4 = [[1.0, 1.0]] cells.f5 = [[1]] - cells.f6 = [[2, 3, 4, float('nan'), 2]] - cells.f7 = ['Substrate Roughness', 'Test Thickness', 'Test SLD', 'Test Roughness'] - cells.f8 = ['Background Param 1'] - cells.f9 = ['Scalefactor 1'] + cells.f6 = [[2, 3, 4, float("nan"), 2]] + cells.f7 = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness"] + cells.f8 = ["Background Param 1"] + cells.f9 = ["Scalefactor 1"] cells.f10 = [] - cells.f11 = ['SLD Air'] - cells.f12 = ['SLD D2O'] - cells.f13 = ['Resolution Param 1'] + cells.f11 = ["SLD Air"] + cells.f12 = ["SLD D2O"] + cells.f13 = ["Resolution Param 1"] cells.f14 = [dummy_function] cells.f15 = [TypeOptions.Constant] cells.f16 = [TypeOptions.Constant] @@ -233,21 +233,21 @@ def domains_cells(): cells.f3 = [[1.0, 1.0]] cells.f4 = [[1.0, 1.0]] cells.f5 = [[2, 1]] - cells.f6 = [[2, 3, 4, float('nan'), 2]] - cells.f7 = ['Substrate Roughness', 'Test Thickness', 'Test SLD', 'Test Roughness'] - cells.f8 = ['Background Param 1'] - cells.f9 = ['Scalefactor 1'] + cells.f6 = [[2, 3, 4, float("nan"), 2]] + cells.f7 = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness"] + cells.f8 = ["Background Param 1"] + cells.f9 = ["Scalefactor 1"] cells.f10 = [] - cells.f11 = ['SLD Air'] - cells.f12 = ['SLD D2O'] - cells.f13 = ['Resolution Param 1'] + cells.f11 = ["SLD Air"] + cells.f12 = ["SLD D2O"] + cells.f13 = ["Resolution Param 1"] cells.f14 = [dummy_function] cells.f15 = [TypeOptions.Constant] cells.f16 = [TypeOptions.Constant] cells.f17 = [[[]]] cells.f18 = [[0, 1], [0, 1]] cells.f19 = [[1], [1]] - cells.f20 = ['Domain Ratio 1'] + cells.f20 = ["Domain Ratio 1"] return cells @@ -262,13 +262,13 @@ def custom_xy_cells(): cells.f4 = [[0.005, 0.7]] cells.f5 = [[0]] cells.f6 = [[0]] - cells.f7 = ['Substrate Roughness', 'Test Thickness', 'Test SLD', 'Test Roughness'] - cells.f8 = ['Background Param 1'] - cells.f9 = ['Scalefactor 1'] + cells.f7 = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness"] + cells.f8 = ["Background Param 1"] + cells.f9 = ["Scalefactor 1"] cells.f10 = [] - cells.f11 = ['SLD Air'] - cells.f12 = ['SLD D2O'] - cells.f13 = ['Resolution Param 1'] + cells.f11 = ["SLD Air"] + cells.f12 = ["SLD D2O"] + cells.f13 = ["Resolution Param 1"] cells.f14 = [dummy_function] cells.f15 = [TypeOptions.Constant] cells.f16 = [TypeOptions.Constant] @@ -316,19 +316,19 @@ def domains_limits(): def non_polarised_priors(): """The expected priors object from "standard_layers_project" and "custom_xy_project".""" priors = Priors() - priors.param = [['Substrate Roughness', RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ['Test Thickness', RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ['Test SLD', RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ['Test Roughness', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.backgroundParam = [['Background Param 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.param = [["Substrate Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Thickness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test SLD", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.backgroundParam = [["Background Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.qzshift = [] - priors.scalefactor = [['Scalefactor 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.bulkIn = [['SLD Air', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.bulkOut = [['SLD D2O', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.resolutionParam = [['Resolution Param 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.scalefactor = [["Scalefactor 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.bulkIn = [["SLD Air", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.bulkOut = [["SLD D2O", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.resolutionParam = [["Resolution Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.domainRatio = [] - priors.priorNames = ['Substrate Roughness', 'Test Thickness', 'Test SLD', 'Test Roughness', 'Background Param 1', - 'Scalefactor 1', 'SLD Air', 'SLD D2O', 'Resolution Param 1'] + priors.priorNames = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness", "Background Param 1", + "Scalefactor 1", "SLD Air", "SLD D2O", "Resolution Param 1"] priors.priorValues = [[1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf]] @@ -339,19 +339,19 @@ def non_polarised_priors(): def domains_priors(): """The expected priors object from "domains_project".""" priors = Priors() - priors.param = [['Substrate Roughness', RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ['Test Thickness', RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ['Test SLD', RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ['Test Roughness', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.backgroundParam = [['Background Param 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.param = [["Substrate Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Thickness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test SLD", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.backgroundParam = [["Background Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.qzshift = [] - priors.scalefactor = [['Scalefactor 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.bulkIn = [['SLD Air', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.bulkOut = [['SLD D2O', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.resolutionParam = [['Resolution Param 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.domainRatio = [['Domain Ratio 1', RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.priorNames = ['Substrate Roughness', 'Test Thickness', 'Test SLD', 'Test Roughness', 'Background Param 1', - 'Scalefactor 1', 'SLD Air', 'SLD D2O', 'Resolution Param 1', 'Domain Ratio 1'] + priors.scalefactor = [["Scalefactor 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.bulkIn = [["SLD Air", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.bulkOut = [["SLD D2O", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.resolutionParam = [["Resolution Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.domainRatio = [["Domain Ratio 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.priorNames = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness", "Background Param 1", + "Scalefactor 1", "SLD Air", "SLD D2O", "Resolution Param 1", "Domain Ratio 1"] priors.priorValues = [[1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf]] @@ -488,8 +488,8 @@ def test_make_input(test_project, test_problem, test_cells, test_limits, test_pr mocked_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_engine - with mock.patch.dict('sys.modules', {'matlab': mocked_matlab_module, - 'matlab.engine': mocked_matlab_module.engine}), \ + with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, + "matlab.engine": mocked_matlab_module.engine}), \ mock.patch.object(RAT.rat_core, "DylibEngine", mock.MagicMock()), \ mock.patch.object(RAT.inputs, "get_python_handle", mock.MagicMock(return_value=dummy_function)), \ mock.patch.object(RAT.wrappers.MatlabWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)), \ @@ -574,12 +574,12 @@ def test_check_indices(test_problem, request) -> None: def test_check_indices_error(test_problem, index_list, bad_value, request) -> None: """The check_indices routine should raise an IndexError if a contrast list contains an index that is out of the range of the corresponding parameter list in a ProblemDefinition object.""" - param_list = {'contrastBulkIns': 'bulkIn', - 'contrastBulkOuts': 'bulkOut', - 'contrastScalefactors': 'scalefactors', - 'contrastDomainRatios': 'domainRatio', - 'contrastBackgroundParams': 'backgroundParams', - 'contrastResolutionParams': 'resolutionParams', + param_list = {"contrastBulkIns": "bulkIn", + "contrastBulkOuts": "bulkOut", + "contrastScalefactors": "scalefactors", + "contrastDomainRatios": "domainRatio", + "contrastBackgroundParams": "backgroundParams", + "contrastResolutionParams": "resolutionParams", } test_problem = request.getfixturevalue(test_problem) @@ -604,8 +604,8 @@ def test_make_cells(test_project, test_cells, request) -> None: mocked_matlab_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_matlab_engine - with mock.patch.dict('sys.modules', {'matlab': mocked_matlab_module, - 'matlab.engine': mocked_matlab_module.engine}), \ + with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, + "matlab.engine": mocked_matlab_module.engine}), \ mock.patch.object(RAT.rat_core, "DylibEngine", mock.MagicMock()), \ mock.patch.object(RAT.inputs, "get_python_handle", mock.MagicMock(return_value=dummy_function)), \ mock.patch.object(RAT.wrappers.MatlabWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)), \ @@ -647,8 +647,8 @@ def check_problem_equal(actual_problem, expected_problem) -> None: # Need to account for "NaN" entries in contrastCustomFiles field assert ((actual_problem.contrastCustomFiles == expected_problem.contrastCustomFiles).all() or - ['NaN' if np.isnan(el) else el for el in actual_problem.contrastCustomFiles] == - ['NaN' if np.isnan(el) else el for el in expected_problem.contrastCustomFiles] + ["NaN" if np.isnan(el) else el for el in actual_problem.contrastCustomFiles] == + ["NaN" if np.isnan(el) else el for el in expected_problem.contrastCustomFiles] ) return None @@ -664,8 +664,8 @@ def check_cells_equal(actual_cells, expected_cells) -> None: # f6 may contain "NaN" values, so consider separately assert (actual_cells.f6 == expected_cells.f6 or - ['NaN' if np.isnan(el) else el for entry in actual_cells.f6 for el in entry] == - ['NaN' if np.isnan(el) else el for entry in expected_cells.f6 for el in entry]) + ["NaN" if np.isnan(el) else el for entry in actual_cells.f6 for el in entry] == + ["NaN" if np.isnan(el) else el for entry in expected_cells.f6 for el in entry]) for index in chain(range(3, 6), range(7, 21)): field = f"f{index}" diff --git a/tests/test_models.py b/tests/test_models.py index ad142467..9e8ffbfe 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -16,7 +16,7 @@ (RAT.models.CustomFile, "Custom File", {}), (RAT.models.Data, "Data", {}), (RAT.models.DomainContrast, "Domain Contrast", {}), - (RAT.models.Layer, "Layer", {'thickness': 'Test Thickness', 'SLD': 'Test SLD', 'roughness': 'Test Roughness'}), + (RAT.models.Layer, "Layer", {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"}), (RAT.models.Parameter, "Parameter", {}), (RAT.models.Resolution, "Resolution", {}), ]) @@ -26,7 +26,7 @@ def test_default_names(model: Callable, model_name: str, model_params: dict) -> """ model_1 = model(**model_params) model_2 = model(**model_params) - model_3 = model(name='Given Name', **model_params) + model_3 = model(name="Given Name", **model_params) model_4 = model(**model_params) assert model_1.name == f"New {model_name} 1" @@ -42,36 +42,36 @@ def test_default_names(model: Callable, model_name: str, model_params: dict) -> (RAT.models.CustomFile, {}), (RAT.models.Data, {}), (RAT.models.DomainContrast, {}), - (RAT.models.Layer, {'thickness': 'Test Thickness', 'SLD': 'Test SLD', 'roughness': 'Test Roughness'}), - (RAT.models.AbsorptionLayer, {'thickness': 'Test Thickness', 'SLD_real': 'Test SLD', 'SLD_imaginary': 'Test SLD', - 'roughness': 'Test Roughness'}), + (RAT.models.Layer, {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"}), + (RAT.models.AbsorptionLayer, {"thickness": "Test Thickness", "SLD_real": "Test SLD", "SLD_imaginary": "Test SLD", + "roughness": "Test Roughness"}), (RAT.models.Parameter, {}), (RAT.models.Resolution, {}), ]) class TestModels: def test_initialise_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When initialising a model with the wrong type for the "name" field, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n ' - f'Input should be a valid string'): + with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nname\n " + f"Input should be a valid string"): model(name=1, **model_params) def test_assignment_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When assigning the "name" field of a model with the wrong type, we should raise a ValidationError.""" test_model = model(**model_params) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n ' - f'Input should be a valid string'): + with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nname\n " + f"Input should be a valid string"): test_model.name = 1 def test_initialise_with_zero_length_name(self, model: Callable, model_params: dict) -> None: """When initialising a model with a zero length name, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nname\n ' - f'String should have at least 1 character'): - model(name='', **model_params) + with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nname\n " + f"String should have at least 1 character"): + model(name="", **model_params) def test_initialise_with_extra_fields(self, model: Callable, model_params: dict) -> None: """When initialising a model with unspecified fields, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {model.__name__}\nnew_field\n ' - f'Extra inputs are not permitted'): + with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nnew_field\n " + f"Extra inputs are not permitted"): model(new_field=1, **model_params) @@ -79,7 +79,7 @@ def test_data_eq() -> None: """If we use the Data.__eq__ method with an object that is not a pydantic BaseModel, we should return "NotImplemented". """ - assert RAT.models.Data().__eq__('data') == NotImplemented + assert RAT.models.Data().__eq__("data") == NotImplemented @pytest.mark.parametrize("input_data", [ @@ -159,8 +159,8 @@ def test_two_values_in_simulation_range(input_range: list[float]) -> None: @pytest.mark.parametrize("field", [ - 'data_range', - 'simulation_range' + "data_range", + "simulation_range" ]) def test_min_max_in_range(field: str) -> None: """If the maximum value of the "data_range" or "simulation_range" fields of the "Data" model is greater than the @@ -189,9 +189,9 @@ def test_data_range(test_range) -> None: """If "data" is specified but the "data_range" lies outside of the limits of the data we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=re.escape(f'1 validation error for Data\n Value error, The ' - f'data_range value of: {test_range} must lie within ' - f'the min/max values of the data: [1.0, 3.0]')): + with pytest.raises(pydantic.ValidationError, match=re.escape(f"1 validation error for Data\n Value error, The " + f"data_range value of: {test_range} must lie within " + f"the min/max values of the data: [1.0, 3.0]")): RAT.models.Data(data=np.array([[1.0, 0.0, 0.0], [3.0, 0.0, 0.0]]), data_range=test_range) @@ -204,10 +204,10 @@ def test_simulation_range(test_range) -> None: """If "data" is specified but the "simulation_range" lies within the limits of the data we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=re.escape(f'1 validation error for Data\n Value error, The ' - f'simulation_range value of: {test_range} must lie ' - f'outside of the min/max values of the data: ' - f'[1.0, 3.0]')): + with pytest.raises(pydantic.ValidationError, match=re.escape(f"1 validation error for Data\n Value error, The " + f"simulation_range value of: {test_range} must lie " + f"outside of the min/max values of the data: " + f"[1.0, 3.0]")): RAT.models.Data(data=np.array([[1.0, 0.0, 0.0], [3.0, 0.0, 0.0]]), simulation_range=test_range) @@ -220,7 +220,7 @@ def test_parameter_range(minimum: float, value: float, maximum: float) -> None: """For the "Parameter" model, if the value of the "value" field does not lie with the values given in the "min" and "max" fields, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Parameter\n Value error, value ' - f'{float(value)} is not within the defined range: ' - f'{float(minimum)} <= value <= {float(maximum)}'): + with pytest.raises(pydantic.ValidationError, match=f"1 validation error for Parameter\n Value error, value " + f"{float(value)} is not within the defined range: " + f"{float(minimum)} <= value <= {float(maximum)}"): RAT.models.Parameter(min=minimum, value=value, max=maximum) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 652281f4..d5b30b3c 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -10,26 +10,26 @@ from RAT.utils.plotting import Figure, plot_ref_sld_helper TEST_DIR_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), - 'test_data') + "test_data") def data() -> PlotEventData: """ Creates the fixture for the tests. """ - data_path = os.path.join(TEST_DIR_PATH, 'plotting_data.pickle') - with open(data_path, 'rb') as f: + data_path = os.path.join(TEST_DIR_PATH, "plotting_data.pickle") + with open(data_path, "rb") as f: loaded_data = pickle.load(f) data = PlotEventData() - data.modelType = loaded_data['modelType'] - data.dataPresent = loaded_data['dataPresent'] - data.subRoughs = loaded_data['subRoughs'] - data.resample = loaded_data['resample'] - data.resampledLayers = loaded_data['resampledLayers'] - data.reflectivity = loaded_data['reflectivity'] - data.shiftedData = loaded_data['shiftedData'] - data.sldProfiles = loaded_data['sldProfiles'] + data.modelType = loaded_data["modelType"] + data.dataPresent = loaded_data["dataPresent"] + data.subRoughs = loaded_data["subRoughs"] + data.resample = loaded_data["resample"] + data.resampledLayers = loaded_data["resampledLayers"] + data.reflectivity = loaded_data["reflectivity"] + data.shiftedData = loaded_data["shiftedData"] + data.sldProfiles = loaded_data["sldProfiles"] return data @@ -38,7 +38,7 @@ def fig() -> Figure: """ Creates the fixture for the tests. """ - plt.close('all') + plt.close("all") figure = Figure(1, 3) fig = plot_ref_sld_helper(fig=figure, data=data()) return fig @@ -58,13 +58,13 @@ def test_figure_axis_formating(fig: Figure) -> None: assert ref_plot.get_xscale() == "log" assert ref_plot.get_ylabel() == "Ref" assert ref_plot.get_yscale() == "log" - assert [label._text for label in ref_plot.get_legend().texts] == ['ref 1', 'ref 2', 'ref 3'] + assert [label._text for label in ref_plot.get_legend().texts] == ["ref 1", "ref 2", "ref 3"] assert sld_plot.get_xlabel() == "Z" assert sld_plot.get_xscale() == "linear" assert sld_plot.get_ylabel() == "SLD" assert sld_plot.get_yscale() == "linear" - assert [label._text for label in sld_plot.get_legend().texts] == ['sld 1', 'sld 2', 'sld 3'] + assert [label._text for label in sld_plot.get_legend().texts] == ["sld 1", "sld 2", "sld 3"] def test_figure_color_formating(fig: Figure) -> None: @@ -97,21 +97,21 @@ def test_eventhandlers_linked_to_figure(fig: Figure) -> None: and key_press_event in the figure are linked to the class methods. """ - pattern = r'\(([^\]]+)\)' + pattern = r"\(([^\]]+)\)" - for ix, val in fig._fig.canvas.callbacks.callbacks['close_event'].items(): + for ix, val in fig._fig.canvas.callbacks.callbacks["close_event"].items(): if str(type(val)) == "": break - canvas_close_event_callback = fig._fig.canvas.callbacks.callbacks['close_event'][ix]._func_ref.__repr__() + canvas_close_event_callback = fig._fig.canvas.callbacks.callbacks["close_event"][ix]._func_ref.__repr__() close_event_callback = re.findall(pattern, canvas_close_event_callback)[0] assert close_event_callback == "_close" assert hasattr(Figure, "_close") - for ix, val in fig._fig.canvas.callbacks.callbacks['key_press_event'].items(): + for ix, val in fig._fig.canvas.callbacks.callbacks["key_press_event"].items(): if str(type(val)) == "": break - canvas_key_press_event_callback = fig._fig.canvas.callbacks.callbacks['key_press_event'][ix]._func_ref.__repr__() + canvas_key_press_event_callback = fig._fig.canvas.callbacks.callbacks["key_press_event"][ix]._func_ref.__repr__() key_press_event_callback = re.findall(pattern, canvas_key_press_event_callback)[0] assert key_press_event_callback == "_process_button_press" @@ -125,12 +125,12 @@ def test_eventhandler_variable_update(fig: Figure) -> None: while loop in wait_for_close. """ assert not fig._esc_pressed - on_key_mock_event = type('MockEvent', (object,), {'key': 'escape'}) + on_key_mock_event = type("MockEvent", (object,), {"key": "escape"}) fig._process_button_press(on_key_mock_event) assert fig._esc_pressed assert not fig._close_clicked - fig._close('test') + fig._close("test") assert fig._close_clicked diff --git a/tests/test_project.py b/tests/test_project.py index 84220c5e..8f19d0e2 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -13,22 +13,22 @@ import RAT from RAT.utils.enums import Calculations, LayerModels -layer_params = {'thickness': 'Test Thickness', 'SLD': 'Test SLD', 'roughness': 'Test Roughness'} -absorption_layer_params = {'thickness': 'Test Thickness', 'SLD_real': 'Test SLD', 'SLD_imaginary': 'Test SLD', - 'roughness': 'Test Roughness'} +layer_params = {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"} +absorption_layer_params = {"thickness": "Test Thickness", "SLD_real": "Test SLD", "SLD_imaginary": "Test SLD", + "roughness": "Test Roughness"} @pytest.fixture def test_project(): """Add parameters to the default project, so each ClassList can be tested properly.""" - test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name='Simulation', data=np.array([[1.0, 1.0, 1.0]]))])) - test_project.parameters.append(name='Test Thickness') - test_project.parameters.append(name='Test SLD') - test_project.parameters.append(name='Test Roughness') - test_project.custom_files.append(name='Test Custom File') - test_project.layers.append(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness') - test_project.contrasts.append(name='Test Contrast', data='Simulation', background='Background 1', bulk_in='SLD Air', - bulk_out='SLD D2O', scalefactor='Scalefactor 1', resolution='Resolution 1', - model=['Test Layer']) + test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name="Simulation", data=np.array([[1.0, 1.0, 1.0]]))])) + test_project.parameters.append(name="Test Thickness") + test_project.parameters.append(name="Test SLD") + test_project.parameters.append(name="Test Roughness") + test_project.custom_files.append(name="Test Custom File") + test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness") + test_project.contrasts.append(name="Test Contrast", data="Simulation", background="Background 1", bulk_in="SLD Air", + bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", + model=["Test Layer"]) return test_project @@ -36,66 +36,66 @@ def test_project(): def default_project_repr(): """A string of the output of repr() for a Project model with no parameters specified.""" return( - 'Calculation: ---------------------------------------------------------------------------------------\n\n' - 'non polarised\n\n' - 'Model: ---------------------------------------------------------------------------------------------\n\n' - 'standard layers\n\n' - 'Geometry: ------------------------------------------------------------------------------------------\n\n' - 'air/substrate\n\n' - 'Parameters: ----------------------------------------------------------------------------------------\n\n' - '+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n' - '| index | name | min | value | max | fit | prior type | mu | sigma |\n' - '+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n' - '| 0 | Substrate Roughness | 1.0 | 3.0 | 5.0 | True | uniform | 0.0 | inf |\n' - '+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n\n' - 'Bulk In: -------------------------------------------------------------------------------------------\n\n' - '+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n' - '| index | name | min | value | max | fit | prior type | mu | sigma |\n' - '+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n' - '| 0 | SLD Air | 0.0 | 0.0 | 0.0 | False | uniform | 0.0 | inf |\n' - '+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n\n' - 'Bulk Out: ------------------------------------------------------------------------------------------\n\n' - '+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n' - '| index | name | min | value | max | fit | prior type | mu | sigma |\n' - '+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n' - '| 0 | SLD D2O | 6.2e-06 | 6.35e-06 | 6.35e-06 | False | uniform | 0.0 | inf |\n' - '+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n\n' - 'Scalefactors: --------------------------------------------------------------------------------------\n\n' - '+-------+---------------+------+-------+------+-------+------------+-----+-------+\n' - '| index | name | min | value | max | fit | prior type | mu | sigma |\n' - '+-------+---------------+------+-------+------+-------+------------+-----+-------+\n' - '| 0 | Scalefactor 1 | 0.02 | 0.23 | 0.25 | False | uniform | 0.0 | inf |\n' - '+-------+---------------+------+-------+------+-------+------------+-----+-------+\n\n' - 'Background Parameters: -----------------------------------------------------------------------------\n\n' - '+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n' - '| index | name | min | value | max | fit | prior type | mu | sigma |\n' - '+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n' - '| 0 | Background Param 1 | 1e-07 | 1e-06 | 1e-05 | False | uniform | 0.0 | inf |\n' - '+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n\n' - 'Backgrounds: ---------------------------------------------------------------------------------------\n\n' - '+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n' - '| index | name | type | value 1 | value 2 | value 3 | value 4 | value 5 |\n' - '+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n' - '| 0 | Background 1 | constant | Background Param 1 | | | | |\n' - '+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n\n' - 'Resolution Parameters: -----------------------------------------------------------------------------\n\n' - '+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n' - '| index | name | min | value | max | fit | prior type | mu | sigma |\n' - '+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n' - '| 0 | Resolution Param 1 | 0.01 | 0.03 | 0.05 | False | uniform | 0.0 | inf |\n' - '+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n\n' - 'Resolutions: ---------------------------------------------------------------------------------------\n\n' - '+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n' - '| index | name | type | value 1 | value 2 | value 3 | value 4 | value 5 |\n' - '+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n' - '| 0 | Resolution 1 | constant | Resolution Param 1 | | | | |\n' - '+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n\n' - 'Data: ----------------------------------------------------------------------------------------------\n\n' - '+-------+------------+------+------------+------------------+\n' - '| index | name | data | data range | simulation range |\n' - '+-------+------------+------+------------+------------------+\n' - '| 0 | Simulation | [] | [] | [0.005, 0.7] |\n' - '+-------+------------+------+------------+------------------+\n\n') + "Calculation: ---------------------------------------------------------------------------------------\n\n" + "non polarised\n\n" + "Model: ---------------------------------------------------------------------------------------------\n\n" + "standard layers\n\n" + "Geometry: ------------------------------------------------------------------------------------------\n\n" + "air/substrate\n\n" + "Parameters: ----------------------------------------------------------------------------------------\n\n" + "+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n" + "| index | name | min | value | max | fit | prior type | mu | sigma |\n" + "+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n" + "| 0 | Substrate Roughness | 1.0 | 3.0 | 5.0 | True | uniform | 0.0 | inf |\n" + "+-------+---------------------+-----+-------+-----+------+------------+-----+-------+\n\n" + "Bulk In: -------------------------------------------------------------------------------------------\n\n" + "+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n" + "| index | name | min | value | max | fit | prior type | mu | sigma |\n" + "+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n" + "| 0 | SLD Air | 0.0 | 0.0 | 0.0 | False | uniform | 0.0 | inf |\n" + "+-------+---------+-----+-------+-----+-------+------------+-----+-------+\n\n" + "Bulk Out: ------------------------------------------------------------------------------------------\n\n" + "+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n" + "| index | name | min | value | max | fit | prior type | mu | sigma |\n" + "+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n" + "| 0 | SLD D2O | 6.2e-06 | 6.35e-06 | 6.35e-06 | False | uniform | 0.0 | inf |\n" + "+-------+---------+---------+----------+----------+-------+------------+-----+-------+\n\n" + "Scalefactors: --------------------------------------------------------------------------------------\n\n" + "+-------+---------------+------+-------+------+-------+------------+-----+-------+\n" + "| index | name | min | value | max | fit | prior type | mu | sigma |\n" + "+-------+---------------+------+-------+------+-------+------------+-----+-------+\n" + "| 0 | Scalefactor 1 | 0.02 | 0.23 | 0.25 | False | uniform | 0.0 | inf |\n" + "+-------+---------------+------+-------+------+-------+------------+-----+-------+\n\n" + "Background Parameters: -----------------------------------------------------------------------------\n\n" + "+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n" + "| index | name | min | value | max | fit | prior type | mu | sigma |\n" + "+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n" + "| 0 | Background Param 1 | 1e-07 | 1e-06 | 1e-05 | False | uniform | 0.0 | inf |\n" + "+-------+--------------------+-------+-------+-------+-------+------------+-----+-------+\n\n" + "Backgrounds: ---------------------------------------------------------------------------------------\n\n" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n" + "| index | name | type | value 1 | value 2 | value 3 | value 4 | value 5 |\n" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n" + "| 0 | Background 1 | constant | Background Param 1 | | | | |\n" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n\n" + "Resolution Parameters: -----------------------------------------------------------------------------\n\n" + "+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n" + "| index | name | min | value | max | fit | prior type | mu | sigma |\n" + "+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n" + "| 0 | Resolution Param 1 | 0.01 | 0.03 | 0.05 | False | uniform | 0.0 | inf |\n" + "+-------+--------------------+------+-------+------+-------+------------+-----+-------+\n\n" + "Resolutions: ---------------------------------------------------------------------------------------\n\n" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n" + "| index | name | type | value 1 | value 2 | value 3 | value 4 | value 5 |\n" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n" + "| 0 | Resolution 1 | constant | Resolution Param 1 | | | | |\n" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+\n\n" + "Data: ----------------------------------------------------------------------------------------------\n\n" + "+-------+------------+------+------------+------------------+\n" + "| index | name | data | data range | simulation range |\n" + "+-------+------------+------+------------+------------------+\n" + "| 0 | Simulation | [] | [] | [0.005, 0.7] |\n" + "+-------+------------+------+------------+------------------+\n\n") @pytest.fixture @@ -144,8 +144,8 @@ def test_classlists_specific_cases() -> None: options. """ project = RAT.Project(calculation=Calculations.Domains, absorption=True) - assert project.layers._class_handle.__name__ == 'AbsorptionLayer' - assert project.contrasts._class_handle.__name__ == 'ContrastWithRatio' + assert project.layers._class_handle.__name__ == "AbsorptionLayer" + assert project.contrasts._class_handle.__name__ == "ContrastWithRatio" @pytest.mark.parametrize(["input_model", "model_params"], [ @@ -198,26 +198,26 @@ def test_initialise_wrong_contrasts(input_model: Callable, calculation: Calculat @pytest.mark.parametrize("input_parameter", [ - (RAT.models.Parameter(name='Test Parameter')), - (RAT.models.Parameter(name='Substrate Roughness')), + (RAT.models.Parameter(name="Test Parameter")), + (RAT.models.Parameter(name="Substrate Roughness")), ]) def test_initialise_without_substrate_roughness(input_parameter: Callable) -> None: """If the "Project" model is initialised without "Substrate Roughness as a protected parameter, add it to the front of the "parameters" ClassList. """ - project = RAT.Project(parameters=RAT.ClassList(RAT.models.Parameter(name='Substrate Roughness'))) - assert project.parameters[0] == RAT.models.ProtectedParameter(name='Substrate Roughness') + project = RAT.Project(parameters=RAT.ClassList(RAT.models.Parameter(name="Substrate Roughness"))) + assert project.parameters[0] == RAT.models.ProtectedParameter(name="Substrate Roughness") @pytest.mark.parametrize(["field", "wrong_input_model", "model_params"], [ - ('backgrounds', RAT.models.Resolution, {}), - ('contrasts', RAT.models.Layer, layer_params), - ('domain_contrasts', RAT.models.Parameter, {}), - ('custom_files', RAT.models.Data, {}), - ('data', RAT.models.Contrast, {}), - ('layers', RAT.models.DomainContrast, {}), - ('parameters', RAT.models.CustomFile, {}), - ('resolutions', RAT.models.Background, {}), + ("backgrounds", RAT.models.Resolution, {}), + ("contrasts", RAT.models.Layer, layer_params), + ("domain_contrasts", RAT.models.Parameter, {}), + ("custom_files", RAT.models.Data, {}), + ("data", RAT.models.Contrast, {}), + ("layers", RAT.models.DomainContrast, {}), + ("parameters", RAT.models.CustomFile, {}), + ("resolutions", RAT.models.Background, {}), ]) def test_assign_wrong_classes(test_project, field: str, wrong_input_model: Callable, model_params: dict) -> None: """If we assign incorrect classes to the "Project" model, we should raise a ValidationError.""" @@ -255,45 +255,45 @@ def test_assign_wrong_contrasts(wrong_input_model: Callable, calculation: Calcul @pytest.mark.parametrize(["field", "model_params"], [ - ('backgrounds', {}), - ('contrasts', {}), - ('custom_files', {}), - ('data', {}), - ('layers', layer_params), - ('parameters', {}), - ('resolutions', {}), + ("backgrounds", {}), + ("contrasts", {}), + ("custom_files", {}), + ("data", {}), + ("layers", layer_params), + ("parameters", {}), + ("resolutions", {}), ]) def test_assign_models(test_project, field: str, model_params: dict) -> None: """If the "Project" model is initialised with models rather than ClassLists, we should raise a ValidationError.""" input_model = getattr(RAT.models, RAT.project.model_in_classlist[field]) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n{field}\n Input should be ' - f'an instance of ClassList'): + with pytest.raises(pydantic.ValidationError, match=f"1 validation error for Project\n{field}\n Input should be " + f"an instance of ClassList"): setattr(test_project, field, input_model(**model_params)) def test_wrapped_routines(test_project) -> None: """When initialising a project, several ClassList routines should be wrapped.""" - wrapped_methods = ['_setitem', '_delitem', '_iadd', 'append', 'insert', 'pop', 'remove', 'clear', 'extend', - 'set_fields'] + wrapped_methods = ["_setitem", "_delitem", "_iadd", "append", "insert", "pop", "remove", "clear", "extend", + "set_fields"] for class_list in RAT.project.class_lists: attribute = getattr(test_project, class_list) for methodName in wrapped_methods: - assert hasattr(getattr(attribute, methodName), '__wrapped__') + assert hasattr(getattr(attribute, methodName), "__wrapped__") def test_set_domain_ratios(test_project) -> None: """If we are not running a domains calculation, the "domain_ratios" field of the model should always be empty.""" assert test_project.domain_ratios == [] - test_project.domain_ratios.append(name='New Domain Ratio') + test_project.domain_ratios.append(name="New Domain Ratio") assert test_project.domain_ratios == [] @pytest.mark.parametrize("project_parameters", [ - ({'calculation': Calculations.NonPolarised, 'model': LayerModels.StandardLayers}), - ({'calculation': Calculations.NonPolarised, 'model': LayerModels.CustomLayers}), - ({'calculation': Calculations.NonPolarised, 'model': LayerModels.CustomXY}), - ({'calculation': Calculations.Domains, 'model': LayerModels.CustomLayers}), - ({'calculation': Calculations.Domains, 'model': LayerModels.CustomXY}), + ({"calculation": Calculations.NonPolarised, "model": LayerModels.StandardLayers}), + ({"calculation": Calculations.NonPolarised, "model": LayerModels.CustomLayers}), + ({"calculation": Calculations.NonPolarised, "model": LayerModels.CustomXY}), + ({"calculation": Calculations.Domains, "model": LayerModels.CustomLayers}), + ({"calculation": Calculations.Domains, "model": LayerModels.CustomXY}), ]) def test_set_domain_contrasts(project_parameters: dict) -> None: """If we are not running a domains calculation with standard layers, the "domain_contrasts" field of the model @@ -301,19 +301,19 @@ def test_set_domain_contrasts(project_parameters: dict) -> None: """ project = RAT.Project(**project_parameters) assert project.domain_contrasts == [] - project.domain_contrasts.append(name='New Domain Contrast') + project.domain_contrasts.append(name="New Domain Contrast") assert project.domain_contrasts == [] @pytest.mark.parametrize("project_parameters", [ - ({'model': LayerModels.CustomLayers}), - ({'model': LayerModels.CustomXY}), + ({"model": LayerModels.CustomLayers}), + ({"model": LayerModels.CustomXY}), ]) def test_set_layers(project_parameters: dict) -> None: """If we are not using a standard layers model, the "layers" field of the model should always be empty.""" project = RAT.Project(**project_parameters) assert project.layers == [] - project.layers.append(name='New Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness') + project.layers.append(name="New Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness") assert project.layers == [] @@ -337,7 +337,7 @@ def test_set_calculation(input_calculation: Calculations, input_contrast: Callab @pytest.mark.parametrize(["new_calc", "new_model", "expected_contrast_model"], [ - (Calculations.NonPolarised, LayerModels.StandardLayers, ['Test Layer']), + (Calculations.NonPolarised, LayerModels.StandardLayers, ["Test Layer"]), (Calculations.NonPolarised, LayerModels.CustomLayers, []), (Calculations.NonPolarised, LayerModels.CustomXY, []), (Calculations.Domains, LayerModels.StandardLayers, []), @@ -355,11 +355,11 @@ def test_set_contrast_model_field(test_project, new_calc: Calculations, new_mode @pytest.mark.parametrize(["input_model", "test_contrast_model", "error_message"], [ - (LayerModels.StandardLayers, ['Test Domain Ratio'], + (LayerModels.StandardLayers, ["Test Domain Ratio"], 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.'), - (LayerModels.StandardLayers, ['Test Domain Ratio', 'Test Domain Ratio', 'Test Domain Ratio'], + (LayerModels.StandardLayers, ["Test Domain Ratio", "Test Domain Ratio", "Test Domain Ratio"], 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.'), - (LayerModels.CustomLayers, ['Test Custom File', 'Test Custom File'], + (LayerModels.CustomLayers, ["Test Custom File", "Test Custom File"], 'For a custom model calculation the "model" field of "contrasts" cannot contain more than one value.'), ]) def test_check_contrast_model_length(test_project, input_model: LayerModels, test_contrast_model: list[str], @@ -367,10 +367,10 @@ def test_check_contrast_model_length(test_project, input_model: LayerModels, tes """If we are not running a domains calculation with standard layers, the "domain_contrasts" field of the model should always be empty. """ - test_domain_ratios = RAT.ClassList(RAT.models.Parameter(name='Test Domain Ratio')) + test_domain_ratios = RAT.ClassList(RAT.models.Parameter(name="Test Domain Ratio")) test_contrasts = RAT.ClassList(RAT.models.ContrastWithRatio(model=test_contrast_model)) with pytest.raises(pydantic.ValidationError, - match=f'1 validation error for Project\n Value error, {error_message}'): + match=f"1 validation error for Project\n Value error, {error_message}"): RAT.Project(calculation=Calculations.Domains, model=input_model, domain_ratios=test_domain_ratios, contrasts=test_contrasts) @@ -384,9 +384,9 @@ def test_set_absorption(input_layer: Callable, model_params: dict, input_absorpt Layer model. """ project = RAT.Project(absorption=input_absorption, - parameters=RAT.ClassList([RAT.models.Parameter(name='Test Thickness'), - RAT.models.Parameter(name='Test SLD'), - RAT.models.Parameter(name='Test Roughness')]), + parameters=RAT.ClassList([RAT.models.Parameter(name="Test Thickness"), + RAT.models.Parameter(name="Test SLD"), + RAT.models.Parameter(name="Test Roughness")]), layers=RAT.ClassList(input_layer(**model_params))) project.absorption = not input_absorption @@ -396,55 +396,55 @@ def test_set_absorption(input_layer: Callable, model_params: dict, input_absorpt @pytest.mark.parametrize("delete_operation", [ - 'project.parameters.__delitem__(0)', - 'project.parameters.pop()', + "project.parameters.__delitem__(0)", + "project.parameters.pop()", 'project.parameters.remove("Substrate Roughness")', - 'project.parameters.clear()', + "project.parameters.clear()", ]) def test_check_protected_parameters(delete_operation) -> None: """If we try to remove a protected parameter, we should raise an error.""" project = RAT.Project() - with pytest.raises(pydantic.ValidationError, match='1 validation error for Project\n Value error, Can\'t delete' - ' the protected parameters: Substrate Roughness'): + with pytest.raises(pydantic.ValidationError, match="1 validation error for Project\n Value error, Can\'t delete" + " the protected parameters: Substrate Roughness"): eval(delete_operation) # Ensure model was not deleted - assert project.parameters[0].name == 'Substrate Roughness' + assert project.parameters[0].name == "Substrate Roughness" @pytest.mark.parametrize(["model", "field"], [ - ('background_parameters', 'value_1'), - ('resolution_parameters', 'value_1'), - ('parameters', 'roughness'), - ('data', 'data'), - ('backgrounds', 'background'), - ('bulk_in', 'bulk_in'), - ('bulk_out', 'bulk_out'), - ('scalefactors', 'scalefactor'), - ('resolutions', 'resolution'), + ("background_parameters", "value_1"), + ("resolution_parameters", "value_1"), + ("parameters", "roughness"), + ("data", "data"), + ("backgrounds", "background"), + ("bulk_in", "bulk_in"), + ("bulk_out", "bulk_out"), + ("scalefactors", "scalefactor"), + ("resolutions", "resolution"), ]) def test_rename_models(test_project, model: str, field: str) -> None: """When renaming a model in the project, the new name should be recorded when that model is referred to elsewhere in the project. """ - getattr(test_project, model).set_fields(-1, name='New Name') + getattr(test_project, model).set_fields(-1, name="New Name") attribute = RAT.project.model_names_used_in[model].attribute - assert getattr(getattr(test_project, attribute)[-1], field) == 'New Name' + assert getattr(getattr(test_project, attribute)[-1], field) == "New Name" @pytest.mark.parametrize("field", [ - 'value_1', - 'value_2', - 'value_3', - 'value_4', - 'value_5', + "value_1", + "value_2", + "value_3", + "value_4", + "value_5", ]) def test_allowed_backgrounds(field: str) -> None: """If the "value" fields of the Background model are set to values that are not specified in the background parameters, we should raise a ValidationError. """ - test_background = RAT.models.Background(**{field: 'undefined'}) + test_background = RAT.models.Background(**{field: "undefined"}) with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' f'"undefined" in the "{field}" field of "backgrounds" must be ' f'defined in "background_parameters".'): @@ -452,58 +452,58 @@ def test_allowed_backgrounds(field: str) -> None: @pytest.mark.parametrize("field", [ - 'thickness', - 'SLD', - 'roughness', + "thickness", + "SLD", + "roughness", ]) def test_allowed_layers(field: str) -> None: """If the "thickness", "SLD", or "roughness" fields of the Layer model are set to values that are not specified in the parameters, we should raise a ValidationError. """ - test_layer = RAT.models.Layer(**{**layer_params, field: 'undefined'}) + test_layer = RAT.models.Layer(**{**layer_params, field: "undefined"}) with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' f'"undefined" in the "{field}" field of "layers" must be ' f'defined in "parameters".'): - RAT.Project(absorption=False, parameters=RAT.ClassList([RAT.models.Parameter(name='Test Thickness'), - RAT.models.Parameter(name='Test SLD'), - RAT.models.Parameter(name='Test Roughness')]), + RAT.Project(absorption=False, parameters=RAT.ClassList([RAT.models.Parameter(name="Test Thickness"), + RAT.models.Parameter(name="Test SLD"), + RAT.models.Parameter(name="Test Roughness")]), layers=RAT.ClassList(test_layer)) @pytest.mark.parametrize("field", [ - 'thickness', - 'SLD_real', - 'SLD_imaginary', - 'roughness', + "thickness", + "SLD_real", + "SLD_imaginary", + "roughness", ]) def test_allowed_absorption_layers(field: str) -> None: """If the "thickness", "SLD_real", "SLD_imaginary", or "roughness" fields of the AbsorptionLayer model are set to values that are not specified in the parameters, we should raise a ValidationError. """ - test_layer = RAT.models.AbsorptionLayer(**{**absorption_layer_params, field: 'undefined'}) + test_layer = RAT.models.AbsorptionLayer(**{**absorption_layer_params, field: "undefined"}) with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' f'"undefined" in the "{field}" field of "layers" must be ' f'defined in "parameters".'): - RAT.Project(absorption=True, parameters=RAT.ClassList([RAT.models.Parameter(name='Test Thickness'), - RAT.models.Parameter(name='Test SLD'), - RAT.models.Parameter(name='Test Roughness')]), + RAT.Project(absorption=True, parameters=RAT.ClassList([RAT.models.Parameter(name="Test Thickness"), + RAT.models.Parameter(name="Test SLD"), + RAT.models.Parameter(name="Test Roughness")]), layers=RAT.ClassList(test_layer)) @pytest.mark.parametrize("field", [ - 'value_1', - 'value_2', - 'value_3', - 'value_4', - 'value_5', + "value_1", + "value_2", + "value_3", + "value_4", + "value_5", ]) def test_allowed_resolutions(field: str) -> None: """If the "value" fields of the Resolution model are set to values that are not specified in the background parameters, we should raise a ValidationError. """ - test_resolution = RAT.models.Resolution(**{field: 'undefined'}) + test_resolution = RAT.models.Resolution(**{field: "undefined"}) with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' f'"undefined" in the "{field}" field of "resolutions" must be ' f'defined in "resolution_parameters".'): @@ -511,18 +511,18 @@ def test_allowed_resolutions(field: str) -> None: @pytest.mark.parametrize(["field", "model_name"], [ - ('data', 'data'), - ('background', 'backgrounds'), - ('bulk_in', 'bulk_in'), - ('bulk_out', 'bulk_out'), - ('scalefactor', 'scalefactors'), - ('resolution', 'resolutions'), + ("data", "data"), + ("background", "backgrounds"), + ("bulk_in", "bulk_in"), + ("bulk_out", "bulk_out"), + ("scalefactor", "scalefactors"), + ("resolution", "resolutions"), ]) def test_allowed_contrasts(field: str, model_name: str) -> None: """If the fields of the Contrast model are set to values not specified in the other respective models of the project, we should raise a ValidationError. """ - test_contrast = RAT.models.Contrast(**{field: 'undefined'}) + test_contrast = RAT.models.Contrast(**{field: "undefined"}) with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' f'"undefined" in the "{field}" field of "contrasts" must be ' f'defined in "{model_name}".'): @@ -530,19 +530,19 @@ def test_allowed_contrasts(field: str, model_name: str) -> None: @pytest.mark.parametrize(["field", "model_name"], [ - ('data', 'data'), - ('background', 'backgrounds'), - ('bulk_in', 'bulk_in'), - ('bulk_out', 'bulk_out'), - ('scalefactor', 'scalefactors'), - ('resolution', 'resolutions'), - ('domain_ratio', 'domain_ratios'), + ("data", "data"), + ("background", "backgrounds"), + ("bulk_in", "bulk_in"), + ("bulk_out", "bulk_out"), + ("scalefactor", "scalefactors"), + ("resolution", "resolutions"), + ("domain_ratio", "domain_ratios"), ]) def test_allowed_contrasts_with_ratio(field: str, model_name: str) -> None: """If the fields of the ContrastWithRatio model are set to values not specified in the other respective models of the project, we should raise a ValidationError. """ - test_contrast = RAT.models.ContrastWithRatio(**{field: 'undefined'}) + test_contrast = RAT.models.ContrastWithRatio(**{field: "undefined"}) with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' f'"undefined" in the "{field}" field of "contrasts" must be ' f'defined in "{model_name}".'): @@ -551,19 +551,19 @@ def test_allowed_contrasts_with_ratio(field: str, model_name: str) -> None: @pytest.mark.parametrize(["input_calc", "input_model", "test_contrast", "field_name"], [ (Calculations.Domains, LayerModels.StandardLayers, - RAT.models.ContrastWithRatio(name='Test Contrast', model=['undefined', 'undefined']), 'domain_contrasts'), + RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined", "undefined"]), "domain_contrasts"), (Calculations.Domains, LayerModels.CustomLayers, - RAT.models.ContrastWithRatio(name='Test Contrast', model=['undefined']), 'custom_files'), + RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined"]), "custom_files"), (Calculations.Domains, LayerModels.CustomXY, - RAT.models.ContrastWithRatio(name='Test Contrast', model=['undefined']), 'custom_files'), + RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined"]), "custom_files"), (Calculations.NonPolarised, LayerModels.StandardLayers, - RAT.models.Contrast(name='Test Contrast', model=['undefined', 'undefined', 'undefined']), 'layers'), + RAT.models.Contrast(name="Test Contrast", model=["undefined", "undefined", "undefined"]), "layers"), (Calculations.NonPolarised, LayerModels.CustomLayers, - RAT.models.Contrast(name='Test Contrast', model=['undefined']), 'custom_files'), + RAT.models.Contrast(name="Test Contrast", model=["undefined"]), "custom_files"), (Calculations.NonPolarised, LayerModels.CustomXY, - RAT.models.Contrast(name='Test Contrast', model=['undefined']), 'custom_files'), + RAT.models.Contrast(name="Test Contrast", model=["undefined"]), "custom_files"), ]) -def test_allowed_contrast_models(input_calc: Calculations, input_model: LayerModels, test_contrast: 'RAT.models', +def test_allowed_contrast_models(input_calc: Calculations, input_model: LayerModels, test_contrast: "RAT.models", field_name: str) -> None: """If any value in the model field of the contrasts is set to a value not specified in the appropriate part of the project, we should raise a ValidationError. @@ -578,7 +578,7 @@ def test_allowed_domain_contrast_models() -> None: """If any value in the model field of the domain_contrasts is set to a value not specified in the "layers" field of the project, we should raise a ValidationError. """ - test_contrast = RAT.models.DomainContrast(name='Test Domain Contrast', model=['undefined']) + test_contrast = RAT.models.DomainContrast(name="Test Domain Contrast", model=["undefined"]) with pytest.raises(pydantic.ValidationError, match='1 validation error for Project\n Value error, The values: ' '"undefined" in the "model" field of "domain_contrasts" must be ' 'defined in "layers".'): @@ -592,39 +592,39 @@ def test_repr(default_project_repr: str) -> None: def test_get_all_names(test_project) -> None: """We should be able to get the names of all the models defined in the project.""" - assert test_project.get_all_names() == {'parameters': ['Substrate Roughness', 'Test Thickness', 'Test SLD', - 'Test Roughness'], - 'bulk_in': ['SLD Air'], - 'bulk_out': ['SLD D2O'], - 'scalefactors': ['Scalefactor 1'], - 'domain_ratios': [], - 'background_parameters': ['Background Param 1'], - 'backgrounds': ['Background 1'], - 'resolution_parameters': ['Resolution Param 1'], - 'resolutions': ['Resolution 1'], - 'custom_files': ['Test Custom File'], - 'data': ['Simulation'], - 'layers': ['Test Layer'], - 'domain_contrasts': [], - 'contrasts': ['Test Contrast'] + assert test_project.get_all_names() == {"parameters": ["Substrate Roughness", "Test Thickness", "Test SLD", + "Test Roughness"], + "bulk_in": ["SLD Air"], + "bulk_out": ["SLD D2O"], + "scalefactors": ["Scalefactor 1"], + "domain_ratios": [], + "background_parameters": ["Background Param 1"], + "backgrounds": ["Background 1"], + "resolution_parameters": ["Resolution Param 1"], + "resolutions": ["Resolution 1"], + "custom_files": ["Test Custom File"], + "data": ["Simulation"], + "layers": ["Test Layer"], + "domain_contrasts": [], + "contrasts": ["Test Contrast"] } def test_get_all_protected_parameters(test_project) -> None: """We should be able to get the names of all the protected parameters defined in the project.""" - assert test_project.get_all_protected_parameters() == {'parameters': ['Substrate Roughness'], - 'bulk_in': [], - 'bulk_out': [], - 'scalefactors': [], - 'domain_ratios': [], - 'background_parameters': [], - 'resolution_parameters': [] + assert test_project.get_all_protected_parameters() == {"parameters": ["Substrate Roughness"], + "bulk_in": [], + "bulk_out": [], + "scalefactors": [], + "domain_ratios": [], + "background_parameters": [], + "resolution_parameters": [] } @pytest.mark.parametrize("test_value", [ - '', - 'Background Param 1', + "", + "Background Param 1", ]) def test_check_allowed_values(test_value: str) -> None: """We should not raise an error if string values are defined and on the list of allowed values.""" @@ -645,12 +645,12 @@ def test_check_allowed_values_not_on_list(test_value: str) -> None: @pytest.mark.parametrize("test_values", [ [], - ['Test Layer'], + ["Test Layer"], ]) def test_check_contrast_model_allowed_values(test_values: list[str]) -> None: """We should not raise an error if values are defined in a non-empty list and all are on the list of allowed values. """ - project = RAT.Project.model_construct(contrasts=RAT.ClassList(RAT.models.Contrast(name='Test Contrast', + project = RAT.Project.model_construct(contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", model=test_values))) assert project.check_contrast_model_allowed_values("contrasts", ["Test Layer"], "layers") is None @@ -663,7 +663,7 @@ def test_check_allowed_contrast_model_not_on_list(test_values: list[str]) -> Non """If string values are defined in a non-empty list and any of them are not included on the list of allowed values we should raise a ValueError. """ - project = RAT.Project.model_construct(contrasts=RAT.ClassList(RAT.models.Contrast(name='Test Contrast', + project = RAT.Project.model_construct(contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", model=test_values))) with pytest.raises(ValueError, match=f'The values: "{", ".join(str(i) for i in test_values)}" in the "model" field ' f'of "contrasts" must be defined in "layers".'): @@ -671,12 +671,12 @@ def test_check_allowed_contrast_model_not_on_list(test_values: list[str]) -> Non @pytest.mark.parametrize(["input_calc", "input_model", "expected_field_name"], [ - (Calculations.Domains, LayerModels.StandardLayers, 'domain_contrasts'), - (Calculations.NonPolarised, LayerModels.StandardLayers, 'layers'), - (Calculations.Domains, LayerModels.CustomLayers, 'custom_files'), - (Calculations.NonPolarised, LayerModels.CustomLayers, 'custom_files'), - (Calculations.Domains, LayerModels.CustomXY, 'custom_files'), - (Calculations.NonPolarised, LayerModels.CustomXY, 'custom_files'), + (Calculations.Domains, LayerModels.StandardLayers, "domain_contrasts"), + (Calculations.NonPolarised, LayerModels.StandardLayers, "layers"), + (Calculations.Domains, LayerModels.CustomLayers, "custom_files"), + (Calculations.NonPolarised, LayerModels.CustomLayers, "custom_files"), + (Calculations.Domains, LayerModels.CustomXY, "custom_files"), + (Calculations.NonPolarised, LayerModels.CustomXY, "custom_files"), ]) def test_get_contrast_model_field(input_calc: Calculations, input_model: LayerModels, expected_field_name: str) -> None: """Each combination of calculation and model determines the field where the values of "model" field of "contrasts" @@ -692,10 +692,10 @@ def test_get_contrast_model_field(input_calc: Calculations, input_model: LayerMo ]) def test_write_script(test_project, temp_dir, test_project_script, input_filename: str) -> None: """Test the script we write to regenerate the project is created and runs as expected.""" - test_project.write_script('problem', os.path.join(temp_dir, input_filename)) + test_project.write_script("problem", os.path.join(temp_dir, input_filename)) # Test the file is written in the correct place - script_path = os.path.join(temp_dir, 'test_script.py') + script_path = os.path.join(temp_dir, "test_script.py") assert os.path.isfile(script_path) # Test the contents of the file are as expected @@ -706,7 +706,7 @@ def test_write_script(test_project, temp_dir, test_project_script, input_filenam # Test we get the project object we expect when running the script exec(script) - new_project = locals()['problem'] + new_project = locals()["problem"] for class_list in RAT.project.class_lists: assert getattr(new_project, class_list) == getattr(test_project, class_list) @@ -721,29 +721,29 @@ def test_write_script(test_project, temp_dir, test_project_script, input_filenam def test_write_script_wrong_extension(test_project, extension: str) -> None: """If we try to write the script to anything other than a ".py" file, we raise a ValueError.""" with pytest.raises(ValueError, match='The script name provided to "write_script" must use the ".py" format'): - test_project.write_script('problem', 'test' + extension) + test_project.write_script("problem", "test" + extension) @pytest.mark.parametrize(["class_list", "field"], [ - ('backgrounds', 'value_1'), - ('backgrounds', 'value_2'), - ('backgrounds', 'value_3'), - ('backgrounds', 'value_4'), - ('backgrounds', 'value_5'), - ('resolutions', 'value_1'), - ('resolutions', 'value_2'), - ('resolutions', 'value_3'), - ('resolutions', 'value_4'), - ('resolutions', 'value_5'), - ('layers', 'thickness'), - ('layers', 'SLD'), - ('layers', 'roughness'), - ('contrasts', 'data'), - ('contrasts', 'background'), - ('contrasts', 'bulk_in'), - ('contrasts', 'bulk_out'), - ('contrasts', 'scalefactor'), - ('contrasts', 'resolution'), + ("backgrounds", "value_1"), + ("backgrounds", "value_2"), + ("backgrounds", "value_3"), + ("backgrounds", "value_4"), + ("backgrounds", "value_5"), + ("resolutions", "value_1"), + ("resolutions", "value_2"), + ("resolutions", "value_3"), + ("resolutions", "value_4"), + ("resolutions", "value_5"), + ("layers", "thickness"), + ("layers", "SLD"), + ("layers", "roughness"), + ("contrasts", "data"), + ("contrasts", "background"), + ("contrasts", "bulk_in"), + ("contrasts", "bulk_out"), + ("contrasts", "scalefactor"), + ("contrasts", "resolution"), ]) def test_wrap_set(test_project, class_list: str, field: str) -> None: """If we set the field values of a model in a ClassList as undefined values, we should raise a ValidationError.""" @@ -754,22 +754,22 @@ def test_wrap_set(test_project, class_list: str, field: str) -> None: f'"undefined" in the "{field}" field of "{class_list}" must be ' f'defined in ' f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): - test_attribute.set_fields(0, **{field: 'undefined'}) + test_attribute.set_fields(0, **{field: "undefined"}) # Ensure invalid model was not changed assert test_attribute == orig_class_list @pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ('background_parameters', 'Background Param 1', 'value_1'), - ('resolution_parameters', 'Resolution Param 1', 'value_1'), - ('parameters', 'Test SLD', 'SLD'), - ('data', 'Simulation', 'data'), - ('backgrounds', 'Background 1', 'background'), - ('bulk_in', 'SLD Air', 'bulk_in'), - ('bulk_out', 'SLD D2O', 'bulk_out'), - ('scalefactors', 'Scalefactor 1', 'scalefactor'), - ('resolutions', 'Resolution 1', 'resolution'), + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test SLD", "SLD"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), ]) def test_wrap_del(test_project, class_list: str, parameter: str, field: str) -> None: """If we delete a model in a ClassList containing values defined elsewhere, we should raise a ValidationError.""" @@ -788,25 +788,25 @@ def test_wrap_del(test_project, class_list: str, parameter: str, field: str) -> @pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ('backgrounds', 'value_1', {}), - ('backgrounds', 'value_2', {}), - ('backgrounds', 'value_3', {}), - ('backgrounds', 'value_4', {}), - ('backgrounds', 'value_5', {}), - ('resolutions', 'value_1', {}), - ('resolutions', 'value_2', {}), - ('resolutions', 'value_3', {}), - ('resolutions', 'value_4', {}), - ('resolutions', 'value_5', {}), - ('layers', 'thickness', layer_params), - ('layers', 'SLD', layer_params), - ('layers', 'roughness', layer_params), - ('contrasts', 'data', {}), - ('contrasts', 'background', {}), - ('contrasts', 'bulk_in', {}), - ('contrasts', 'bulk_out', {}), - ('contrasts', 'scalefactor', {}), - ('contrasts', 'resolution', {}), + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), ]) def test_wrap_iadd(test_project, class_list: str, field: str, model_params: dict) -> None: """If we add a model containing undefined values to a ClassList, we should raise a ValidationError.""" @@ -818,32 +818,32 @@ def test_wrap_iadd(test_project, class_list: str, field: str, model_params: dict f'"undefined" in the "{field}" field of "{class_list}" must be ' f'defined in ' f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): - test_attribute += [input_model(**{**model_params, field: 'undefined'})] + test_attribute += [input_model(**{**model_params, field: "undefined"})] # Ensure invalid model was not added assert test_attribute == orig_class_list @pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ('backgrounds', 'value_1', {}), - ('backgrounds', 'value_2', {}), - ('backgrounds', 'value_3', {}), - ('backgrounds', 'value_4', {}), - ('backgrounds', 'value_5', {}), - ('resolutions', 'value_1', {}), - ('resolutions', 'value_2', {}), - ('resolutions', 'value_3', {}), - ('resolutions', 'value_4', {}), - ('resolutions', 'value_5', {}), - ('layers', 'thickness', layer_params), - ('layers', 'SLD', layer_params), - ('layers', 'roughness', layer_params), - ('contrasts', 'data', {}), - ('contrasts', 'background', {}), - ('contrasts', 'bulk_in', {}), - ('contrasts', 'bulk_out', {}), - ('contrasts', 'scalefactor', {}), - ('contrasts', 'resolution', {}), + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), ]) def test_wrap_append(test_project, class_list: str, field: str, model_params: dict) -> None: """If we append a model containing undefined values to a ClassList, we should raise a ValidationError.""" @@ -855,32 +855,32 @@ def test_wrap_append(test_project, class_list: str, field: str, model_params: di f'"undefined" in the "{field}" field of "{class_list}" must be ' f'defined in ' f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): - test_attribute.append(input_model(**{**model_params, field: 'undefined'})) + test_attribute.append(input_model(**{**model_params, field: "undefined"})) # Ensure invalid model was not appended assert test_attribute == orig_class_list @pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ('backgrounds', 'value_1', {}), - ('backgrounds', 'value_2', {}), - ('backgrounds', 'value_3', {}), - ('backgrounds', 'value_4', {}), - ('backgrounds', 'value_5', {}), - ('resolutions', 'value_1', {}), - ('resolutions', 'value_2', {}), - ('resolutions', 'value_3', {}), - ('resolutions', 'value_4', {}), - ('resolutions', 'value_5', {}), - ('layers', 'thickness', layer_params), - ('layers', 'SLD', layer_params), - ('layers', 'roughness', layer_params), - ('contrasts', 'data', {}), - ('contrasts', 'background', {}), - ('contrasts', 'bulk_in', {}), - ('contrasts', 'bulk_out', {}), - ('contrasts', 'scalefactor', {}), - ('contrasts', 'resolution', {}), + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), ]) def test_wrap_insert(test_project, class_list: str, field: str, model_params: dict) -> None: """If we insert a model containing undefined values into a ClassList, we should raise a ValidationError.""" @@ -892,29 +892,29 @@ def test_wrap_insert(test_project, class_list: str, field: str, model_params: di f'"undefined" in the "{field}" field of "{class_list}" must be ' f'defined in ' f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): - test_attribute.insert(0, input_model(**{**model_params, field: 'undefined'})) + test_attribute.insert(0, input_model(**{**model_params, field: "undefined"})) # Ensure invalid model was not inserted assert test_attribute == orig_class_list @pytest.mark.parametrize(["class_list", "field"], [ - ('backgrounds', 'value_1'), - ('backgrounds', 'value_2'), - ('backgrounds', 'value_3'), - ('backgrounds', 'value_4'), - ('backgrounds', 'value_5'), - ('resolutions', 'value_1'), - ('resolutions', 'value_2'), - ('resolutions', 'value_3'), - ('resolutions', 'value_4'), - ('resolutions', 'value_5'), - ('contrasts', 'data'), - ('contrasts', 'background'), - ('contrasts', 'bulk_in'), - ('contrasts', 'bulk_out'), - ('contrasts', 'scalefactor'), - ('contrasts', 'resolution'), + ("backgrounds", "value_1"), + ("backgrounds", "value_2"), + ("backgrounds", "value_3"), + ("backgrounds", "value_4"), + ("backgrounds", "value_5"), + ("resolutions", "value_1"), + ("resolutions", "value_2"), + ("resolutions", "value_3"), + ("resolutions", "value_4"), + ("resolutions", "value_5"), + ("contrasts", "data"), + ("contrasts", "background"), + ("contrasts", "bulk_in"), + ("contrasts", "bulk_out"), + ("contrasts", "scalefactor"), + ("contrasts", "resolution"), ]) def test_wrap_insert_type_error(test_project, class_list: str, field: str) -> None: """If we raise a TypeError using the wrapped insert routine, we should re-raise the error.""" @@ -930,15 +930,15 @@ def test_wrap_insert_type_error(test_project, class_list: str, field: str) -> No @pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ('background_parameters', 'Background Param 1', 'value_1'), - ('resolution_parameters', 'Resolution Param 1', 'value_1'), - ('parameters', 'Test SLD', 'SLD'), - ('data', 'Simulation', 'data'), - ('backgrounds', 'Background 1', 'background'), - ('bulk_in', 'SLD Air', 'bulk_in'), - ('bulk_out', 'SLD D2O', 'bulk_out'), - ('scalefactors', 'Scalefactor 1', 'scalefactor'), - ('resolutions', 'Resolution 1', 'resolution'), + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test SLD", "SLD"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), ]) def test_wrap_pop(test_project, class_list: str, parameter: str, field: str) -> None: """If we pop a model in a ClassList containing values defined elsewhere, we should raise a ValidationError.""" @@ -957,15 +957,15 @@ def test_wrap_pop(test_project, class_list: str, parameter: str, field: str) -> @pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ('background_parameters', 'Background Param 1', 'value_1'), - ('resolution_parameters', 'Resolution Param 1', 'value_1'), - ('parameters', 'Test SLD', 'SLD'), - ('data', 'Simulation', 'data'), - ('backgrounds', 'Background 1', 'background'), - ('bulk_in', 'SLD Air', 'bulk_in'), - ('bulk_out', 'SLD D2O', 'bulk_out'), - ('scalefactors', 'Scalefactor 1', 'scalefactor'), - ('resolutions', 'Resolution 1', 'resolution'), + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test SLD", "SLD"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), ]) def test_wrap_remove(test_project, class_list: str, parameter: str, field: str) -> None: """If we remove a model in a ClassList containing values defined elsewhere, we should raise a ValidationError.""" @@ -983,15 +983,15 @@ def test_wrap_remove(test_project, class_list: str, parameter: str, field: str) @pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ('background_parameters', 'Background Param 1', 'value_1'), - ('resolution_parameters', 'Resolution Param 1', 'value_1'), - ('parameters', 'Test Thickness', 'thickness'), - ('data', 'Simulation', 'data'), - ('backgrounds', 'Background 1', 'background'), - ('bulk_in', 'SLD Air', 'bulk_in'), - ('bulk_out', 'SLD D2O', 'bulk_out'), - ('scalefactors', 'Scalefactor 1', 'scalefactor'), - ('resolutions', 'Resolution 1', 'resolution'), + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test Thickness", "thickness"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), ]) def test_wrap_clear(test_project, class_list: str, parameter: str, field: str) -> None: """If we clear a ClassList containing models with values defined elsewhere, we should raise a ValidationError.""" @@ -1009,25 +1009,25 @@ def test_wrap_clear(test_project, class_list: str, parameter: str, field: str) - @pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ('backgrounds', 'value_1', {}), - ('backgrounds', 'value_2', {}), - ('backgrounds', 'value_3', {}), - ('backgrounds', 'value_4', {}), - ('backgrounds', 'value_5', {}), - ('resolutions', 'value_1', {}), - ('resolutions', 'value_2', {}), - ('resolutions', 'value_3', {}), - ('resolutions', 'value_4', {}), - ('resolutions', 'value_5', {}), - ('layers', 'thickness', layer_params), - ('layers', 'SLD', layer_params), - ('layers', 'roughness', layer_params), - ('contrasts', 'data', {}), - ('contrasts', 'background', {}), - ('contrasts', 'bulk_in', {}), - ('contrasts', 'bulk_out', {}), - ('contrasts', 'scalefactor', {}), - ('contrasts', 'resolution', {}), + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), ]) def test_wrap_extend(test_project, class_list: str, field: str, model_params: dict) -> None: """If we extend a ClassList with model containing undefined values, we should raise a ValidationError.""" @@ -1039,7 +1039,7 @@ def test_wrap_extend(test_project, class_list: str, field: str, model_params: di f'"undefined" in the "{field}" field of "{class_list}" must be ' f'defined in ' f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): - test_attribute.extend([input_model(**{**model_params, field: 'undefined'})]) + test_attribute.extend([input_model(**{**model_params, field: "undefined"})]) # Ensure invalid model was not appended assert test_attribute == orig_class_list diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index e4d02c0f..a618b916 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -7,21 +7,21 @@ def test_matlab_wrapper() -> None: - with mock.patch.dict('sys.modules', {'matlab': mock.MagicMock(side_effect=ImportError)}), \ + with mock.patch.dict("sys.modules", {"matlab": mock.MagicMock(side_effect=ImportError)}), \ pytest.raises(ImportError): - RAT.wrappers.MatlabWrapper('demo.m') + RAT.wrappers.MatlabWrapper("demo.m") mocked_matlab_module = mock.MagicMock() mocked_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_engine # mocked_matlab_module.engine = mock.MagicMock() - with mock.patch.dict('sys.modules', {'matlab': mocked_matlab_module, - 'matlab.engine': mocked_matlab_module.engine}): - wrapper = RAT.wrappers.MatlabWrapper('demo.m') - assert wrapper.function_name == 'demo' + with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, + "matlab.engine": mocked_matlab_module.engine}): + wrapper = RAT.wrappers.MatlabWrapper("demo.m") + assert wrapper.function_name == "demo" mocked_engine.cd.assert_called_once() - assert pathlib.Path(mocked_engine.cd.call_args[0][0]).samefile('.') + assert pathlib.Path(mocked_engine.cd.call_args[0][0]).samefile(".") handle = wrapper.getHandle() @@ -40,9 +40,9 @@ def test_matlab_wrapper() -> None: def test_dylib_wrapper() -> None: mocked_engine = mock.MagicMock() - with mock.patch('RAT.wrappers.RAT.rat_core.DylibEngine', mocked_engine): - wrapper = RAT.wrappers.DylibWrapper('demo.dylib', 'demo') - mocked_engine.assert_called_once_with('demo.dylib', 'demo') + with mock.patch("RAT.wrappers.RAT.rat_core.DylibEngine", mocked_engine): + wrapper = RAT.wrappers.DylibWrapper("demo.dylib", "demo") + mocked_engine.assert_called_once_with("demo.dylib", "demo") wrapper.engine.invoke.return_value = ([2], 5) handle = wrapper.getHandle() diff --git a/tests/utils.py b/tests/utils.py index f26449bd..579e0135 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -139,8 +139,8 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: expected_array = getattr(expected_subclass, array) for i in range(len(actual_array)): assert ((actual_array == expected_array).all() or - (['NaN' if np.isnan(el) else el for el in actual_array[i]] == - ['NaN' if np.isnan(el) else el for el in expected_array[i]])) + (["NaN" if np.isnan(el) else el for el in actual_array[i]] == + ["NaN" if np.isnan(el) else el for el in expected_array[i]])) assert (actual_results.chain == expected_results.chain).all() From 422e346b1ac7bba81d42d71148885511847cfcd3 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Tue, 25 Jun 2024 16:55:07 +0100 Subject: [PATCH 05/11] Resolves automatically fixable linting errors on full rule set --- RAT/__init__.py | 2 +- RAT/classlist.py | 11 +++++ RAT/controls.py | 10 +++- RAT/events.py | 7 ++- .../absorption/volume_thiol_bilayer.py | 3 +- RAT/examples/domains/alloy_domains.py | 1 - RAT/examples/domains/domains_XY_model.py | 2 +- RAT/examples/languages/custom_bilayer.py | 8 ++-- RAT/examples/languages/setup_problem.py | 3 +- RAT/examples/non_polarised/DSPC_custom_XY.py | 5 +- .../non_polarised/DSPC_custom_layers.py | 3 +- RAT/examples/non_polarised/custom_XY_DSPC.py | 4 +- .../non_polarised/custom_bilayer_DSPC.py | 4 +- RAT/inputs.py | 14 ++++-- RAT/models.py | 17 +++++-- RAT/outputs.py | 19 ++++---- RAT/project.py | 6 +++ RAT/run.py | 1 - RAT/utils/custom_errors.py | 3 +- RAT/utils/enums.py | 5 ++ RAT/utils/plotting.py | 33 ++++++------- RAT/wrappers.py | 18 ++++--- pyproject.toml | 5 +- setup.py | 19 ++++---- tests/conftest.py | 14 +++--- tests/test_classlist.py | 47 ++++++++++++------- tests/test_controls.py | 46 +++++++++--------- tests/test_custom_errors.py | 2 +- tests/test_events.py | 10 ++-- tests/test_inputs.py | 16 +++---- tests/test_models.py | 2 +- tests/test_outputs.py | 3 +- tests/test_plotting.py | 32 +++++-------- tests/test_project.py | 12 ++--- tests/test_run.py | 5 +- tests/test_wrappers.py | 8 ++-- tests/utils.py | 17 +++---- 37 files changed, 227 insertions(+), 190 deletions(-) diff --git a/RAT/__init__.py b/RAT/__init__.py index 684d492f..98d87986 100644 --- a/RAT/__init__.py +++ b/RAT/__init__.py @@ -1,6 +1,6 @@ import os -import RAT.models as models +from RAT import models from RAT.classlist import ClassList from RAT.controls import set_controls from RAT.project import Project diff --git a/RAT/classlist.py b/RAT/classlist.py index ae77e90f..cb136816 100644 --- a/RAT/classlist.py +++ b/RAT/classlist.py @@ -32,7 +32,9 @@ class ClassList(collections.UserList): An instance, or list of instance(s), of the class to be used in this ClassList. name_field : str, optional The field used to define unique objects in the ClassList (default is "name"). + """ + def __init__(self, init_list: Union[Sequence[object], object] = None, name_field: str = "name") -> None: self.name_field = name_field @@ -130,6 +132,7 @@ def append(self, obj: object = None, **kwargs) -> None: SyntaxWarning Raised if the input arguments contain BOTH an object and keyword arguments. In this situation the object is appended to the ClassList and the keyword arguments are discarded. + """ if obj and kwargs: warnings.warn("ClassList.append() called with both an object and keyword arguments. " @@ -170,6 +173,7 @@ def insert(self, index: int, obj: object = None, **kwargs) -> None: SyntaxWarning Raised if the input arguments contain both an object and keyword arguments. In this situation the object is inserted into the ClassList and the keyword arguments are discarded. + """ if obj and kwargs: warnings.warn("ClassList.insert() called with both an object and keyword arguments. " @@ -230,6 +234,7 @@ def get_names(self) -> list[str]: ------- names : list [str] The value of the name_field attribute of each object in the ClassList. + """ return [getattr(model, self.name_field) for model in self.data if hasattr(model, self.name_field)] @@ -245,6 +250,7 @@ def get_all_matches(self, value: Any) -> list[tuple]: ------- : list [tuple] A list of (index, field) tuples matching the given value. + """ return [(index, field) for index, element in enumerate(self.data) for field in vars(element) if getattr(element, field) == value] @@ -262,6 +268,7 @@ def _validate_name_field(self, input_args: dict[str, Any]) -> None: ------ ValueError Raised if the input arguments contain a name_field value already defined in the ClassList. + """ names = self.get_names() with contextlib.suppress(KeyError): @@ -282,6 +289,7 @@ def _check_unique_name_fields(self, input_list: Iterable[object]) -> None: ------ ValueError Raised if the input list defines more than one object with the same value of name_field. + """ names = [getattr(model, self.name_field) for model in input_list if hasattr(model, self.name_field)] if len(set(names)) != len(names): @@ -299,6 +307,7 @@ def _check_classes(self, input_list: Iterable[object]) -> None: ------ ValueError Raised if the input list defines objects of different types. + """ if not (all(isinstance(element, self._class_handle) for element in input_list)): raise ValueError(f"Input list contains elements of type other than '{self._class_handle.__name__}'") @@ -316,6 +325,7 @@ def _get_item_from_name_field(self, value: Union[object, str]) -> Union[object, instance : object or str Either the object with the value of the name_field attribute given by value, or the input value if an object with that value of the name_field attribute cannot be found. + """ return next((model for model in self.data if getattr(model, self.name_field) == value), value) @@ -334,6 +344,7 @@ def _determine_class_handle(input_list: Sequence[object]): class_handle : type The type object of the element fulfilling the condition of satisfying "issubclass" for all of the other elements. + """ for this_element in input_list: if all([issubclass(type(instance), type(this_element)) for instance in input_list]): diff --git a/RAT/controls.py b/RAT/controls.py index 6df2f3a2..707b4396 100644 --- a/RAT/controls.py +++ b/RAT/controls.py @@ -11,6 +11,7 @@ @dataclass(frozen=True) class Controls: """The full set of controls parameters required for the compiled RAT code.""" + # All Procedures procedure: Procedures = Procedures.Calculate parallel: Parallel = Parallel.Single @@ -47,6 +48,7 @@ class Controls: class Calculate(BaseModel, validate_assignment=True, extra="forbid"): """Defines the class for the calculate procedure, which includes the properties used in all five procedures.""" + procedure: Literal[Procedures.Calculate] = Procedures.Calculate parallel: Parallel = Parallel.Single calcSldDuringFit: bool = False @@ -71,6 +73,7 @@ def __repr__(self) -> str: class Simplex(Calculate): """Defines the additional fields for the simplex procedure.""" + procedure: Literal[Procedures.Simplex] = Procedures.Simplex xTolerance: float = Field(1.0e-6, gt=0.0) funcTolerance: float = Field(1.0e-6, gt=0.0) @@ -82,6 +85,7 @@ class Simplex(Calculate): class DE(Calculate): """Defines the additional fields for the Differential Evolution procedure.""" + procedure: Literal[Procedures.DE] = Procedures.DE populationSize: int = Field(20, ge=1) fWeight: float = 0.5 @@ -93,6 +97,7 @@ class DE(Calculate): class NS(Calculate): """Defines the additional fields for the Nested Sampler procedure.""" + procedure: Literal[Procedures.NS] = Procedures.NS nLive: int = Field(150, ge=1) nMCMC: float = Field(0.0, ge=0.0) @@ -102,6 +107,7 @@ class NS(Calculate): class Dream(Calculate): """Defines the additional fields for the Dream procedure.""" + procedure: Literal[Procedures.Dream] = Procedures.Dream nSamples: int = Field(50000, ge=0) nChains: int = Field(10, gt=0) @@ -119,7 +125,7 @@ def set_controls(procedure: Procedures = Procedures.Calculate, **properties)\ Procedures.Simplex: Simplex, Procedures.DE: DE, Procedures.NS: NS, - Procedures.Dream: Dream + Procedures.Dream: Dream, } try: @@ -131,7 +137,7 @@ def set_controls(procedure: Procedures = Procedures.Calculate, **properties)\ except ValidationError as exc: custom_error_msgs = {"extra_forbidden": f'Extra inputs are not permitted. The fields for the {procedure}' f' controls procedure are:\n ' - f'{", ".join(controls[procedure].model_fields.keys())}\n' + f'{", ".join(controls[procedure].model_fields.keys())}\n', } custom_error_list = custom_pydantic_validation_error(exc.errors(), custom_error_msgs) raise ValidationError.from_exception_data(exc.title, custom_error_list) from None diff --git a/RAT/events.py b/RAT/events.py index de8cbcae..d5679984 100644 --- a/RAT/events.py +++ b/RAT/events.py @@ -4,7 +4,7 @@ def notify(event_type: EventTypes, data: Union[str, PlotEventData, ProgressEventData]) -> None: - """Calls registered callbacks with the data when event type has + """Calls registered callbacks with the data when event type has been triggered. Parameters @@ -13,6 +13,7 @@ def notify(event_type: EventTypes, data: Union[str, PlotEventData, ProgressEvent The event type that was triggered. data : str or PlotEventData or ProgressEventData The data sent by the event. The message event data is a string. + """ callbacks = __event_callbacks[event_type] for callback in callbacks: @@ -31,6 +32,7 @@ def get_event_callback(event_type: EventTypes) -> List[Callable[[Union[str, Plot ------- callback : Callable[[Union[str, PlotEventData, ProgressEventData]], None] The callback for the event type. + """ return list(__event_callbacks[event_type]) @@ -44,10 +46,11 @@ def register(event_type: EventTypes, callback: Callable[[Union[str, PlotEventDat The event type to register. callback : Callable[[Union[str, PlotEventData, ProgressEventData]], None] The callback for when the event is triggered. + """ if not isinstance(event_type, EventTypes): raise ValueError("event_type must be a events.EventTypes enum") - + if len(__event_callbacks[event_type]) == 0: __event_impl.register(event_type) __event_callbacks[event_type].add(callback) diff --git a/RAT/examples/absorption/volume_thiol_bilayer.py b/RAT/examples/absorption/volume_thiol_bilayer.py index 8896007d..b6ba6830 100644 --- a/RAT/examples/absorption/volume_thiol_bilayer.py +++ b/RAT/examples/absorption/volume_thiol_bilayer.py @@ -1,6 +1,5 @@ def volume_thiol_bilayer(params, bulk_in, bulk_out, contrast): - """ - volumeThiolBilayer RAT Custom Layer Model File. + """VolumeThiolBilayer RAT Custom Layer Model File. This file accepts 3 vectors containing the values for params, bulk in and bulk out. The final parameter is an index of the contrast being calculated diff --git a/RAT/examples/domains/alloy_domains.py b/RAT/examples/domains/alloy_domains.py index eb919a62..40db8338 100644 --- a/RAT/examples/domains/alloy_domains.py +++ b/RAT/examples/domains/alloy_domains.py @@ -3,7 +3,6 @@ def alloy_domains(params, bulkIn, bulkOut, contrast, domain): """Simple custom model for testing incoherent summing. Simple two layer of permalloy / gold, with up/down domains. """ - # Split up the parameters subRough = params[0] alloyThick = params[1] diff --git a/RAT/examples/domains/domains_XY_model.py b/RAT/examples/domains/domains_XY_model.py index 403c7e50..9e6c59c8 100644 --- a/RAT/examples/domains/domains_XY_model.py +++ b/RAT/examples/domains/domains_XY_model.py @@ -52,7 +52,7 @@ def domains_XY_model(params, bulk_in, bulk_out, contrast, domain): def makeLayer(z, prevLaySurf, thickness, height, Sigma_L, Sigma_R): - """ This produces a layer, with a defined thickness, height and roughness. + """This produces a layer, with a defined thickness, height and roughness. Each side of the layer has its own roughness value. """ # Find the edges diff --git a/RAT/examples/languages/custom_bilayer.py b/RAT/examples/languages/custom_bilayer.py index e7181cf8..9798e699 100644 --- a/RAT/examples/languages/custom_bilayer.py +++ b/RAT/examples/languages/custom_bilayer.py @@ -14,7 +14,7 @@ def custom_bilayer(params, bulk_in, bulk_out, contrast): # We have a constant SLD for the bilayer oxide_SLD = 3.41e-6 - + # Now make the lipid layers # Use known lipid volume and compositions # to make the layers @@ -29,7 +29,7 @@ def custom_bilayer(params, bulk_in, bulk_out, contrast): # Now make the lipid groups COO = (4*bo) + (2*bc) GLYC = (3*bc) + (5*bh) - CH3 = (2*bc) + (6*bh) + CH3 = (2*bc) + (6*bh) PO4 = (1*bp) + (4*bo) CH2 = (1*bc) + (2*bh) CHOL = (5*bc) + (12*bh) + (1*bn) @@ -54,14 +54,14 @@ def custom_bilayer(params, bulk_in, bulk_out, contrast): # Manually deal with hydration for layers in this example. oxSLD = (oxide_hydration * bulk_out[contrast]) + ((1 - oxide_hydration) * oxide_SLD) headSLD = (headHydration * bulk_out[contrast]) + ((1 - headHydration) * SLDhead) - tailSLD = (bilayerHydration * bulk_out[contrast]) + ((1 - bilayerHydration) * SLDtail) + tailSLD = (bilayerHydration * bulk_out[contrast]) + ((1 - bilayerHydration) * SLDtail) # Make the layers oxide = [oxide_thick, oxSLD, sub_rough] water = [waterThick, bulk_out[contrast], bilayerRough] head = [headThick, headSLD, bilayerRough] tail = [tailThick, tailSLD, bilayerRough] - + output = np.array([oxide, water, head, tail, tail, head]) return output, sub_rough diff --git a/RAT/examples/languages/setup_problem.py b/RAT/examples/languages/setup_problem.py index 33313e03..f3bd448b 100644 --- a/RAT/examples/languages/setup_problem.py +++ b/RAT/examples/languages/setup_problem.py @@ -1,5 +1,4 @@ -""" -Custom Layers example for Supported DSPC layer. +"""Custom Layers example for Supported DSPC layer. Example of using custom layers to model a DSPC supported bilayer. """ diff --git a/RAT/examples/non_polarised/DSPC_custom_XY.py b/RAT/examples/non_polarised/DSPC_custom_XY.py index 33cb0061..da05f7c5 100644 --- a/RAT/examples/non_polarised/DSPC_custom_XY.py +++ b/RAT/examples/non_polarised/DSPC_custom_XY.py @@ -1,5 +1,4 @@ -""" -Custom XY Example for Supported DSPC layer. +"""Custom XY Example for Supported DSPC layer. In this example, we model the same data (DSPC supported bilayer) as the Custom Layers example, but this time we will use continuous distributions of the volume fractions of each component to build up the SLD profiles (as described in @@ -19,7 +18,7 @@ does not permit any hydration, so to deal with this, we can simply scale the (full occupation) Heaviside functions by relevant coverage parameters. When this is correctly done, we can obtain the remaining water distribution as -$${\textrm{VF}}_{\textrm{wat}} =1-\sum_n {\textrm{VF}}_n$$ +$${\textrm{VF}}_{\textrm{wat}} =1-\\sum_n {\textrm{VF}}_n$$ where VFn is the Volume Fraction of the n'th layer. """ diff --git a/RAT/examples/non_polarised/DSPC_custom_layers.py b/RAT/examples/non_polarised/DSPC_custom_layers.py index b279eb1a..0b1fdf2f 100644 --- a/RAT/examples/non_polarised/DSPC_custom_layers.py +++ b/RAT/examples/non_polarised/DSPC_custom_layers.py @@ -1,5 +1,4 @@ -""" -Custom Layers example for Supported DSPC layer. +"""Custom Layers example for Supported DSPC layer. Example of using custom layers to model a DSPC supported bilayer. """ diff --git a/RAT/examples/non_polarised/custom_XY_DSPC.py b/RAT/examples/non_polarised/custom_XY_DSPC.py index f4bfffcd..883188cb 100644 --- a/RAT/examples/non_polarised/custom_XY_DSPC.py +++ b/RAT/examples/non_polarised/custom_XY_DSPC.py @@ -5,7 +5,6 @@ def custom_XY_DSPC(params, bulk_in, bulk_out, contrast): """This function makes a model of a supported DSPC bilayer using volume restricted distribution functions.""" - # Split up the parameters subRough = params[0] oxideThick = params[1] @@ -119,8 +118,7 @@ def custom_XY_DSPC(params, bulk_in, bulk_out, contrast): def layer(z, prevLaySurf, thickness, height, Sigma_L, Sigma_R): - """ - This produces a layer, with a defined thickness, height and roughness. + """This produces a layer, with a defined thickness, height and roughness. Each side of the layer has its own roughness value. """ # Find the edges diff --git a/RAT/examples/non_polarised/custom_bilayer_DSPC.py b/RAT/examples/non_polarised/custom_bilayer_DSPC.py index ead61e0c..3c10a64f 100644 --- a/RAT/examples/non_polarised/custom_bilayer_DSPC.py +++ b/RAT/examples/non_polarised/custom_bilayer_DSPC.py @@ -2,8 +2,7 @@ def custom_bilayer_DSPC(params, bulk_in, bulk_out, contrast): - """ - CUSTOMBILAYER RAT Custom Layer Model File. + """CUSTOMBILAYER RAT Custom Layer Model File. This file accepts 3 vectors containing the values for params, bulk in and bulk out. The final parameter is an index of the contrast being calculated. @@ -24,7 +23,6 @@ def custom_bilayer_DSPC(params, bulk_in, bulk_out, contrast): The second output parameter should be the substrate roughness. """ - sub_rough = params[0] oxide_thick = params[1] oxide_hydration = params[2] diff --git a/RAT/inputs.py b/RAT/inputs.py index 22e168cd..b8a2dc37 100644 --- a/RAT/inputs.py +++ b/RAT/inputs.py @@ -12,7 +12,7 @@ def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, - RAT.controls.NS, RAT.controls.Dream] + RAT.controls.NS, RAT.controls.Dream], ) -> tuple[ProblemDefinition, Cells, Limits, Priors, Control]: """Constructs the inputs required for the compiled RAT code using the data defined in the input project and controls. @@ -36,8 +36,8 @@ def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT The priors defined for each parameter in the project. cpp_controls : RAT.rat_core.Control The controls object used in the compiled RAT code. - """ + """ parameter_field = {"parameters": "param", "bulk_in": "bulkIn", "bulk_out": "bulkOut", @@ -102,6 +102,7 @@ def make_problem(project: RAT.Project) -> ProblemDefinition: ------- problem : RAT.rat_core.ProblemDefinition The problem input used in the compiled RAT code. + """ action_id = {"add": 1, "subtract": 2} @@ -188,6 +189,7 @@ def make_resample(project: RAT.Project) -> list[int]: ------- : list[int] The "resample" field of the problem input used in the compiled RAT code. + """ return [contrast.resample for contrast in project.contrasts] @@ -204,6 +206,7 @@ def make_data_present(project: RAT.Project) -> list[int]: ------- : list[int] The "dataPresent" field of the problem input used in the compiled RAT code. + """ return [1 if project.data[project.data.index(contrast.data)].data.size != 0 else 0 for contrast in project.contrasts] @@ -217,6 +220,7 @@ def check_indices(problem: ProblemDefinition) -> None: ---------- problem : RAT.rat_core.ProblemDefinition The problem input used in the compiled RAT code. + """ index_list = {"bulkIn": "contrastBulkIns", "bulkOut": "contrastBulkOuts", @@ -236,7 +240,6 @@ def check_indices(problem: ProblemDefinition) -> None: raise IndexError(f'The problem field "{index_list[params]}" contains: {", ".join(str(i) for i in elements)}' f', which lie outside of the range of "{params}"') - return None def make_cells(project: RAT.Project) -> Cells: @@ -253,8 +256,8 @@ def make_cells(project: RAT.Project) -> Cells: ------- cells : RAT.rat_core.Cells The set of inputs that are defined in MATLAB as cell arrays. - """ + """ hydrate_id = {"bulk in": 1, "bulk out": 2} # Set contrast parameters according to model type @@ -360,6 +363,7 @@ def get_python_handle(file_name: str, function_name: str, path: Union[str, pathl ------- handle : Callable The handle of the function defined in the python module file. + """ spec = importlib.util.spec_from_file_location(pathlib.Path(file_name).stem, os.path.join(path, file_name)) custom_module = importlib.util.module_from_spec(spec) @@ -383,8 +387,8 @@ def make_controls(controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, ------- controls : RAT.rat_core.Control The controls object used in the compiled RAT code. - """ + """ full_controls = RAT.controls.Controls(**controls.model_dump()) cpp_controls = Control() diff --git a/RAT/models.py b/RAT/models.py index dd3b4092..9f8bd099 100644 --- a/RAT/models.py +++ b/RAT/models.py @@ -35,6 +35,7 @@ def int_sequence(): class RATModel(BaseModel, validate_assignment=True, extra="forbid"): """A BaseModel where enums are represented by their value.""" + def __repr__(self): fields_repr = (", ".join(repr(v) if a is None else f"{a}={v.value!r}" if isinstance(v, StrEnum) else @@ -47,6 +48,7 @@ def __repr__(self): class Background(RATModel): """Defines the Backgrounds in RAT.""" + name: str = Field(default_factory=lambda: "New Background " + next(background_number), min_length=1) type: TypeOptions = TypeOptions.Constant value_1: str = "" @@ -58,6 +60,7 @@ class Background(RATModel): class Contrast(RATModel): """Groups together all of the components of the model.""" + name: str = Field(default_factory=lambda: "New Contrast " + next(contrast_number), min_length=1) data: str = "" background: str = "" @@ -72,6 +75,7 @@ class Contrast(RATModel): class ContrastWithRatio(RATModel): """Groups together all of the components of the model including domain terms.""" + name: str = Field(default_factory=lambda: "New Contrast " + next(contrast_number), min_length=1) data: str = "" background: str = "" @@ -87,6 +91,7 @@ class ContrastWithRatio(RATModel): class CustomFile(RATModel): """Defines the files containing functions to run when using custom models.""" + name: str = Field(default_factory=lambda: "New Custom File " + next(custom_file_number), min_length=1) filename: str = "" function_name: str = "" @@ -97,7 +102,6 @@ def model_post_init(self, __context: Any) -> None: """If a "filename" is supplied but the "function_name" field is not set, the "function_name" should be set to the file name without the extension. """ - if "filename" in self.model_fields_set and "function_name" not in self.model_fields_set: self.function_name = pathlib.Path(self.filename).stem @@ -109,10 +113,11 @@ def set_matlab_function_name(self): self.function_name = pathlib.Path(self.filename).stem return self - + class Data(RATModel, arbitrary_types_allowed=True): """Defines the dataset required for each contrast.""" + name: str = Field(default_factory=lambda: "New Data " + next(data_number), min_length=1) data: np.ndarray[np.float64] = np.empty([0, 3]) data_range: list[float] = Field(default=[], min_length=2, max_length=2) @@ -168,7 +173,7 @@ def check_ranges(self) -> "Data": f"min/max values of the data: [{data_min}, {data_max}]") return self - def __eq__(self, other: Any) -> bool: + def __eq__(self, other: object) -> bool: if isinstance(other, BaseModel): # When comparing instances of generic types for equality, as long as all field values are equal, # only require their generic origin types to be equal, rather than exact type equality. @@ -201,12 +206,14 @@ def __repr__(self): class DomainContrast(RATModel): """Groups together the layers required for each domain.""" + name: str = Field(default_factory=lambda: "New Domain Contrast " + next(domain_contrast_number), min_length=1) model: list[str] = [] class Layer(RATModel, populate_by_name=True): """Combines parameters into defined layers.""" + name: str = Field(default_factory=lambda: "New Layer " + next(layer_number), min_length=1) thickness: str SLD: str = Field(validation_alias="SLD_real") @@ -217,6 +224,7 @@ class Layer(RATModel, populate_by_name=True): class AbsorptionLayer(RATModel, populate_by_name=True): """Combines parameters into defined layers including absorption terms.""" + name: str = Field(default_factory=lambda: "New Layer " + next(layer_number), min_length=1) thickness: str SLD_real: str = Field(validation_alias="SLD") @@ -228,6 +236,7 @@ class AbsorptionLayer(RATModel, populate_by_name=True): class Parameter(RATModel): """Defines parameters needed to specify the model.""" + name: str = Field(default_factory=lambda: "New Parameter " + next(parameter_number), min_length=1) min: float = 0.0 value: float = 0.0 @@ -254,11 +263,13 @@ def check_value_in_range(self) -> "Parameter": class ProtectedParameter(Parameter): """A Parameter with a fixed name.""" + name: str = Field(frozen=True, min_length=1) class Resolution(RATModel): """Defines Resolutions in RAT.""" + name: str = Field(default_factory=lambda: "New Resolution " + next(resolution_number), min_length=1) type: TypeOptions = TypeOptions.Constant value_1: str = "" diff --git a/RAT/outputs.py b/RAT/outputs.py index bc0daa30..5323dca9 100644 --- a/RAT/outputs.py +++ b/RAT/outputs.py @@ -111,9 +111,8 @@ class BayesResults(Results): def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResult, bayes_results: Optional[RAT.rat_core.BayesResults] = None) -> Union[Results, BayesResults]: """Initialise a python Results or BayesResults object using the outputs from a RAT calculation.""" - calculation_results = CalculationResults(chiValues=output_results.calculationResults.chiValues, - sumChi=output_results.calculationResults.sumChi + sumChi=output_results.calculationResults.sumChi, ) contrast_params = ContrastParams( backgroundParams=output_results.contrastParams.backgroundParams, @@ -122,7 +121,7 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul bulkOut=output_results.contrastParams.bulkOut, resolutionParams=output_results.contrastParams.resolutionParams, subRoughs=output_results.contrastParams.subRoughs, - resample=output_results.contrastParams.resample + resample=output_results.contrastParams.resample, ) if procedure in [Procedures.NS, Procedures.Dream]: @@ -132,13 +131,13 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul sld=bayes_results.predictionIntervals.sld, reflectivityXData=bayes_results.predictionIntervals.reflectivityXData, sldXData=bayes_results.predictionIntervals.sldXData, - sampleChi=bayes_results.predictionIntervals.sampleChi + sampleChi=bayes_results.predictionIntervals.sampleChi, ) confidence_intervals = ConfidenceIntervals( percentile95=bayes_results.confidenceIntervals.percentile95, percentile65=bayes_results.confidenceIntervals.percentile65, - mean=bayes_results.confidenceIntervals.mean + mean=bayes_results.confidenceIntervals.mean, ) dream_params = DreamParams( @@ -160,7 +159,7 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul ABC=bool(bayes_results.dreamParams.ABC), IO=bool(bayes_results.dreamParams.IO), storeOutput=bool(bayes_results.dreamParams.storeOutput), - R=bayes_results.dreamParams.R + R=bayes_results.dreamParams.R, ) dream_output = DreamOutput( @@ -171,13 +170,13 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul modelOutput=bayes_results.dreamOutput.modelOutput, AR=bayes_results.dreamOutput.AR, R_stat=bayes_results.dreamOutput.R_stat, - CR=bayes_results.dreamOutput.CR + CR=bayes_results.dreamOutput.CR, ) nested_sampler_output = NestedSamplerOutput( logZ=bayes_results.nestedSamplerOutput.logZ, nestSamples=bayes_results.nestedSamplerOutput.nestSamples, - postSamples=bayes_results.nestedSamplerOutput.postSamples + postSamples=bayes_results.nestedSamplerOutput.postSamples, ) results = BayesResults( @@ -196,7 +195,7 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul dreamParams=dream_params, dreamOutput=dream_output, nestedSamplerOutput=nested_sampler_output, - chain=bayes_results.chain + chain=bayes_results.chain, ) else: @@ -211,7 +210,7 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul calculationResults=calculation_results, contrastParams=contrast_params, fitParams=output_results.fitParams, - fitNames=output_results.fitNames + fitNames=output_results.fitNames, ) return results diff --git a/RAT/project.py b/RAT/project.py index 842b688f..c8c0630c 100644 --- a/RAT/project.py +++ b/RAT/project.py @@ -84,6 +84,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid", arbitrary_typ This class combines the data defined in each of the pydantic models included in "models.py" into the full set of inputs required for a reflectivity calculation. """ + name: str = "" calculation: Calculations = Calculations.NonPolarised model: LayerModels = LayerModels.StandardLayers @@ -388,6 +389,7 @@ def check_allowed_values(self, attribute: str, field_list: list[str], allowed_va ------ ValueError Raised if any field in field_list has a value not specified in allowed_values. + """ class_list = getattr(self, attribute) for model in class_list: @@ -415,6 +417,7 @@ def check_contrast_model_allowed_values(self, contrast_attribute: str, allowed_v ------ ValueError Raised if any model in contrast_attribute has a value not specified in allowed_values. + """ class_list = getattr(self, contrast_attribute) for contrast in class_list: @@ -430,6 +433,7 @@ def get_contrast_model_field(self): ------- model_field : str The name of the field used to define the contrasts' model field. + """ if self.model == LayerModels.StandardLayers: if self.calculation == Calculations.Domains: @@ -449,6 +453,7 @@ def write_script(self, obj_name: str = "problem", script: str = "project_script. The name given to the project object under construction (default is "problem"). script : str, optional The filepath of the generated script (default is "project_script.py"). + """ # Need to ensure correct format for script name file_parts = os.path.splitext(script) @@ -491,6 +496,7 @@ def _classlist_wrapper(self, class_list: ClassList, func: Callable): ------- wrapped_func : Callable The wrapped routine. + """ @functools.wraps(func) def wrapped_func(*args, **kwargs): diff --git a/RAT/run.py b/RAT/run.py index 01be091e..b2ce25d1 100644 --- a/RAT/run.py +++ b/RAT/run.py @@ -5,7 +5,6 @@ def run(project, controls): """Run RAT for the given project and controls inputs.""" - parameter_field = {"parameters": "params", "bulk_in": "bulkIn", "bulk_out": "bulkOut", diff --git a/RAT/utils/custom_errors.py b/RAT/utils/custom_errors.py index 4615724f..d3d2bd8e 100644 --- a/RAT/utils/custom_errors.py +++ b/RAT/utils/custom_errors.py @@ -3,7 +3,7 @@ def custom_pydantic_validation_error(error_list: list[pydantic_core.ErrorDetails], - custom_error_msgs: dict[str, str] = None + custom_error_msgs: dict[str, str] = None, ) -> list[pydantic_core.ErrorDetails]: """Run through the list of errors generated from a pydantic ValidationError, substituting the standard error for a PydanticCustomError for a given set of error types. @@ -22,6 +22,7 @@ def custom_pydantic_validation_error(error_list: list[pydantic_core.ErrorDetails ------- new_error : list[pydantic_core.ErrorDetails] A list of errors including PydanticCustomErrors in place of the error types in custom_errors. + """ if custom_error_msgs is None: custom_error_msgs = {} diff --git a/RAT/utils/enums.py b/RAT/utils/enums.py index 306768c6..9e22b380 100644 --- a/RAT/utils/enums.py +++ b/RAT/utils/enums.py @@ -9,6 +9,7 @@ # Controls class Parallel(StrEnum): """Defines the available options for parallelization""" + Single = "single" Points = "points" Contrasts = "contrasts" @@ -16,6 +17,7 @@ class Parallel(StrEnum): class Procedures(StrEnum): """Defines the available options for procedures""" + Calculate = "calculate" Simplex = "simplex" DE = "de" @@ -25,6 +27,7 @@ class Procedures(StrEnum): class Display(StrEnum): """Defines the available options for display""" + Off = "off" Iter = "iter" Notify = "notify" @@ -33,6 +36,7 @@ class Display(StrEnum): class BoundHandling(StrEnum): """Defines the available options for bound handling""" + Off = "off" Reflect = "reflect" Bound = "bound" @@ -41,6 +45,7 @@ class BoundHandling(StrEnum): class Strategies(Enum): """Defines the available options for strategies""" + Random = 1 LocalToBest = 2 BestWithJitter = 3 diff --git a/RAT/utils/plotting.py b/RAT/utils/plotting.py index 8f381431..0a2cc351 100644 --- a/RAT/utils/plotting.py +++ b/RAT/utils/plotting.py @@ -1,5 +1,4 @@ -""" -Plots using the matplotlib library +"""Plots using the matplotlib library """ from typing import Optional, Union @@ -14,13 +13,11 @@ class Figure: - """ - Creates a plotting figure. + """Creates a plotting figure. """ def __init__(self, row: int = 1, col: int = 1): - """ - Initializes the figure and the subplots. + """Initializes the figure and the subplots. Parameters ---------- @@ -28,6 +25,7 @@ def __init__(self, row: int = 1, col: int = 1): The number of rows in subplot col : int, default: 1 The number of columns in subplot + """ self._fig, self._ax = \ plt.subplots(row, col, num="Reflectivity Algorithms Toolbox (RAT)") @@ -40,8 +38,7 @@ def __init__(self, row: int = 1, col: int = 1): self._close) def wait_for_close(self): - """ - Waits for the user to close the figure + """Waits for the user to close the figure using the esc key. """ while not (self._esc_pressed or self._close_clicked): @@ -49,23 +46,20 @@ def wait_for_close(self): plt.close(self._fig) def _process_button_press(self, event): - """ - Process the key_press_event. + """Process the key_press_event. """ if event.key == "escape": self._esc_pressed = True def _close(self, _): - """ - Process the close_event. + """Process the close_event. """ self._close_clicked = True def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, one_sided: bool, color: str): - """ - Plots the error bars. + """Plots the error bars. Parameters ---------- @@ -81,6 +75,7 @@ def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, A boolean to indicate whether to draw one sided errorbars color : str The hex representing the color of the errorbars + """ y_error = [[0]*len(err), err] if one_sided else err ax.errorbar(x=x, @@ -94,8 +89,7 @@ def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay: bool = True): - """ - Clears the previous plots and updates the ref and SLD plots. + """Clears the previous plots and updates the ref and SLD plots. Parameters ---------- @@ -111,6 +105,7 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay ------- fig : Figure The figure class that has two subplots + """ if fig is None: fig = Figure(1, 2) @@ -198,14 +193,13 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay if delay: plt.pause(0.005) - + return fig def plot_ref_sld(project: RAT.Project, results: Union[RAT.outputs.Results, RAT.outputs.BayesResults], block: bool = False): - """ - Plots the reflectivity and SLD profiles. + """Plots the reflectivity and SLD profiles. Parameters ---------- @@ -215,6 +209,7 @@ def plot_ref_sld(project: RAT.Project, results: Union[RAT.outputs.Results, RAT.o The result from the calculation block : bool, default: False Indicates the plot should block until it is closed + """ data = PlotEventData() diff --git a/RAT/wrappers.py b/RAT/wrappers.py index 53cb5846..b10d014f 100644 --- a/RAT/wrappers.py +++ b/RAT/wrappers.py @@ -14,7 +14,9 @@ class MatlabWrapper: ---------- filename : string The path of the file containing MATLAB function + """ + def __init__(self, filename: str) -> None: self.engine = None try: @@ -37,9 +39,10 @@ def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tup ------- wrapper : Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tuple[ArrayLike, float]] The wrapper function for the MATLAB callback + """ def handle(params, bulk_in, bulk_out, contrast, domain=-1): - if domain == -1: + if domain == -1: output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, "float"), np.array(bulk_in, "float"), np.array(bulk_out, "float"), @@ -50,8 +53,8 @@ def handle(params, bulk_in, bulk_out, contrast, domain=-1): np.array(bulk_out, "float"), float(contrast + 1), float(domain + 1), nargout=2) - return output, sub_rough - return handle + return output, sub_rough + return handle class DylibWrapper: @@ -63,10 +66,12 @@ class DylibWrapper: The path of the dynamic library function_name : str The name of the function to call + """ + def __init__(self, filename, function_name) -> None: self.engine = RAT.rat_core.DylibEngine(filename, function_name) - + def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tuple[ArrayLike, float]]: """Returns a wrapper for the custom dynamic library function @@ -74,11 +79,12 @@ def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tup ------- wrapper : Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tuple[ArrayLike, float]] The wrapper function for the dynamic library callback + """ def handle(params, bulk_in, bulk_out, contrast, domain=-1): - if domain == -1: + if domain == -1: output, sub_rough = self.engine.invoke(params, bulk_in, bulk_out, contrast) else: output, sub_rough = self.engine.invoke(params, bulk_in, bulk_out, contrast, domain) - return output, sub_rough + return output, sub_rough return handle diff --git a/pyproject.toml b/pyproject.toml index b81afc73..a1f35ffa 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,5 +10,8 @@ build-backend = 'setuptools.build_meta' line-length = 120 [tool.ruff.lint] -select = ["E", "F", "UP", "B", "SIM", "I"] +select = ["E", "F", "UP", "B", "SIM", "I", "COM"] ignore = ["SIM108"] + +[tool.ruff.lint.flake8-pytest-style] +fixture-parentheses = false diff --git a/setup.py b/setup.py index 4b16947a..a9fc1f3b 100644 --- a/setup.py +++ b/setup.py @@ -25,7 +25,7 @@ pybind11.get_include(True), "cpp/RAT/", ], - language="c++" + language="c++", ), ] @@ -55,6 +55,7 @@ def get_shared_object_name(lib_name): class BuildExt(build_ext): """A custom build extension for adding compiler-specific options.""" + c_opts = { "msvc": ["/EHsc"], "unix": ["-fopenmp", "-std=c++11"], @@ -86,15 +87,15 @@ def build_extensions(self): ext.extra_compile_args = opts ext.extra_link_args = link_opts build_ext.build_extensions(self) - + def run(self): super().run() build_py = self.get_finalized_command("build_py") package_dir = f"{build_py.build_lib}/RAT/" for p in Path(package_dir).glob("**/*"): if p.suffix in {".exp", ".a", ".lib"}: - p.unlink() - + p.unlink() + if self.inplace: obj_name = get_shared_object_name(libevent[0]) src = f"{build_py.build_lib}/RAT/{obj_name}" @@ -133,18 +134,18 @@ def build_libraries(self, libraries): macros=macros, include_dirs=include_dirs, extra_postargs=cflags, - debug=self.debug + debug=self.debug, ) language = self.compiler.detect_language(sources) self.compiler.link_shared_object( - objects, + objects, get_shared_object_name(lib_name), - output_dir=self.build_clib, - target_lang=language + output_dir=self.build_clib, + target_lang=language, ) super().build_libraries(libraries) - + setup( name="RAT", diff --git a/tests/conftest.py b/tests/conftest.py index d9c2e698..a0278214 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -439,7 +439,7 @@ def reflectivity_calculation_results(): "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", - "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"] + "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"], ) @@ -1333,7 +1333,7 @@ def dream_results(): resampledLayers=[[np.array([[0., 0., 0.]])], [np.array([[0., 0., 0.]])]], calculationResults=RAT.outputs.CalculationResults( chiValues=np.array([4.6077885, 7.00028098]), - sumChi=11.608069475997699 + sumChi=11.608069475997699, ), contrastParams=RAT.outputs.ContrastParams( backgroundParams=np.array([2.37113128e-06, 1.99006694e-06]), @@ -1342,7 +1342,7 @@ def dream_results(): bulkOut=np.array([6.01489149e-06, 1.59371685e-06]), resolutionParams=np.array([0.03, 0.03]), subRoughs=np.array([6.19503045, 6.19503045]), - resample=np.array([0.0, 0.0]) + resample=np.array([0.0, 0.0]), ), fitParams=np.array([6.19503045e+00, 1.84420960e+01, 2.11039621e+01, 8.75538121e+00, 3.72292994e+00, 1.84624551e+01, 1.02316734e+01, 2.31156093e+01, @@ -1698,7 +1698,7 @@ def dream_results(): 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16]) + 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16]), ), confidenceIntervals=RAT.outputs.ConfidenceIntervals( percentile65=np.array([[-1.29074586e-231, 8.33251318e+000, 1.75397363e+001, 1.75397363e+001, @@ -1725,7 +1725,7 @@ def dream_results(): 2.78042232e+01, 1.08043784e+01, 1.00488530e+01, 2.01086197e+01, 2.42251646e+01, 1.55593097e+01, 1.49022278e+01, 3.20423080e+01, 4.85551946e+01, 3.87046084e+01, 1.45612723e+01, 3.15508089e-06, - 3.29384190e-06, 5.26668495e-06]]) + 3.29384190e-06, 5.26668495e-06]]), ), dreamParams=RAT.outputs.DreamParams( nParams=18.0, @@ -1781,7 +1781,7 @@ def dream_results(): nestedSamplerOutput=RAT.outputs.NestedSamplerOutput( logZ=0.0, nestSamples=np.array([[0.0, 0.0]]), - postSamples=np.array([[0.0, 0.0]]) + postSamples=np.array([[0.0, 0.0]]), ), chain=np.array([[-1.29231905e-231, 8.33251318e+000, 5.48185565e+001, 1.75397363e+001, 4.57554170e+001, 9.85302945e+000, 1.17557273e+001, 8.34197863e+000, @@ -1792,5 +1792,5 @@ def dream_results(): 9.85302945e+000, 1.17557273e+001, 8.34197863e+000, 3.18752608e+001, 1.65750684e+001, 1.45435510e+001, 1.52609047e+001, 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 5.87958515e-006, 4.65378475e-006]]) + 5.87958515e-006, 4.65378475e-006]]), ) diff --git a/tests/test_classlist.py b/tests/test_classlist.py index a57017d9..ad818e39 100644 --- a/tests/test_classlist.py +++ b/tests/test_classlist.py @@ -92,7 +92,8 @@ def test_input_sequence_subclass(self, input_sequence: Sequence[object]) -> None @pytest.mark.parametrize("empty_input", [([]), (())]) def test_empty_input(self, empty_input: Sequence[object]) -> None: """If we initialise a ClassList with an empty input (list or tuple), the ClassList should be an empty list, and - _class_handle should be unset.""" + _class_handle should be unset. + """ class_list = ClassList(empty_input) assert class_list.data == [] assert not hasattr(class_list, "_class_handle") @@ -112,7 +113,8 @@ def test_different_classes(self, input_list: Sequence[object]) -> None: ]) def test_identical_name_fields(self, input_list: Sequence[object], name_field: str) -> None: """If we initialise a ClassList with input objects with identical values of the name_field, - we should raise a ValueError.""" + we should raise a ValueError. + """ with pytest.raises(ValueError, match=f"Input list contains objects with the same value of the {name_field} attribute"): ClassList(input_list, name_field=name_field) @@ -125,7 +127,8 @@ def test_repr_table(two_name_class_list: ClassList, two_name_class_list_table: s def test_repr_empty_table() -> None: """For empty classes with the __dict__ attribute, we should be able to print the contents of the ClassList as a - list.""" + list. + """ empty_attributes = InputAttributes() assert repr(ClassList(empty_attributes)) == repr([empty_attributes]) @@ -196,7 +199,8 @@ def test_delitem_not_present(two_name_class_list: ClassList) -> None: ]) def test_iadd(two_name_class_list: ClassList, added_list: Iterable, three_name_class_list: ClassList) -> None: """We should be able to use the "+=" operator to add iterables to a ClassList. Individual objects should be wrapped - in a list before being added.""" + in a list before being added. + """ class_list = two_name_class_list class_list += added_list assert class_list == three_name_class_list @@ -209,7 +213,8 @@ def test_iadd(two_name_class_list: ClassList, added_list: Iterable, three_name_c ]) def test_iadd_empty_classlist(added_list: Sequence, two_name_class_list: ClassList) -> None: """We should be able to use the "+=" operator to add iterables to an empty ClassList, whilst also setting - _class_handle.""" + _class_handle. + """ class_list = ClassList() class_list += added_list assert class_list == two_name_class_list @@ -275,7 +280,8 @@ def test_append_object_and_kwargs(two_name_class_list: ClassList, new_values: dict[str, Any], three_name_class_list: ClassList) -> None: """If we append to a ClassList using a new object and keyword arguments, we raise a warning, and append the object, - discarding the keyword arguments.""" + discarding the keyword arguments. + """ class_list = two_name_class_list with pytest.warns(SyntaxWarning): warnings.warn("ClassList.append() called with both an object and keyword arguments. " @@ -329,7 +335,7 @@ def test_append_kwargs_same_name_field(two_name_class_list: ClassList, new_value @pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Eve")) + (InputAttributes(name="Eve")), ]) def test_insert_object(two_name_class_list: ClassList, new_object: object) -> None: """We should be able to insert an object within a ClassList using a new object.""" @@ -340,7 +346,7 @@ def test_insert_object(two_name_class_list: ClassList, new_object: object) -> No @pytest.mark.parametrize("new_values", [ - ({"name": "Eve"}) + ({"name": "Eve"}), ]) def test_insert_kwargs(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """We should be able to insert an object within a ClassList using keyword arguments.""" @@ -358,7 +364,8 @@ def test_insert_object_and_kwargs(two_name_class_list: ClassList, new_values: dict[str, Any], three_name_class_list: ClassList) -> None: """If call insert() on a ClassList using a new object and keyword arguments, we raise a warning, and append the - object, discarding the keyword arguments.""" + object, discarding the keyword arguments. + """ class_list = two_name_class_list with pytest.warns(SyntaxWarning): warnings.warn("ClassList.insert() called with both an object and keyword arguments. " @@ -393,7 +400,7 @@ def test_insert_kwargs_empty_classlist(new_values: dict[str, Any]) -> None: @pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Alice")) + (InputAttributes(name="Alice")), ]) def test_insert_object_same_name(two_name_class_list: ClassList, new_object: object) -> None: """If we insert an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" @@ -403,7 +410,7 @@ def test_insert_object_same_name(two_name_class_list: ClassList, new_object: obj @pytest.mark.parametrize("new_values", [ - ({"name": "Alice"}) + ({"name": "Alice"}), ]) def test_insert_kwargs_same_name(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we insert an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" @@ -419,7 +426,8 @@ def test_insert_kwargs_same_name(two_name_class_list: ClassList, new_values: dic ]) def test_remove(two_name_class_list: ClassList, remove_value: Union[object, str]) -> None: """We should be able to remove an object either by the value of the name_field or by specifying the object - itself.""" + itself. + """ two_name_class_list.remove(remove_value) assert two_name_class_list == ClassList([InputAttributes(name="Alice")]) @@ -488,7 +496,8 @@ def test_index_not_present(two_name_class_list: ClassList, index_value: Union[ob ]) def test_extend(two_name_class_list: ClassList, extended_list: Sequence, three_name_class_list: ClassList) -> None: """We should be able to extend a ClassList using another ClassList or a sequence. Individual objects should be - wrapped in a list before being added.""" + wrapped in a list before being added. + """ class_list = two_name_class_list class_list.extend(extended_list) assert class_list == three_name_class_list @@ -543,7 +552,8 @@ def test_set_fields_same_name_field(two_name_class_list: ClassList, new_values: ]) def test_get_names(class_list: ClassList, expected_names: list[str]) -> None: """We should get a list of the values of the name_field attribute from each object with it defined in the - ClassList.""" + ClassList. + """ assert class_list.get_names() == expected_names @@ -564,7 +574,8 @@ def test_get_all_matches(class_list: ClassList, expected_matches: list[tuple]) - ]) def test__validate_name_field(two_name_class_list: ClassList, input_dict: dict[str, Any]) -> None: """We should not raise an error if the input values do not contain a name_field value defined in an object in the - ClassList.""" + ClassList. + """ assert two_name_class_list._validate_name_field(input_dict) is None @@ -588,7 +599,8 @@ def test__validate_name_field_not_unique(two_name_class_list: ClassList, input_d ]) def test__check_unique_name_fields(two_name_class_list: ClassList, input_list: Iterable) -> None: """We should not raise an error if an input list contains objects with different name_field values, or if the - name_field is not defined.""" + name_field is not defined. + """ assert two_name_class_list._check_unique_name_fields(input_list) is None @@ -597,7 +609,8 @@ def test__check_unique_name_fields(two_name_class_list: ClassList, input_list: I ]) def test__check_unique_name_fields_not_unique(two_name_class_list: ClassList, input_list: Iterable) -> None: """We should raise a ValueError if an input list contains multiple objects with matching name_field values - defined.""" + defined. + """ with pytest.raises(ValueError, match=f"Input list contains objects with the same value of the " f"{two_name_class_list.name_field} attribute"): two_name_class_list._check_unique_name_fields(input_list) diff --git a/tests/test_controls.py b/tests/test_controls.py index 17b79381..4c2e6ba8 100644 --- a/tests/test_controls.py +++ b/tests/test_controls.py @@ -21,7 +21,7 @@ def setup_class(self): ("calcSldDuringFit", False), ("resampleParams", [0.9, 50]), ("display", Display.Iter), - ("procedure", Procedures.Calculate) + ("procedure", Procedures.Calculate), ]) def test_calculate_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Calculate class.""" @@ -31,7 +31,7 @@ def test_calculate_property_values(self, control_property: str, value: Any) -> N ("parallel", Parallel.Points), ("calcSldDuringFit", True), ("resampleParams", [0.2, 1]), - ("display", Display.Notify) + ("display", Display.Notify), ]) def test_calculate_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of Calculate class.""" @@ -61,7 +61,7 @@ def test_calculate_display_validation(self, value: Any) -> None: @pytest.mark.parametrize("value, msg", [ ([5.0], "List should have at least 2 items after validation, not 1"), - ([12, 13, 14], "List should have at most 2 items after validation, not 3") + ([12, 13, 14], "List should have at most 2 items after validation, not 3"), ]) def test_calculate_resampleParams_length_validation(self, value: list, msg: str) -> None: """Tests the resampleParams setter length validation in Calculate class.""" @@ -71,7 +71,7 @@ def test_calculate_resampleParams_length_validation(self, value: list, msg: str) @pytest.mark.parametrize("value, msg", [ ([1.0, 2], "Value error, resampleParams[0] must be between 0 and 1"), - ([0.5, -0.1], "Value error, resampleParams[1] must be greater than or equal to 0") + ([0.5, -0.1], "Value error, resampleParams[1] must be greater than or equal to 0"), ]) def test_calculate_resampleParams_value_validation(self, value: list, msg: str) -> None: """Tests the resampleParams setter value validation in Calculate class.""" @@ -132,7 +132,7 @@ def setup_class(self): ("maxFuncEvals", 10000), ("maxIterations", 1000), ("updateFreq", -1), - ("updatePlotFreq", 1) + ("updatePlotFreq", 1), ]) def test_simplex_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Simplex class.""" @@ -148,7 +148,7 @@ def test_simplex_property_values(self, control_property: str, value: Any) -> Non ("maxFuncEvals", 100), ("maxIterations", 50), ("updateFreq", 4), - ("updatePlotFreq", 3) + ("updatePlotFreq", 3), ]) def test_simplex_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of Simplex class.""" @@ -159,7 +159,7 @@ def test_simplex_property_setters(self, control_property: str, value: Any) -> N ("xTolerance", -4e-6), ("funcTolerance", -3e-4), ("maxFuncEvals", -100), - ("maxIterations", -50) + ("maxIterations", -50), ]) def test_simplex_property_errors(self, control_property: str, value: Union[float, int]) -> None: """Tests the property errors of Simplex class.""" @@ -195,7 +195,7 @@ def test_repr(self) -> None: "| parallel | single |\n" "| calcSldDuringFit | False |\n" "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" + "| display | iter |\n" "| xTolerance | 1e-06 |\n" "| funcTolerance | 1e-06 |\n" "| maxFuncEvals | 10000 |\n" @@ -226,7 +226,7 @@ def setup_class(self): ("crossoverProbability", 0.8), ("strategy", Strategies.RandomWithPerVectorDither), ("targetValue", 1), - ("numGenerations", 500) + ("numGenerations", 500), ]) def test_de_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of DE class.""" @@ -242,7 +242,7 @@ def test_de_property_values(self, control_property: str, value: Any) -> None: ("crossoverProbability", 0.4), ("strategy", Strategies.BestWithJitter), ("targetValue", 2.0), - ("numGenerations", 50) + ("numGenerations", 50), ]) def test_de_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of DE class.""" @@ -251,7 +251,7 @@ def test_de_property_setters(self, control_property: str, value: Any) -> None: @pytest.mark.parametrize("value, msg", [ (0, "Input should be greater than 0"), - (2, "Input should be less than 1") + (2, "Input should be less than 1"), ]) def test_de_crossoverProbability_error(self, value: int, msg: str) -> None: """Tests the crossoverProbability setter error in DE class.""" @@ -265,7 +265,7 @@ def test_de_crossoverProbability_error(self, value: int, msg: str) -> None: ("numGenerations", -500), ("numGenerations", 0), ("populationSize", 0), - ("populationSize", -1) + ("populationSize", -1), ]) def test_de_targetValue_numGenerations_populationSize_error(self, control_property: str, @@ -332,7 +332,7 @@ def setup_class(self): ("nLive", 150), ("nMCMC", 0), ("propScale", 0.1), - ("nsTolerance", 0.1) + ("nsTolerance", 0.1), ]) def test_ns_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of NS class.""" @@ -346,7 +346,7 @@ def test_ns_property_values(self, control_property: str, value: Any) -> None: ("nLive", 1500), ("nMCMC", 1), ("propScale", 0.5), - ("nsTolerance", 0.8) + ("nsTolerance", 0.8), ]) def test_ns_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of NS class.""" @@ -356,7 +356,7 @@ def test_ns_property_setters(self, control_property: str, value: Any) -> None: @pytest.mark.parametrize("control_property, value, bound", [ ("nMCMC", -0.6, 0), ("nsTolerance", -500, 0), - ("nLive", -500, 1) + ("nLive", -500, 1), ]) def test_ns_setter_error(self, control_property: str, value: Union[int, float], bound: int) -> None: """Tests the nMCMC, nsTolerance, nLive setter error in NS class.""" @@ -366,7 +366,7 @@ def test_ns_setter_error(self, control_property: str, value: Union[int, float], @pytest.mark.parametrize("value, msg", [ (0, "Input should be greater than 0"), - (2, "Input should be less than 1") + (2, "Input should be less than 1"), ]) def test_ns_propScale_error(self, value: int, msg: str) -> None: """Tests the propScale error in NS class.""" @@ -402,7 +402,7 @@ def test_control_class_ns_repr(self) -> None: "| parallel | single |\n" "| calcSldDuringFit | False |\n" "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" + "| display | iter |\n" "| nLive | 150 |\n" "| nMCMC | 0.0 |\n" "| propScale | 0.1 |\n" @@ -430,7 +430,7 @@ def setup_class(self): ("nChains", 10), ("jumpProbability", 0.5), ("pUnitGamma", 0.2), - ("boundHandling", BoundHandling.Fold) + ("boundHandling", BoundHandling.Fold), ]) def test_dream_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Dream class.""" @@ -445,7 +445,7 @@ def test_dream_property_values(self, control_property: str, value: Any) -> None: ("nChains", 1000), ("jumpProbability", 0.7), ("pUnitGamma", 0.3), - ("boundHandling", BoundHandling.Reflect) + ("boundHandling", BoundHandling.Reflect), ]) def test_dream_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters in Dream class.""" @@ -456,7 +456,7 @@ def test_dream_property_setters(self, control_property: str, value: Any) -> Non ("jumpProbability", 0, "Input should be greater than 0"), ("jumpProbability", 2, "Input should be less than 1"), ("pUnitGamma", -5, "Input should be greater than 0"), - ("pUnitGamma", 20, "Input should be less than 1") + ("pUnitGamma", 20, "Input should be less than 1"), ]) def test_dream_jumpProbability_pUnitGamma_error(self, control_property: str, value: int, msg: str) -> None: """Tests the jumpProbability and pUnitGamma setter errors in Dream class.""" @@ -506,7 +506,7 @@ def test_control_class_dream_repr(self) -> None: "| parallel | single |\n" "| calcSldDuringFit | False |\n" "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" + "| display | iter |\n" "| nSamples | 50000 |\n" "| nChains | 10 |\n" "| jumpProbability | 0.5 |\n" @@ -523,7 +523,7 @@ def test_control_class_dream_repr(self) -> None: ("simplex", Simplex), ("de", DE), ("ns", NS), - ("dream", Dream) + ("dream", Dream), ]) def test_set_controls(procedure: Procedures, expected_model: Union[Calculate, Simplex, DE, NS, Dream]) -> None: """We should return the correct model given the value of procedure.""" @@ -549,7 +549,7 @@ def test_set_controls_invalid_procedure() -> None: ("simplex", Simplex), ("de", DE), ("ns", NS), - ("dream", Dream) + ("dream", Dream), ]) def test_set_controls_extra_fields(procedure: Procedures, expected_model: Union[Calculate, Simplex, DE, NS, Dream])\ -> None: diff --git a/tests/test_custom_errors.py b/tests/test_custom_errors.py index 3d7a5f40..c08f56ec 100644 --- a/tests/test_custom_errors.py +++ b/tests/test_custom_errors.py @@ -24,7 +24,7 @@ def TestModel(): "input_value='string', input_type=str]\nstr_field\n This is another custom error message [type=string_type, " "input_value=5, input_type=int]"), ]) -def test_custom_pydantic_validation_error(TestModel, custom_errors: dict[str, str], expected_error_message: str +def test_custom_pydantic_validation_error(TestModel, custom_errors: dict[str, str], expected_error_message: str, ) -> None: """When we call custom_pydantic_validation_error with custom errors, we should return an error list containing PydanticCustomErrors, otherwise we return the original set of errors. diff --git a/tests/test_events.py b/tests/test_events.py index 6308438d..3f2ac881 100644 --- a/tests/test_events.py +++ b/tests/test_events.py @@ -1,11 +1,11 @@ -import unittest.mock as mock +from unittest import mock import pytest import RAT.events -@pytest.mark.skip +@pytest.mark.skip() def test_event_register() -> None: first_callback = mock.Mock() second_callback = mock.Mock() @@ -29,7 +29,7 @@ def test_event_register() -> None: assert RAT.events.get_event_callback(RAT.events.EventTypes.Message) == [] -@pytest.mark.skip +@pytest.mark.skip() def test_event_notify() -> None: first_callback = mock.Mock() second_callback = mock.Mock() @@ -37,7 +37,7 @@ def test_event_notify() -> None: RAT.events.register(RAT.events.EventTypes.Message, first_callback) RAT.events.register(RAT.events.EventTypes.Plot, second_callback) RAT.events.register(RAT.events.EventTypes.Progress, third_callback) - + RAT.events.notify(RAT.events.EventTypes.Message, "Hello World") first_callback.assert_called_once_with("Hello World") second_callback.assert_not_called() @@ -52,7 +52,7 @@ def test_event_notify() -> None: first_callback.assert_called_once() second_callback.assert_called_once() third_callback.assert_called_once_with(data) - + RAT.events.clear() RAT.events.notify(RAT.events.EventTypes.Message, "Hello World") RAT.events.notify(RAT.events.EventTypes.Plot, data) diff --git a/tests/test_inputs.py b/tests/test_inputs.py index dd3ea218..d003a8ac 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -1,8 +1,8 @@ """Test the inputs module.""" import pathlib -import unittest.mock as mock from itertools import chain +from unittest import mock import numpy as np import pytest @@ -27,14 +27,14 @@ @pytest.fixture def standard_layers_project(): """Add parameters to the default project for a non polarised calculation.""" - test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]) + test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]), ) test_project.parameters.append(name="Test Thickness") test_project.parameters.append(name="Test SLD") test_project.parameters.append(name="Test Roughness") test_project.custom_files.append(name="Test Custom File", filename="python_test.py", function_name="dummy_function", language="python") - test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness" + test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness", ) test_project.contrasts.append(name="Test Contrast", data="Test Data", background="Background 1", bulk_in="SLD Air", bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", @@ -46,13 +46,13 @@ def standard_layers_project(): def domains_project(): """Add parameters to the default project for a domains calculation.""" test_project = RAT.Project(calculation=Calculations.Domains, - data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]) + data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]), ) test_project.parameters.append(name="Test Thickness") test_project.parameters.append(name="Test SLD") test_project.parameters.append(name="Test Roughness") test_project.custom_files.append(name="Test Custom File", filename="matlab_test.m", language="matlab") - test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness" + test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness", ) test_project.domain_contrasts.append(name="up", model=["Test Layer"]) test_project.domain_contrasts.append(name="down", model=["Test Layer"]) @@ -573,7 +573,8 @@ def test_check_indices(test_problem, request) -> None: ]) def test_check_indices_error(test_problem, index_list, bad_value, request) -> None: """The check_indices routine should raise an IndexError if a contrast list contains an index that is out of the - range of the corresponding parameter list in a ProblemDefinition object.""" + range of the corresponding parameter list in a ProblemDefinition object. + """ param_list = {"contrastBulkIns": "bulkIn", "contrastBulkOuts": "bulkOut", "contrastScalefactors": "scalefactors", @@ -651,7 +652,6 @@ def check_problem_equal(actual_problem, expected_problem) -> None: ["NaN" if np.isnan(el) else el for el in expected_problem.contrastCustomFiles] ) - return None def check_cells_equal(actual_cells, expected_cells) -> None: @@ -671,7 +671,6 @@ def check_cells_equal(actual_cells, expected_cells) -> None: field = f"f{index}" assert getattr(actual_cells, field) == getattr(expected_cells, field) - return None def check_controls_equal(actual_controls, expected_controls) -> None: @@ -691,4 +690,3 @@ def check_controls_equal(actual_controls, expected_controls) -> None: for field in checks_fields: assert (getattr(actual_controls.checks, field) == getattr(expected_controls.checks, field)).all() - return None diff --git a/tests/test_models.py b/tests/test_models.py index 9e8ffbfe..328f36ed 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -160,7 +160,7 @@ def test_two_values_in_simulation_range(input_range: list[float]) -> None: @pytest.mark.parametrize("field", [ "data_range", - "simulation_range" + "simulation_range", ]) def test_min_max_in_range(field: str) -> None: """If the maximum value of the "data_range" or "simulation_range" fields of the "Data" model is greater than the diff --git a/tests/test_outputs.py b/tests/test_outputs.py index c96f845c..27b6cf21 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -1,5 +1,4 @@ -""" -Test the outputs module using the example calculation from "DSPC_standard_layers.py". +"""Test the outputs module using the example calculation from "DSPC_standard_layers.py". We use the example for both a reflectivity calculation, and Bayesian analysis using the Dream algorithm. """ diff --git a/tests/test_plotting.py b/tests/test_plotting.py index d5b30b3c..029a297a 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -14,13 +14,12 @@ def data() -> PlotEventData: + """Creates the fixture for the tests. """ - Creates the fixture for the tests. - """ - data_path = os.path.join(TEST_DIR_PATH, "plotting_data.pickle") + data_path = os.path.join(TEST_DIR_PATH, "plotting_data.pickle") with open(data_path, "rb") as f: loaded_data = pickle.load(f) - + data = PlotEventData() data.modelType = loaded_data["modelType"] data.dataPresent = loaded_data["dataPresent"] @@ -35,8 +34,7 @@ def data() -> PlotEventData: @pytest.fixture def fig() -> Figure: - """ - Creates the fixture for the tests. + """Creates the fixture for the tests. """ plt.close("all") figure = Figure(1, 3) @@ -45,8 +43,7 @@ def fig() -> Figure: def test_figure_axis_formating(fig: Figure) -> None: - """ - Tests the axis formating of the figure. + """Tests the axis formating of the figure. """ ref_plot = fig._ax[0] sld_plot = fig._ax[1] @@ -68,8 +65,7 @@ def test_figure_axis_formating(fig: Figure) -> None: def test_figure_color_formating(fig: Figure) -> None: - """ - Tests the color formating of the figure. + """Tests the color formating of the figure. """ ref_plot = fig._ax[0] sld_plot = fig._ax[1] @@ -92,8 +88,7 @@ def test_figure_color_formating(fig: Figure) -> None: def test_eventhandlers_linked_to_figure(fig: Figure) -> None: - """ - Tests whether the eventhandlers for close_event + """Tests whether the eventhandlers for close_event and key_press_event in the figure are linked to the class methods. """ @@ -119,8 +114,7 @@ class methods. def test_eventhandler_variable_update(fig: Figure) -> None: - """ - Tests whether the eventhandlers for close_event + """Tests whether the eventhandlers for close_event and key_press_event update variables that stop while loop in wait_for_close. """ @@ -134,10 +128,9 @@ def test_eventhandler_variable_update(fig: Figure) -> None: assert fig._close_clicked -@patch("RAT.utils.plotting.plt.waitforbuttonpress") +@patch("RAT.utils.plotting.plt.waitforbuttonpress") def test_wait_for_close(mock: MagicMock, fig: Figure) -> None: - """ - Tests the _wait_for_close method stops the + """Tests the _wait_for_close method stops the while loop when _esc_pressed is True. """ def mock_wait_for_button_press(timeout): @@ -149,10 +142,9 @@ def mock_wait_for_button_press(timeout): assert fig._esc_pressed -@patch("RAT.utils.plotting.makeSLDProfileXY") +@patch("RAT.utils.plotting.makeSLDProfileXY") def test_sld_profile_function_call(mock: MagicMock) -> None: - """ - Tests the makeSLDProfileXY function called with + """Tests the makeSLDProfileXY function called with correct args. """ plot_ref_sld_helper(data()) diff --git a/tests/test_project.py b/tests/test_project.py index 8f19d0e2..7fe4a5cd 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -101,7 +101,7 @@ def default_project_repr(): @pytest.fixture def test_project_script(): return ( - "# THIS FILE IS GENERATED FROM RAT VIA THE \"WRITE_SCRIPT\" ROUTINE. IT IS NOT PART OF THE RAT CODE.\n\n" + '# THIS FILE IS GENERATED FROM RAT VIA THE "WRITE_SCRIPT" ROUTINE. IT IS NOT PART OF THE RAT CODE.\n\n' "import RAT\nfrom RAT.models import *\nfrom numpy import array, inf\n\n" "problem = RAT.Project(\n name='', calculation='non polarised', model='standard layers', geometry='air/substrate', absorption=False,\n" " parameters=RAT.ClassList([ProtectedParameter(name='Substrate Roughness', min=1.0, value=3.0, max=5.0, fit=True, prior_type='uniform', mu=0.0, sigma=inf)," @@ -405,7 +405,7 @@ def test_check_protected_parameters(delete_operation) -> None: """If we try to remove a protected parameter, we should raise an error.""" project = RAT.Project() - with pytest.raises(pydantic.ValidationError, match="1 validation error for Project\n Value error, Can\'t delete" + with pytest.raises(pydantic.ValidationError, match="1 validation error for Project\n Value error, Can't delete" " the protected parameters: Substrate Roughness"): eval(delete_operation) @@ -606,7 +606,7 @@ def test_get_all_names(test_project) -> None: "data": ["Simulation"], "layers": ["Test Layer"], "domain_contrasts": [], - "contrasts": ["Test Contrast"] + "contrasts": ["Test Contrast"], } @@ -618,7 +618,7 @@ def test_get_all_protected_parameters(test_project) -> None: "scalefactors": [], "domain_ratios": [], "background_parameters": [], - "resolution_parameters": [] + "resolution_parameters": [], } @@ -688,7 +688,7 @@ def test_get_contrast_model_field(input_calc: Calculations, input_model: LayerMo @pytest.mark.parametrize("input_filename", [ "test_script.py", - "test_script" + "test_script", ]) def test_write_script(test_project, temp_dir, test_project_script, input_filename: str) -> None: """Test the script we write to regenerate the project is created and runs as expected.""" @@ -716,7 +716,7 @@ def test_write_script(test_project, temp_dir, test_project_script, input_filenam ".txt" ".f90" ".m" - ".pyc" + ".pyc", ]) def test_write_script_wrong_extension(test_project, extension: str) -> None: """If we try to write the script to anything other than a ".py" file, we raise a ValueError.""" diff --git a/tests/test_run.py b/tests/test_run.py index 885a6431..1009b696 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -1,10 +1,9 @@ -""" -Test the run module using the example calculation from "DSPC_standard_layers.py". +"""Test the run module using the example calculation from "DSPC_standard_layers.py". We use the example for both a reflectivity calculation, and Bayesian analysis using the Dream algorithm. """ -import unittest.mock as mock +from unittest import mock import numpy as np import pytest diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index a618b916..32502bb0 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -1,5 +1,5 @@ import pathlib -import unittest.mock as mock +from unittest import mock import pytest @@ -13,7 +13,7 @@ def test_matlab_wrapper() -> None: mocked_matlab_module = mock.MagicMock() mocked_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_engine - + # mocked_matlab_module.engine = mock.MagicMock() with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, @@ -24,7 +24,7 @@ def test_matlab_wrapper() -> None: assert pathlib.Path(mocked_engine.cd.call_args[0][0]).samefile(".") handle = wrapper.getHandle() - + mocked_engine.demo.return_value = ([2], 5) result = handle([1], [2], [3], 0) assert result == ([2], 5) @@ -36,7 +36,7 @@ def test_matlab_wrapper() -> None: assert result == ([3, 1], 7) assert wrapper.engine.demo.call_args[0] == ([4], [5], [6], 2, 2) assert mocked_engine.demo.call_count == 2 - + def test_dylib_wrapper() -> None: mocked_engine = mock.MagicMock() diff --git a/tests/utils.py b/tests/utils.py index 579e0135..294951fe 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,4 +1,3 @@ -from typing import Any import numpy as np @@ -7,11 +6,12 @@ class InputAttributes: """Set input arguments as class attributes.""" + def __init__(self, **kwargs) -> None: for key, value in kwargs.items(): setattr(self, key, value) - def __eq__(self, other: Any): + def __eq__(self, other: object): if isinstance(other, InputAttributes): return self.__dict__ == other.__dict__ return False @@ -19,12 +19,11 @@ def __eq__(self, other: Any): class SubInputAttributes(InputAttributes): """Trivial subclass of InputAttributes.""" - pass + def dummy_function() -> None: """Trivial function for function handle tests.""" - pass def check_results_equal(actual_results, expected_results) -> None: @@ -33,7 +32,6 @@ def check_results_equal(actual_results, expected_results) -> None: We focus here on the fields common to both results objects, and also check the equality of the subclasses "CalculationResults" and "ContrastParams". """ - list_fields = ["reflectivity", "simulation", "shiftedData"] double_list_fields = ["layerSlds", "sldProfiles", "resampledLayers"] contrast_param_fields = ["backgroundParams", "scalefactors", "bulkIn", "bulkOut", "resolutionParams", "subRoughs", @@ -72,7 +70,6 @@ def check_results_equal(actual_results, expected_results) -> None: if isinstance(actual_results, RAT.outputs.BayesResults) and isinstance(expected_results, RAT.outputs.BayesResults): check_bayes_fields_equal(actual_results, expected_results) - return None def check_bayes_fields_equal(actual_results, expected_results) -> None: @@ -80,7 +77,6 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: We focus here on the fields and subclasses specific to the Bayesian optimisation. """ - # The BayesResults object consists of a number of subclasses, each containing fields of differing formats. subclasses = ["predictionIntervals", "confidenceIntervals", "dreamParams", "dreamOutput", "nestedSamplerOutput"] @@ -90,21 +86,21 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: "pUnitGamma", "nCR", "delta", "steps", "zeta", "outlier", "adaptPCR", "thinning", "epsilon", "ABC", "IO", "storeOutput"], "dreamOutput": ["runtime", "iteration", "modelOutput"], - "nestedSamplerOutput": ["logZ"] + "nestedSamplerOutput": ["logZ"], } list_fields = {"predictionIntervals": ["reflectivity", "reflectivityXData"], "confidenceIntervals": [], "dreamParams": [], "dreamOutput": [], - "nestedSamplerOutput": [] + "nestedSamplerOutput": [], } double_list_fields = {"predictionIntervals": ["sld", "sldXData"], "confidenceIntervals": [], "dreamParams": [], "dreamOutput": [], - "nestedSamplerOutput": [] + "nestedSamplerOutput": [], } array_fields = {"predictionIntervals": ["sampleChi"], @@ -144,4 +140,3 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: assert (actual_results.chain == expected_results.chain).all() - return None From a0d22d4cc5af3cb7f8b14242c6e1303ca3b0b8e5 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 26 Jun 2024 10:44:40 +0100 Subject: [PATCH 06/11] Applies ruff formatter --- RAT/classlist.py | 45 +- RAT/controls.py | 15 +- RAT/examples/absorption/absorption.py | 79 +- .../absorption/volume_thiol_bilayer.py | 78 +- RAT/examples/domains/alloy_domains.py | 1 - RAT/examples/domains/domains_XY_model.py | 5 +- RAT/examples/domains/domains_custom_XY.py | 50 +- RAT/examples/domains/domains_custom_layers.py | 23 +- .../domains/domains_standard_layers.py | 45 +- RAT/examples/languages/custom_bilayer.py | 25 +- .../languages/run_custom_file_languages.py | 1 + RAT/examples/languages/setup_problem.py | 82 +- RAT/examples/non_polarised/DSPC_custom_XY.py | 61 +- .../non_polarised/DSPC_custom_layers.py | 62 +- .../non_polarised/DSPC_standard_layers.py | 363 +- RAT/examples/non_polarised/custom_XY_DSPC.py | 26 +- .../non_polarised/custom_bilayer_DSPC.py | 22 +- RAT/inputs.py | 189 +- RAT/models.py | 60 +- RAT/outputs.py | 16 +- RAT/project.py | 394 +- RAT/run.py | 28 +- RAT/utils/custom_errors.py | 8 +- RAT/utils/plotting.py | 102 +- RAT/wrappers.py | 28 +- pyproject.toml | 3 +- setup.py | 49 +- tests/conftest.py | 6206 ++++++++++++----- tests/test_classlist.py | 751 +- tests/test_controls.py | 581 +- tests/test_custom_errors.py | 35 +- tests/test_inputs.py | 517 +- tests/test_models.py | 274 +- tests/test_outputs.py | 11 +- tests/test_plotting.py | 35 +- tests/test_project.py | 1371 ++-- tests/test_run.py | 575 +- tests/test_wrappers.py | 8 +- tests/utils.py | 118 +- 39 files changed, 8582 insertions(+), 3760 deletions(-) diff --git a/RAT/classlist.py b/RAT/classlist.py index cb136816..325cf250 100644 --- a/RAT/classlist.py +++ b/RAT/classlist.py @@ -1,5 +1,4 @@ -"""The classlist module. Contains the ClassList class, which defines a list containing instances of a particular class. -""" +"""The classlist module. Contains the ClassList class, which defines a list containing instances of a particular class.""" import collections import contextlib @@ -135,8 +134,12 @@ def append(self, obj: object = None, **kwargs) -> None: """ if obj and kwargs: - warnings.warn("ClassList.append() called with both an object and keyword arguments. " - "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) + warnings.warn( + "ClassList.append() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", + SyntaxWarning, + stacklevel=2, + ) if obj: if not hasattr(self, "_class_handle"): self._class_handle = type(obj) @@ -145,8 +148,10 @@ def append(self, obj: object = None, **kwargs) -> None: self.data.append(obj) else: if not hasattr(self, "_class_handle"): - raise TypeError("ClassList.append() called with keyword arguments for a ClassList without a class " - "defined. Call ClassList.append() with an object to define the class.") + raise TypeError( + "ClassList.append() called with keyword arguments for a ClassList without a class " + "defined. Call ClassList.append() with an object to define the class.", + ) self._validate_name_field(kwargs) self.data.append(self._class_handle(**kwargs)) @@ -176,8 +181,12 @@ def insert(self, index: int, obj: object = None, **kwargs) -> None: """ if obj and kwargs: - warnings.warn("ClassList.insert() called with both an object and keyword arguments. " - "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) + warnings.warn( + "ClassList.insert() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", + SyntaxWarning, + stacklevel=2, + ) if obj: if not hasattr(self, "_class_handle"): self._class_handle = type(obj) @@ -186,8 +195,10 @@ def insert(self, index: int, obj: object = None, **kwargs) -> None: self.data.insert(index, obj) else: if not hasattr(self, "_class_handle"): - raise TypeError("ClassList.insert() called with keyword arguments for a ClassList without a class " - "defined. Call ClassList.insert() with an object to define the class.") + raise TypeError( + "ClassList.insert() called with keyword arguments for a ClassList without a class " + "defined. Call ClassList.insert() with an object to define the class.", + ) self._validate_name_field(kwargs) self.data.insert(index, self._class_handle(**kwargs)) @@ -252,8 +263,12 @@ def get_all_matches(self, value: Any) -> list[tuple]: A list of (index, field) tuples matching the given value. """ - return [(index, field) for index, element in enumerate(self.data) for field in vars(element) - if getattr(element, field) == value] + return [ + (index, field) + for index, element in enumerate(self.data) + for field in vars(element) + if getattr(element, field) == value + ] def _validate_name_field(self, input_args: dict[str, Any]) -> None: """Raise a ValueError if the name_field attribute is passed as an object parameter, and its value is already @@ -273,8 +288,10 @@ def _validate_name_field(self, input_args: dict[str, Any]) -> None: names = self.get_names() with contextlib.suppress(KeyError): if input_args[self.name_field] in names: - raise ValueError(f"Input arguments contain the {self.name_field} '{input_args[self.name_field]}', " - f"which is already specified in the ClassList") + raise ValueError( + f"Input arguments contain the {self.name_field} '{input_args[self.name_field]}', " + f"which is already specified in the ClassList", + ) def _check_unique_name_fields(self, input_list: Iterable[object]) -> None: """Raise a ValueError if any value of the name_field attribute is used more than once in a list of class diff --git a/RAT/controls.py b/RAT/controls.py index 707b4396..093687d5 100644 --- a/RAT/controls.py +++ b/RAT/controls.py @@ -117,8 +117,10 @@ class Dream(Calculate): adaptPCR: bool = False -def set_controls(procedure: Procedures = Procedures.Calculate, **properties)\ - -> Union[Calculate, Simplex, DE, NS, Dream]: +def set_controls( + procedure: Procedures = Procedures.Calculate, + **properties, +) -> Union[Calculate, Simplex, DE, NS, Dream]: """Returns the appropriate controls model given the specified procedure.""" controls = { Procedures.Calculate: Calculate, @@ -135,10 +137,11 @@ def set_controls(procedure: Procedures = Procedures.Calculate, **properties)\ allowed_values = f'{", ".join([repr(member.value) for member in members[:-1]])} or {members[-1].value!r}' raise ValueError(f"The controls procedure must be one of: {allowed_values}") from None except ValidationError as exc: - custom_error_msgs = {"extra_forbidden": f'Extra inputs are not permitted. The fields for the {procedure}' - f' controls procedure are:\n ' - f'{", ".join(controls[procedure].model_fields.keys())}\n', - } + custom_error_msgs = { + "extra_forbidden": f'Extra inputs are not permitted. The fields for the {procedure}' + f' controls procedure are:\n ' + f'{", ".join(controls[procedure].model_fields.keys())}\n', + } custom_error_list = custom_pydantic_validation_error(exc.errors(), custom_error_msgs) raise ValidationError.from_exception_data(exc.title, custom_error_list) from None diff --git a/RAT/examples/absorption/absorption.py b/RAT/examples/absorption/absorption.py index 61c4007c..35f9a057 100644 --- a/RAT/examples/absorption/absorption.py +++ b/RAT/examples/absorption/absorption.py @@ -8,8 +8,13 @@ import RAT import RAT.utils.plotting -problem = RAT.Project(name="Absorption example", calculation="non polarised", model="custom layers", - geometry="substrate/liquid", absorption=True) +problem = RAT.Project( + name="Absorption example", + calculation="non polarised", + model="custom layers", + geometry="substrate/liquid", + absorption=True, +) # Add the required parameters (substrate roughness is already there by default) problem.parameters.append(name="Alloy Thickness", min=100.0, value=135.6, max=200.0, fit=True) @@ -80,25 +85,61 @@ problem.data.append(name="H2O_up", data=data_4) # Add the custom file -problem.custom_files.append(name="DPPC absorption", filename="volume_thiol_bilayer.py", language="python", - path=pathlib.Path(__file__).parent.resolve()) +problem.custom_files.append( + name="DPPC absorption", + filename="volume_thiol_bilayer.py", + language="python", + path=pathlib.Path(__file__).parent.resolve(), +) # Finally add the contrasts -problem.contrasts.append(name="D2O Down", data="D2O_dn", background="Background 1", bulk_in="Silicon", - bulk_out="D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", resample=True, - model=["DPPC absorption"]) - -problem.contrasts.append(name="D2O Up", data="D2O_up", background="Background 2", bulk_in="Silicon", - bulk_out="D2O", scalefactor="Scalefactor 2", resolution="Resolution 1", resample=True, - model=["DPPC absorption"]) - -problem.contrasts.append(name="H2O Down", data="H2O_dn", background="Background 3", bulk_in="Silicon", - bulk_out="H2O", scalefactor="Scalefactor 3", resolution="Resolution 1", resample=True, - model=["DPPC absorption"]) - -problem.contrasts.append(name="H2O Up", data="H2O_up", background="Background 4", bulk_in="Silicon", - bulk_out="H2O", scalefactor="Scalefactor 4", resolution="Resolution 1", resample=True, - model=["DPPC absorption"]) +problem.contrasts.append( + name="D2O Down", + data="D2O_dn", + background="Background 1", + bulk_in="Silicon", + bulk_out="D2O", + scalefactor="Scalefactor 1", + resolution="Resolution 1", + resample=True, + model=["DPPC absorption"], +) + +problem.contrasts.append( + name="D2O Up", + data="D2O_up", + background="Background 2", + bulk_in="Silicon", + bulk_out="D2O", + scalefactor="Scalefactor 2", + resolution="Resolution 1", + resample=True, + model=["DPPC absorption"], +) + +problem.contrasts.append( + name="H2O Down", + data="H2O_dn", + background="Background 3", + bulk_in="Silicon", + bulk_out="H2O", + scalefactor="Scalefactor 3", + resolution="Resolution 1", + resample=True, + model=["DPPC absorption"], +) + +problem.contrasts.append( + name="H2O Up", + data="H2O_up", + background="Background 4", + bulk_in="Silicon", + bulk_out="H2O", + scalefactor="Scalefactor 4", + resolution="Resolution 1", + resample=True, + model=["DPPC absorption"], +) # Now make a controls block controls = RAT.set_controls(parallel="contrasts", resampleParams=[0.9, 150.0]) diff --git a/RAT/examples/absorption/volume_thiol_bilayer.py b/RAT/examples/absorption/volume_thiol_bilayer.py index b6ba6830..f5ac1bbc 100644 --- a/RAT/examples/absorption/volume_thiol_bilayer.py +++ b/RAT/examples/absorption/volume_thiol_bilayer.py @@ -47,22 +47,22 @@ def volume_thiol_bilayer(params, bulk_in, bulk_out, contrast): # Neutron b's.. # define all the neutron b's. - bc = 0.6646e-4 # Carbon - bo = 0.5843e-4 # Oxygen + bc = 0.6646e-4 # Carbon + bo = 0.5843e-4 # Oxygen bh = -0.3739e-4 # Hydrogen - bp = 0.513e-4 # Phosphorus - bn = 0.936e-4 # Nitrogen + bp = 0.513e-4 # Phosphorus + bn = 0.936e-4 # Nitrogen # Work out the total scattering length in each fragment # Define scattering lengths # Hydrogenated version - COO = (2*bo) + (bc) - GLYC = (3*bc) + (5*bh) - CH3 = (1*bc) + (3*bh) - PO4 = (1*bp) + (4*bo) - CH2 = (1*bc) + (2*bh) - CH = (1*bc) + (1*bh) - CHOL = (5*bc) + (12*bh) + (1*bn) + COO = (2 * bo) + (bc) + GLYC = (3 * bc) + (5 * bh) + CH3 = (1 * bc) + (3 * bh) + PO4 = (1 * bp) + (4 * bo) + CH2 = (1 * bc) + (2 * bh) + CH = (1 * bc) + (1 * bh) + CHOL = (5 * bc) + (12 * bh) + (1 * bn) # And also volumes vCH3 = 52.7 # CH3 volume in the paper appears to be for 2 * CH3's @@ -73,54 +73,54 @@ def volume_thiol_bilayer(params, bulk_in, bulk_out, contrast): vCHOL = 120.4 vCHCH = 42.14 - vHead = vCHOL + vPO4 + vGLYC + 2*vCOO - vTail = (28*vCH2) + (1*vCHCH) + (2*vCH3) # Tail volume + vHead = vCHOL + vPO4 + vGLYC + 2 * vCOO + vTail = (28 * vCH2) + (1 * vCHCH) + (2 * vCH3) # Tail volume # Calculate sum_b's for other fragments - sumbHead = CHOL + PO4 + GLYC + 2*COO - sumbTail = (28*CH2) + (2*CH) + 2*CH3 + sumbHead = CHOL + PO4 + GLYC + 2 * COO + sumbTail = (28 * CH2) + (2 * CH) + 2 * CH3 # Calculate SLDs and Thickness - sldHead = sumbHead/vHead - thickHead = vHead/thiolAPM + sldHead = sumbHead / vHead + thickHead = vHead / thiolAPM - sldTail = sumbTail/vTail - thickTail = vTail/thiolAPM + sldTail = sumbTail / vTail + thickTail = vTail / thiolAPM # Correct head SLD based on hydration - thiolHeadHydr = thiolHeadHydr/100 - sldHead = (sldHead * (1 - thiolHeadHydr) + (thiolHeadHydr * bulk_out[contrast])) + thiolHeadHydr = thiolHeadHydr / 100 + sldHead = sldHead * (1 - thiolHeadHydr) + (thiolHeadHydr * bulk_out[contrast]) # Now correct both the SLDs for the coverage parameter - sldTail = (thiolCoverage*sldTail) + ((1-thiolCoverage) * bulk_out[contrast]) - sldHead = (thiolCoverage*sldHead) + ((1-thiolCoverage) * bulk_out[contrast]) + sldTail = (thiolCoverage * sldTail) + ((1 - thiolCoverage) * bulk_out[contrast]) + sldHead = (thiolCoverage * sldHead) + ((1 - thiolCoverage) * bulk_out[contrast]) SAMTAILS = [thickTail, sldTail, 0, goldRough] SAMHEAD = [thickHead, sldHead, 0, goldRough] # Now do the same for the bilayer - vHead = vCHOL + vPO4 + vGLYC + 2*vCOO - vTail = (28*vCH2) # Tail volume - vMe = (2*vCH3) + vHead = vCHOL + vPO4 + vGLYC + 2 * vCOO + vTail = 28 * vCH2 # Tail volume + vMe = 2 * vCH3 - sumbHead = CHOL + PO4 + GLYC + 2*COO - sumbTail = (28*CH2) - sumbMe = 2*CH3 + sumbHead = CHOL + PO4 + GLYC + 2 * COO + sumbTail = 28 * CH2 + sumbMe = 2 * CH3 - sldHead = sumbHead/vHead - thickHead = vHead/bilayerAPM + sldHead = sumbHead / vHead + thickHead = vHead / bilayerAPM bilHeadHydr = bilHeadHydr / 100 - sldHead = (sldHead * (1 - bilHeadHydr) + (bilHeadHydr * bulk_out[contrast])) + sldHead = sldHead * (1 - bilHeadHydr) + (bilHeadHydr * bulk_out[contrast]) - sldTail = sumbTail/vTail - thickTail = vTail/bilayerAPM + sldTail = sumbTail / vTail + thickTail = vTail / bilayerAPM - sldMe = sumbMe/vMe - thickMe = vMe/bilayerAPM + sldMe = sumbMe / vMe + thickMe = vMe / bilayerAPM - sldTail = (bilayerCoverage * sldTail) + ((1-bilayerCoverage) * bulk_out[contrast]) - sldHead = (bilayerCoverage * sldHead) + ((1-bilayerCoverage) * bulk_out[contrast]) - sldMe = (bilayerCoverage * sldMe) + ((1-bilayerCoverage) * bulk_out[contrast]) + sldTail = (bilayerCoverage * sldTail) + ((1 - bilayerCoverage) * bulk_out[contrast]) + sldHead = (bilayerCoverage * sldHead) + ((1 - bilayerCoverage) * bulk_out[contrast]) + sldMe = (bilayerCoverage * sldMe) + ((1 - bilayerCoverage) * bulk_out[contrast]) BILTAILS = [thickTail, sldTail, 0, bilayerRough] BILHEAD = [thickHead, sldHead, 0, bilayerRough] diff --git a/RAT/examples/domains/alloy_domains.py b/RAT/examples/domains/alloy_domains.py index 40db8338..73aec375 100644 --- a/RAT/examples/domains/alloy_domains.py +++ b/RAT/examples/domains/alloy_domains.py @@ -1,4 +1,3 @@ - def alloy_domains(params, bulkIn, bulkOut, contrast, domain): """Simple custom model for testing incoherent summing. Simple two layer of permalloy / gold, with up/down domains. diff --git a/RAT/examples/domains/domains_XY_model.py b/RAT/examples/domains/domains_XY_model.py index 9e6c59c8..cde8c299 100644 --- a/RAT/examples/domains/domains_XY_model.py +++ b/RAT/examples/domains/domains_XY_model.py @@ -4,7 +4,6 @@ def domains_XY_model(params, bulk_in, bulk_out, contrast, domain): - # Split up the parameters for convenience subRough = params[0] oxideThick = params[1] @@ -60,8 +59,8 @@ def makeLayer(z, prevLaySurf, thickness, height, Sigma_L, Sigma_R): right = prevLaySurf + thickness # Make our heaviside - a = (z-left) / ((2**0.5) * Sigma_L) - b = (z-right) / ((2**0.5) * Sigma_R) + a = (z - left) / ((2**0.5) * Sigma_L) + b = (z - right) / ((2**0.5) * Sigma_R) erf_a = np.array([math.erf(value) for value in a]) erf_b = np.array([math.erf(value) for value in b]) diff --git a/RAT/examples/domains/domains_custom_XY.py b/RAT/examples/domains/domains_custom_XY.py index 80ff4f9f..75665898 100644 --- a/RAT/examples/domains/domains_custom_XY.py +++ b/RAT/examples/domains/domains_custom_XY.py @@ -20,21 +20,49 @@ problem.bulk_out.append(name="SLD H2O", min=-0.6e-6, value=-0.56e-6, max=-0.5e-6) # Add the custom file -problem.custom_files.append(name="Domain Layer", filename="domains_XY_model.py", language="python", - path=pathlib.Path(__file__).parent.resolve()) +problem.custom_files.append( + name="Domain Layer", + filename="domains_XY_model.py", + language="python", + path=pathlib.Path(__file__).parent.resolve(), +) # Make contrasts -problem.contrasts.append(name="D2O", background="Background 1", resolution="Resolution 1", scalefactor="Scalefactor 1", - bulk_in="Silicon", bulk_out="SLD D2O", domain_ratio="Domain Ratio 1", data="Simulation", - model=["Domain Layer"]) +problem.contrasts.append( + name="D2O", + background="Background 1", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + bulk_in="Silicon", + bulk_out="SLD D2O", + domain_ratio="Domain Ratio 1", + data="Simulation", + model=["Domain Layer"], +) -problem.contrasts.append(name="SMW", background="Background 1", resolution="Resolution 1", scalefactor="Scalefactor 1", - bulk_in="Silicon", bulk_out="SLD SMW", domain_ratio="Domain Ratio 1", data="Simulation", - model=["Domain Layer"]) +problem.contrasts.append( + name="SMW", + background="Background 1", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + bulk_in="Silicon", + bulk_out="SLD SMW", + domain_ratio="Domain Ratio 1", + data="Simulation", + model=["Domain Layer"], +) -problem.contrasts.append(name="H2O", background="Background 1", resolution="Resolution 1", scalefactor="Scalefactor 1", - bulk_in="Silicon", bulk_out="SLD H2O", domain_ratio="Domain Ratio 1", data="Simulation", - model=["Domain Layer"]) +problem.contrasts.append( + name="H2O", + background="Background 1", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + bulk_in="Silicon", + bulk_out="SLD H2O", + domain_ratio="Domain Ratio 1", + data="Simulation", + model=["Domain Layer"], +) controls = RAT.set_controls() problem, results = RAT.run(problem, controls) diff --git a/RAT/examples/domains/domains_custom_layers.py b/RAT/examples/domains/domains_custom_layers.py index bb48c19f..eff1b7ba 100644 --- a/RAT/examples/domains/domains_custom_layers.py +++ b/RAT/examples/domains/domains_custom_layers.py @@ -20,13 +20,26 @@ problem.bulk_in.set_fields(0, name="Silicon", value=2.073e-6, max=1.0) # Add the custom file -problem.custom_files.append(name="Alloy domains", filename="alloy_domains.py", language="python", - path=pathlib.Path(__file__).parent.resolve()) +problem.custom_files.append( + name="Alloy domains", + filename="alloy_domains.py", + language="python", + path=pathlib.Path(__file__).parent.resolve(), +) # Make a contrast -problem.contrasts.append(name="D2O Contrast", data="Simulation", background="Background 1", bulk_in="Silicon", - bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", resample=False, - domain_ratio="Domain Ratio 1", model=["Alloy domains"]) +problem.contrasts.append( + name="D2O Contrast", + data="Simulation", + background="Background 1", + bulk_in="Silicon", + bulk_out="SLD D2O", + scalefactor="Scalefactor 1", + resolution="Resolution 1", + resample=False, + domain_ratio="Domain Ratio 1", + model=["Alloy domains"], +) controls = RAT.set_controls() diff --git a/RAT/examples/domains/domains_standard_layers.py b/RAT/examples/domains/domains_standard_layers.py index fe2f3a21..3ec4fd55 100644 --- a/RAT/examples/domains/domains_standard_layers.py +++ b/RAT/examples/domains/domains_standard_layers.py @@ -21,14 +21,32 @@ # Now group these parameters into layers -problem.layers.append(name="Layer 1", thickness="L1 Thickness", SLD="L1 SLD", roughness="L1 Roughness", - hydration="L1 Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Layer 1", + thickness="L1 Thickness", + SLD="L1 SLD", + roughness="L1 Roughness", + hydration="L1 Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="Layer 2", thickness="L2 Thickness", SLD="L2 SLD", roughness="L2 Roughness", - hydration="L2 Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Layer 2", + thickness="L2 Thickness", + SLD="L2 SLD", + roughness="L2 Roughness", + hydration="L2 Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="Layer 3", thickness="L3 Thickness", SLD="L3 SLD", roughness="L3 Roughness", - hydration="L3 Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Layer 3", + thickness="L3 Thickness", + SLD="L3 SLD", + roughness="L3 Roughness", + hydration="L3 Hydration", + hydrate_with="bulk out", +) # If we look at the project, there are two extra groups as compared to a normal standard layers calculation: @@ -37,9 +55,18 @@ problem.domain_contrasts.append(name="Domain 2", model=["Layer 2", "Layer 3"]) # Now make a contrast as with standard models, but this time also including the default domain ratio ("Domain Ratio 1") -problem.contrasts.append(name="Domain Test", background="Background 1", resolution="Resolution 1", - scalefactor="Scalefactor 1", resample=False, bulk_in="SLD Air", bulk_out="SLD D2O", - domain_ratio="Domain Ratio 1", data="Simulation", model=["Domain 1", "Domain 2"]) +problem.contrasts.append( + name="Domain Test", + background="Background 1", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + resample=False, + bulk_in="SLD Air", + bulk_out="SLD D2O", + domain_ratio="Domain Ratio 1", + data="Simulation", + model=["Domain 1", "Domain 2"], +) # Now we can run our simulation as usual, and plot the results diff --git a/RAT/examples/languages/custom_bilayer.py b/RAT/examples/languages/custom_bilayer.py index 9798e699..dc978e31 100644 --- a/RAT/examples/languages/custom_bilayer.py +++ b/RAT/examples/languages/custom_bilayer.py @@ -2,7 +2,6 @@ def custom_bilayer(params, bulk_in, bulk_out, contrast): - sub_rough = params[0] oxide_thick = params[1] oxide_hydration = params[2] @@ -20,23 +19,23 @@ def custom_bilayer(params, bulk_in, bulk_out, contrast): # to make the layers # define all the neutron b's. - bc = 0.6646e-4 # Carbon - bo = 0.5843e-4 # Oxygen - bh = -0.3739e-4 # Hydrogen - bp = 0.513e-4 # Phosphorus - bn = 0.936e-4 # Nitrogen + bc = 0.6646e-4 # Carbon + bo = 0.5843e-4 # Oxygen + bh = -0.3739e-4 # Hydrogen + bp = 0.513e-4 # Phosphorus + bn = 0.936e-4 # Nitrogen # Now make the lipid groups - COO = (4*bo) + (2*bc) - GLYC = (3*bc) + (5*bh) - CH3 = (2*bc) + (6*bh) - PO4 = (1*bp) + (4*bo) - CH2 = (1*bc) + (2*bh) - CHOL = (5*bc) + (12*bh) + (1*bn) + COO = (4 * bo) + (2 * bc) + GLYC = (3 * bc) + (5 * bh) + CH3 = (2 * bc) + (6 * bh) + PO4 = (1 * bp) + (4 * bo) + CH2 = (1 * bc) + (2 * bh) + CHOL = (5 * bc) + (12 * bh) + (1 * bn) # Group these into heads and tails: Head = CHOL + PO4 + GLYC + COO - Tails = (34*CH2) + (2*CH3) + Tails = (34 * CH2) + (2 * CH3) # We need volumes for each. # Use literature values: diff --git a/RAT/examples/languages/run_custom_file_languages.py b/RAT/examples/languages/run_custom_file_languages.py index d47cfe28..c1b37e3b 100644 --- a/RAT/examples/languages/run_custom_file_languages.py +++ b/RAT/examples/languages/run_custom_file_languages.py @@ -1,4 +1,5 @@ """Test custom function languages.""" + import pathlib import time diff --git a/RAT/examples/languages/setup_problem.py b/RAT/examples/languages/setup_problem.py index f3bd448b..a7ed17f8 100644 --- a/RAT/examples/languages/setup_problem.py +++ b/RAT/examples/languages/setup_problem.py @@ -12,7 +12,6 @@ def make_example_problem(): - problem = RAT.Project(name="Orso lipid example - custom layers", model="custom layers", geometry="substrate/liquid") # First we need to set up a parameters group. We will be using a pre-prepared custom model file, so we need to add @@ -52,17 +51,37 @@ def make_example_problem(): problem.data.append(name="Bilayer / H2O", data=H2O_data) # Add the custom file to the project - problem.custom_files.append(name="DSPC Model", filename="custom_bilayer.py", language="python", - path=pathlib.Path(__file__).parent.resolve()) + problem.custom_files.append( + name="DSPC Model", + filename="custom_bilayer.py", + language="python", + path=pathlib.Path(__file__).parent.resolve(), + ) # Also, add the relevant background parameters - one each for each contrast: - problem.background_parameters.set_fields(0, name="Background parameter D2O", fit=True, min=1.0e-10, max=1.0e-5, - value=1.0e-07) - - problem.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=1.0e-7, max=1.0e-5, - fit=True) - problem.background_parameters.append(name="Background parameter H2O", min=1.0e-10, value=1.0e-7, max=1.0e-5, - fit=True) + problem.background_parameters.set_fields( + 0, + name="Background parameter D2O", + fit=True, + min=1.0e-10, + max=1.0e-5, + value=1.0e-07, + ) + + problem.background_parameters.append( + name="Background parameter SMW", + min=1.0e-10, + value=1.0e-7, + max=1.0e-5, + fit=True, + ) + problem.background_parameters.append( + name="Background parameter H2O", + min=1.0e-10, + value=1.0e-7, + max=1.0e-5, + fit=True, + ) # And add the two new constant backgrounds problem.backgrounds.append(name="Background SMW", type="constant", value_1="Background parameter SMW") @@ -75,16 +94,37 @@ def make_example_problem(): problem.scalefactors.set_fields(0, value=1.0, min=0.5, max=2.0, fit=True) # Now add the three contrasts - problem.contrasts.append(name="Bilayer / D2O", background="Background D2O", resolution="Resolution 1", - scalefactor="Scalefactor 1", bulk_out="SLD D2O", bulk_in="Silicon", data="Bilayer / D2O", - model=["DSPC Model"]) - - problem.contrasts.append(name="Bilayer / SMW", background="Background SMW", resolution="Resolution 1", - scalefactor="Scalefactor 1", bulk_out="SLD SMW", bulk_in="Silicon", data="Bilayer / SMW", - model=["DSPC Model"]) - - problem.contrasts.append(name="Bilayer / H2O", background="Background H2O", resolution="Resolution 1", - scalefactor="Scalefactor 1", bulk_out="SLD H2O", bulk_in="Silicon", data="Bilayer / H2O", - model=["DSPC Model"]) + problem.contrasts.append( + name="Bilayer / D2O", + background="Background D2O", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + bulk_out="SLD D2O", + bulk_in="Silicon", + data="Bilayer / D2O", + model=["DSPC Model"], + ) + + problem.contrasts.append( + name="Bilayer / SMW", + background="Background SMW", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + bulk_out="SLD SMW", + bulk_in="Silicon", + data="Bilayer / SMW", + model=["DSPC Model"], + ) + + problem.contrasts.append( + name="Bilayer / H2O", + background="Background H2O", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + bulk_out="SLD H2O", + bulk_in="Silicon", + data="Bilayer / H2O", + model=["DSPC Model"], + ) return problem diff --git a/RAT/examples/non_polarised/DSPC_custom_XY.py b/RAT/examples/non_polarised/DSPC_custom_XY.py index da05f7c5..feb87688 100644 --- a/RAT/examples/non_polarised/DSPC_custom_XY.py +++ b/RAT/examples/non_polarised/DSPC_custom_XY.py @@ -68,12 +68,22 @@ problem.data.append(name="Bilayer / H2O", data=H2O_data) # Add the custom file to the project -problem.custom_files.append(name="DSPC Model", filename="custom_XY_DSPC.py", language="python", - path=pathlib.Path(__file__).parent.resolve()) +problem.custom_files.append( + name="DSPC Model", + filename="custom_XY_DSPC.py", + language="python", + path=pathlib.Path(__file__).parent.resolve(), +) # Also, add the relevant background parameters - one each for each contrast: -problem.background_parameters.set_fields(0, name="Background parameter D2O", fit=True, min=1.0e-10, max=1.0e-5, - value=1.0e-07) +problem.background_parameters.set_fields( + 0, + name="Background parameter D2O", + fit=True, + min=1.0e-10, + max=1.0e-5, + value=1.0e-07, +) problem.background_parameters.append(name="Background parameter SMW", min=0.0, value=1.0e-7, max=1.0e-5, fit=True) problem.background_parameters.append(name="Background parameter H2O", min=0.0, value=1.0e-7, max=1.0e-5, fit=True) @@ -92,17 +102,38 @@ problem.resolutions.append(name="Data Resolution", type="data") # Now add the three contrasts -problem.contrasts.append(name="Bilayer / D2O", background="Background D2O", resolution="Data Resolution", - scalefactor="Scalefactor 1", bulk_out="SLD D2O", bulk_in="Silicon", data="Bilayer / D2O", - model=["DSPC Model"]) - -problem.contrasts.append(name="Bilayer / SMW", background="Background SMW", resolution="Data Resolution", - scalefactor="Scalefactor 1", bulk_out="SLD SMW", bulk_in="Silicon", data="Bilayer / SMW", - model=["DSPC Model"]) - -problem.contrasts.append(name="Bilayer / H2O", background="Background H2O", resolution="Data Resolution", - scalefactor="Scalefactor 1", bulk_out="SLD H2O", bulk_in="Silicon", data="Bilayer / H2O", - model=["DSPC Model"]) +problem.contrasts.append( + name="Bilayer / D2O", + background="Background D2O", + resolution="Data Resolution", + scalefactor="Scalefactor 1", + bulk_out="SLD D2O", + bulk_in="Silicon", + data="Bilayer / D2O", + model=["DSPC Model"], +) + +problem.contrasts.append( + name="Bilayer / SMW", + background="Background SMW", + resolution="Data Resolution", + scalefactor="Scalefactor 1", + bulk_out="SLD SMW", + bulk_in="Silicon", + data="Bilayer / SMW", + model=["DSPC Model"], +) + +problem.contrasts.append( + name="Bilayer / H2O", + background="Background H2O", + resolution="Data Resolution", + scalefactor="Scalefactor 1", + bulk_out="SLD H2O", + bulk_in="Silicon", + data="Bilayer / H2O", + model=["DSPC Model"], +) controls = RAT.set_controls() diff --git a/RAT/examples/non_polarised/DSPC_custom_layers.py b/RAT/examples/non_polarised/DSPC_custom_layers.py index 0b1fdf2f..13cc9cca 100644 --- a/RAT/examples/non_polarised/DSPC_custom_layers.py +++ b/RAT/examples/non_polarised/DSPC_custom_layers.py @@ -2,6 +2,7 @@ Example of using custom layers to model a DSPC supported bilayer. """ + import os import pathlib @@ -48,12 +49,22 @@ problem.data.append(name="Bilayer / H2O", data=H2O_data, data_range=[0.013, 0.33048]) # Add the custom file to the project -problem.custom_files.append(name="DSPC Model", filename="custom_bilayer_DSPC.py", language="python", - path=pathlib.Path(__file__).parent.resolve()) +problem.custom_files.append( + name="DSPC Model", + filename="custom_bilayer_DSPC.py", + language="python", + path=pathlib.Path(__file__).parent.resolve(), +) # Also, add the relevant background parameters - one each for each contrast: -problem.background_parameters.set_fields(0, name="Background parameter D2O", min=1.0e-10, max=1.0e-5, - value=1.0e-07, fit=True) +problem.background_parameters.set_fields( + 0, + name="Background parameter D2O", + min=1.0e-10, + max=1.0e-5, + value=1.0e-07, + fit=True, +) problem.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=1.0e-7, max=1.0e-5, fit=True) problem.background_parameters.append(name="Background parameter H2O", min=1.0e-10, value=1.0e-7, max=1.0e-5, fit=True) @@ -72,17 +83,38 @@ problem.resolutions.append(name="Data Resolution", type="data") # Now add the three contrasts -problem.contrasts.append(name="Bilayer / D2O", background="Background D2O", resolution="Data Resolution", - scalefactor="Scalefactor 1", bulk_out="SLD D2O", bulk_in="Silicon", data="Bilayer / D2O", - model=["DSPC Model"]) - -problem.contrasts.append(name="Bilayer / SMW", background="Background SMW", resolution="Data Resolution", - scalefactor="Scalefactor 1", bulk_out="SLD SMW", bulk_in="Silicon", data="Bilayer / SMW", - model=["DSPC Model"]) - -problem.contrasts.append(name="Bilayer / H2O", background="Background H2O", resolution="Data Resolution", - scalefactor="Scalefactor 1", bulk_out="SLD H2O", bulk_in="Silicon", data="Bilayer / H2O", - model=["DSPC Model"]) +problem.contrasts.append( + name="Bilayer / D2O", + background="Background D2O", + resolution="Data Resolution", + scalefactor="Scalefactor 1", + bulk_out="SLD D2O", + bulk_in="Silicon", + data="Bilayer / D2O", + model=["DSPC Model"], +) + +problem.contrasts.append( + name="Bilayer / SMW", + background="Background SMW", + resolution="Data Resolution", + scalefactor="Scalefactor 1", + bulk_out="SLD SMW", + bulk_in="Silicon", + data="Bilayer / SMW", + model=["DSPC Model"], +) + +problem.contrasts.append( + name="Bilayer / H2O", + background="Background H2O", + resolution="Data Resolution", + scalefactor="Scalefactor 1", + bulk_out="SLD H2O", + bulk_in="Silicon", + data="Bilayer / H2O", + model=["DSPC Model"], +) controls = RAT.set_controls() diff --git a/RAT/examples/non_polarised/DSPC_standard_layers.py b/RAT/examples/non_polarised/DSPC_standard_layers.py index 6212fff2..8799710b 100644 --- a/RAT/examples/non_polarised/DSPC_standard_layers.py +++ b/RAT/examples/non_polarised/DSPC_standard_layers.py @@ -8,71 +8,272 @@ import RAT import RAT.utils.plotting -problem = RAT.Project(name="original_dspc_bilayer", calculation="non polarised", model="standard layers", - geometry="substrate/liquid", absorption=False) +problem = RAT.Project( + name="original_dspc_bilayer", + calculation="non polarised", + model="standard layers", + geometry="substrate/liquid", + absorption=False, +) # Set up the relevant parameters -problem.parameters.append(name="Oxide Thickness", min=5.0, value=19.54, max=60.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) -problem.parameters.append(name="Oxide SLD", min=3.39e-06, value=3.39e-06, max=3.41e-06, fit=False, prior_type="uniform", - mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Tails Thickness", min=15.0, value=22.66, max=35.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Tails SLD", min=-5e-07, value=-4.01e-07, max=-3e-07, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Tails Hydration", min=1.0, value=5.252, max=50.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Roughness", min=1.0, value=5.64, max=15.0, fit=True, prior_type="uniform", mu=0.0, - sigma=np.inf) -problem.parameters.append(name="CW Thickness", min=10.0, value=17.12, max=28.0, fit=True, prior_type="uniform", mu=0.0, - sigma=np.inf) -problem.parameters.append(name="CW SLD", min=0.0, value=0.0, max=1e-09, fit=False, prior_type="uniform", mu=0.0, - sigma=np.inf) -problem.parameters.append(name="SAM Heads Thickness", min=5.0, value=8.56, max=17.0, fit=True, prior_type="gaussian", - mu=10.0, sigma=2.0) -problem.parameters.append(name="SAM Heads SLD", min=1.0e-07, value=1.75e-06, max=2.0e-06, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="SAM Heads Hydration", min=10.0, value=45.45, max=50.0, fit=True, prior_type="uniform", - mu=30.0, sigma=3.0) -problem.parameters.append(name="Bilayer Heads Thickness", min=7.0, value=10.7, max=17.0, fit=True, - prior_type="gaussian", mu=10.0, sigma=2.0) -problem.parameters.append(name="Bilayer Heads SLD", min=5.0e-07, value=1.47e-06, max=1.5e-06, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Roughness", min=2.0, value=6.014, max=15.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Tails Thickness", min=14.0, value=17.82, max=22.0, fit=True, - prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Tails SLD", min=-5.0e-07, value=-4.61e-07, max=0.0, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Tails Hydration", min=10.0, value=17.64, max=50.0, fit=True, - prior_type="uniform", mu=0.0, sigma=np.inf) -problem.parameters.append(name="Bilayer Heads Hydration", min=10.0, value=36.15, max=50.0, fit=True, - prior_type="gaussian", mu=30.0, sigma=3.0) -problem.parameters.append(name="CW Hydration", min=99.9, value=100.0, max=100.0, fit=False, prior_type="uniform", - mu=0.0, sigma=np.inf) -problem.parameters.append(name="Oxide Hydration", min=0.0, value=23.61, max=60.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) +problem.parameters.append( + name="Oxide Thickness", + min=5.0, + value=19.54, + max=60.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Oxide SLD", + min=3.39e-06, + value=3.39e-06, + max=3.41e-06, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="SAM Tails Thickness", + min=15.0, + value=22.66, + max=35.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="SAM Tails SLD", + min=-5e-07, + value=-4.01e-07, + max=-3e-07, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="SAM Tails Hydration", + min=1.0, + value=5.252, + max=50.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="SAM Roughness", + min=1.0, + value=5.64, + max=15.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="CW Thickness", + min=10.0, + value=17.12, + max=28.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="CW SLD", + min=0.0, + value=0.0, + max=1e-09, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="SAM Heads Thickness", + min=5.0, + value=8.56, + max=17.0, + fit=True, + prior_type="gaussian", + mu=10.0, + sigma=2.0, +) +problem.parameters.append( + name="SAM Heads SLD", + min=1.0e-07, + value=1.75e-06, + max=2.0e-06, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="SAM Heads Hydration", + min=10.0, + value=45.45, + max=50.0, + fit=True, + prior_type="uniform", + mu=30.0, + sigma=3.0, +) +problem.parameters.append( + name="Bilayer Heads Thickness", + min=7.0, + value=10.7, + max=17.0, + fit=True, + prior_type="gaussian", + mu=10.0, + sigma=2.0, +) +problem.parameters.append( + name="Bilayer Heads SLD", + min=5.0e-07, + value=1.47e-06, + max=1.5e-06, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Bilayer Roughness", + min=2.0, + value=6.014, + max=15.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Bilayer Tails Thickness", + min=14.0, + value=17.82, + max=22.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Bilayer Tails SLD", + min=-5.0e-07, + value=-4.61e-07, + max=0.0, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Bilayer Tails Hydration", + min=10.0, + value=17.64, + max=50.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Bilayer Heads Hydration", + min=10.0, + value=36.15, + max=50.0, + fit=True, + prior_type="gaussian", + mu=30.0, + sigma=3.0, +) +problem.parameters.append( + name="CW Hydration", + min=99.9, + value=100.0, + max=100.0, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) +problem.parameters.append( + name="Oxide Hydration", + min=0.0, + value=23.61, + max=60.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, +) problem.parameters.set_fields(0, max=10) # Group these into layers -problem.layers.append(name="Oxide", thickness="Oxide Thickness", SLD="Oxide SLD", roughness="Substrate Roughness", - hydration="Oxide Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Oxide", + thickness="Oxide Thickness", + SLD="Oxide SLD", + roughness="Substrate Roughness", + hydration="Oxide Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="SAM Tails", thickness="SAM Tails Thickness", SLD="SAM Tails SLD", roughness="SAM Roughness", - hydration="SAM Tails Hydration", hydrate_with="bulk out") +problem.layers.append( + name="SAM Tails", + thickness="SAM Tails Thickness", + SLD="SAM Tails SLD", + roughness="SAM Roughness", + hydration="SAM Tails Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="SAM Heads", thickness="SAM Heads Thickness", SLD="SAM Heads SLD", roughness="SAM Roughness", - hydration="SAM Heads Hydration", hydrate_with="bulk out") +problem.layers.append( + name="SAM Heads", + thickness="SAM Heads Thickness", + SLD="SAM Heads SLD", + roughness="SAM Roughness", + hydration="SAM Heads Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="Central Water", thickness="CW Thickness", SLD="CW SLD", roughness="Bilayer Roughness", - hydration="CW Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Central Water", + thickness="CW Thickness", + SLD="CW SLD", + roughness="Bilayer Roughness", + hydration="CW Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="Bilayer Heads", thickness="Bilayer Heads Thickness", SLD="Bilayer Heads SLD", - roughness="Bilayer Roughness", hydration="Bilayer Heads Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Bilayer Heads", + thickness="Bilayer Heads Thickness", + SLD="Bilayer Heads SLD", + roughness="Bilayer Roughness", + hydration="Bilayer Heads Hydration", + hydrate_with="bulk out", +) -problem.layers.append(name="Bilayer Tails", thickness="Bilayer Tails Thickness", SLD="Bilayer Tails SLD", - roughness="Bilayer Roughness", hydration="Bilayer Tails Hydration", hydrate_with="bulk out") +problem.layers.append( + name="Bilayer Tails", + thickness="Bilayer Tails Thickness", + SLD="Bilayer Tails SLD", + roughness="Bilayer Roughness", + hydration="Bilayer Tails Hydration", + hydrate_with="bulk out", +) # Make the bulk SLDs del problem.bulk_in[0] @@ -90,10 +291,20 @@ # Now deal with the backgrounds del problem.backgrounds[0] del problem.background_parameters[0] -problem.background_parameters.append(name="Background parameter D2O", min=5.0e-10, value=2.23e-06, max=7.0e-06, - fit=True) -problem.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=3.38e-06, max=4.99e-06, - fit=True) +problem.background_parameters.append( + name="Background parameter D2O", + min=5.0e-10, + value=2.23e-06, + max=7.0e-06, + fit=True, +) +problem.background_parameters.append( + name="Background parameter SMW", + min=1.0e-10, + value=3.38e-06, + max=4.99e-06, + fit=True, +) problem.backgrounds.append(name="D2O Background", type="constant", value_1="Background parameter D2O") problem.backgrounds.append(name="SMW Background", type="constant", value_1="Background parameter SMW") @@ -108,15 +319,39 @@ problem.data.append(name="dspc_bil_smw", data=smw_dat) # Set the model -stack = ["Oxide", "SAM Tails", "SAM Heads", "Central Water", "Bilayer Heads", "Bilayer Tails", "Bilayer Tails", - "Bilayer Heads"] +stack = [ + "Oxide", + "SAM Tails", + "SAM Heads", + "Central Water", + "Bilayer Heads", + "Bilayer Tails", + "Bilayer Tails", + "Bilayer Heads", +] # Then make the two contrasts -problem.contrasts.append(name="D2O", bulk_in="Silicon", bulk_out="D2O", background="D2O Background", - resolution="Resolution 1", scalefactor="Scalefactor 1", data="dspc_bil_D2O", model=stack) +problem.contrasts.append( + name="D2O", + bulk_in="Silicon", + bulk_out="D2O", + background="D2O Background", + resolution="Resolution 1", + scalefactor="Scalefactor 1", + data="dspc_bil_D2O", + model=stack, +) -problem.contrasts.append(name="SMW", bulk_in="Silicon", bulk_out="SMW", background="SMW Background", - resolution="Resolution 1", scalefactor="Scalefactor 2", data="dspc_bil_smw", model=stack) +problem.contrasts.append( + name="SMW", + bulk_in="Silicon", + bulk_out="SMW", + background="SMW Background", + resolution="Resolution 1", + scalefactor="Scalefactor 2", + data="dspc_bil_smw", + model=stack, +) controls = RAT.set_controls() diff --git a/RAT/examples/non_polarised/custom_XY_DSPC.py b/RAT/examples/non_polarised/custom_XY_DSPC.py index 883188cb..0a8c1167 100644 --- a/RAT/examples/non_polarised/custom_XY_DSPC.py +++ b/RAT/examples/non_polarised/custom_XY_DSPC.py @@ -19,23 +19,23 @@ def custom_XY_DSPC(params, bulk_in, bulk_out, contrast): # Define these first # define all the neutron b's. - bc = 0.6646e-4 # Carbon - bo = 0.5843e-4 # Oxygen + bc = 0.6646e-4 # Carbon + bo = 0.5843e-4 # Oxygen bh = -0.3739e-4 # Hydrogen - bp = 0.513e-4 # Phosphorus - bn = 0.936e-4 # Nitrogen + bp = 0.513e-4 # Phosphorus + bn = 0.936e-4 # Nitrogen # Now make the lipid groups - COO = (4*bo) + (2*bc) - GLYC = (3*bc) + (5*bh) - CH3 = (2*bc) + (6*bh) - PO4 = (1*bp) + (4*bo) - CH2 = (1*bc) + (2*bh) - CHOL = (5*bc) + (12*bh) + (1*bn) + COO = (4 * bo) + (2 * bc) + GLYC = (3 * bc) + (5 * bh) + CH3 = (2 * bc) + (6 * bh) + PO4 = (1 * bp) + (4 * bo) + CH2 = (1 * bc) + (2 * bh) + CHOL = (5 * bc) + (12 * bh) + (1 * bn) # Group these into heads and tails heads = CHOL + PO4 + GLYC + COO - tails = (34*CH2) + (2*CH3) + tails = (34 * CH2) + (2 * CH3) # We need volumes for each. Use literature values vHead = 319 @@ -126,8 +126,8 @@ def layer(z, prevLaySurf, thickness, height, Sigma_L, Sigma_R): right = prevLaySurf + thickness # Make our heaviside - a = (z-left) / ((2**0.5) * Sigma_L) - b = (z-right) / ((2**0.5) * Sigma_R) + a = (z - left) / ((2**0.5) * Sigma_L) + b = (z - right) / ((2**0.5) * Sigma_R) erf_a = np.array([math.erf(value) for value in a]) erf_b = np.array([math.erf(value) for value in b]) diff --git a/RAT/examples/non_polarised/custom_bilayer_DSPC.py b/RAT/examples/non_polarised/custom_bilayer_DSPC.py index 3c10a64f..c97cde4e 100644 --- a/RAT/examples/non_polarised/custom_bilayer_DSPC.py +++ b/RAT/examples/non_polarised/custom_bilayer_DSPC.py @@ -39,23 +39,23 @@ def custom_bilayer_DSPC(params, bulk_in, bulk_out, contrast): # Use known lipid volume and compositions to make the layers # define all the neutron b's. - bc = 0.6646e-4 # Carbon - bo = 0.5843e-4 # Oxygen + bc = 0.6646e-4 # Carbon + bo = 0.5843e-4 # Oxygen bh = -0.3739e-4 # Hydrogen - bp = 0.513e-4 # Phosphorus - bn = 0.936e-4 # Nitrogen + bp = 0.513e-4 # Phosphorus + bn = 0.936e-4 # Nitrogen # Now make the lipid groups - COO = (4*bo) + (2*bc) - GLYC = (3*bc) + (5*bh) - CH3 = (2*bc) + (6*bh) - PO4 = (1*bp) + (4*bo) - CH2 = (1*bc) + (2*bh) - CHOL = (5*bc) + (12*bh) + (1*bn) + COO = (4 * bo) + (2 * bc) + GLYC = (3 * bc) + (5 * bh) + CH3 = (2 * bc) + (6 * bh) + PO4 = (1 * bp) + (4 * bo) + CH2 = (1 * bc) + (2 * bh) + CHOL = (5 * bc) + (12 * bh) + (1 * bn) # Group these into heads and tails: Head = CHOL + PO4 + GLYC + COO - Tails = (34*CH2) + (2*CH3) + Tails = (34 * CH2) + (2 * CH3) # We need volumes for each. Use literature values: vHead = 319 diff --git a/RAT/inputs.py b/RAT/inputs.py index b8a2dc37..3c64ceef 100644 --- a/RAT/inputs.py +++ b/RAT/inputs.py @@ -1,4 +1,5 @@ """Converts python models to the necessary inputs for the compiled RAT code""" + import importlib import os import pathlib @@ -11,9 +12,10 @@ from RAT.utils.enums import Calculations, Languages, LayerModels, TypeOptions -def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, - RAT.controls.NS, RAT.controls.Dream], - ) -> tuple[ProblemDefinition, Cells, Limits, Priors, Control]: +def make_input( + project: RAT.Project, + controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, RAT.controls.NS, RAT.controls.Dream], +) -> tuple[ProblemDefinition, Cells, Limits, Priors, Control]: """Constructs the inputs required for the compiled RAT code using the data defined in the input project and controls. @@ -38,22 +40,24 @@ def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT The controls object used in the compiled RAT code. """ - parameter_field = {"parameters": "param", - "bulk_in": "bulkIn", - "bulk_out": "bulkOut", - "scalefactors": "scalefactor", - "domain_ratios": "domainRatio", - "background_parameters": "backgroundParam", - "resolution_parameters": "resolutionParam", - } - checks_field = {"parameters": "fitParam", - "bulk_in": "fitBulkIn", - "bulk_out": "fitBulkOut", - "scalefactors": "fitScalefactor", - "domain_ratios": "fitDomainRatio", - "background_parameters": "fitBackgroundParam", - "resolution_parameters": "fitResolutionParam", - } + parameter_field = { + "parameters": "param", + "bulk_in": "bulkIn", + "bulk_out": "bulkOut", + "scalefactors": "scalefactor", + "domain_ratios": "domainRatio", + "background_parameters": "backgroundParam", + "resolution_parameters": "resolutionParam", + } + checks_field = { + "parameters": "fitParam", + "bulk_in": "fitBulkIn", + "bulk_out": "fitBulkOut", + "scalefactors": "fitScalefactor", + "domain_ratios": "fitDomainRatio", + "background_parameters": "fitBackgroundParam", + "resolution_parameters": "fitResolutionParam", + } prior_id = {"uniform": 1, "gaussian": 2, "jeffreys": 3} @@ -66,21 +70,30 @@ def make_input(project: RAT.Project, controls: Union[RAT.controls.Calculate, RAT for class_list in RAT.project.parameter_class_lists: setattr(checks, checks_field[class_list], [int(element.fit) for element in getattr(project, class_list)]) - setattr(limits, parameter_field[class_list], [[element.min, element.max] - for element in getattr(project, class_list)]) - setattr(priors, parameter_field[class_list], [[element.name, element.prior_type, element.mu, element.sigma] - for element in getattr(project, class_list)]) + setattr( + limits, + parameter_field[class_list], + [[element.min, element.max] for element in getattr(project, class_list)], + ) + setattr( + priors, + parameter_field[class_list], + [[element.name, element.prior_type, element.mu, element.sigma] for element in getattr(project, class_list)], + ) # Use dummy values for qzshifts checks.fitQzshift = [] limits.qzshift = [] priors.qzshift = [] - priors.priorNames = [param.name for class_list in RAT.project.parameter_class_lists - for param in getattr(project, class_list)] - priors.priorValues = [[prior_id[param.prior_type], param.mu, param.sigma] - for class_list in RAT.project.parameter_class_lists - for param in getattr(project, class_list)] + priors.priorNames = [ + param.name for class_list in RAT.project.parameter_class_lists for param in getattr(project, class_list) + ] + priors.priorValues = [ + [prior_id[param.prior_type], param.mu, param.sigma] + for class_list in RAT.project.parameter_class_lists + for param in getattr(project, class_list) + ] if project.model == LayerModels.CustomXY: controls.calcSldDuringFit = True @@ -149,10 +162,13 @@ def make_problem(project: RAT.Project) -> ProblemDefinition: problem.contrastBulkIns = [project.bulk_in.index(contrast.bulk_in, True) for contrast in project.contrasts] problem.contrastBulkOuts = [project.bulk_out.index(contrast.bulk_out, True) for contrast in project.contrasts] problem.contrastQzshifts = [0] * len(project.contrasts) # This is marked as "to do" in RAT - problem.contrastScalefactors = [project.scalefactors.index(contrast.scalefactor, True) - for contrast in project.contrasts] - problem.contrastDomainRatios = [project.domain_ratios.index(contrast.domain_ratio, True) - if hasattr(contrast, "domain_ratio") else 0 for contrast in project.contrasts] + problem.contrastScalefactors = [ + project.scalefactors.index(contrast.scalefactor, True) for contrast in project.contrasts + ] + problem.contrastDomainRatios = [ + project.domain_ratios.index(contrast.domain_ratio, True) if hasattr(contrast, "domain_ratio") else 0 + for contrast in project.contrasts + ] problem.contrastBackgroundParams = contrast_background_params problem.contrastBackgroundActions = [action_id[contrast.background_action] for contrast in project.contrasts] problem.contrastResolutionParams = contrast_resolution_params @@ -163,14 +179,30 @@ def make_problem(project: RAT.Project) -> ProblemDefinition: problem.numberOfContrasts = len(project.contrasts) problem.numberOfLayers = len(project.layers) problem.numberOfDomainContrasts = len(project.domain_contrasts) - problem.fitParams = [param.value for class_list in RAT.project.parameter_class_lists - for param in getattr(project, class_list) if param.fit] - problem.fitLimits = [[param.min, param.max] for class_list in RAT.project.parameter_class_lists - for param in getattr(project, class_list) if param.fit] - problem.otherParams = [param.value for class_list in RAT.project.parameter_class_lists - for param in getattr(project, class_list) if not param.fit] - problem.otherLimits = [[param.min, param.max] for class_list in RAT.project.parameter_class_lists - for param in getattr(project, class_list) if not param.fit] + problem.fitParams = [ + param.value + for class_list in RAT.project.parameter_class_lists + for param in getattr(project, class_list) + if param.fit + ] + problem.fitLimits = [ + [param.min, param.max] + for class_list in RAT.project.parameter_class_lists + for param in getattr(project, class_list) + if param.fit + ] + problem.otherParams = [ + param.value + for class_list in RAT.project.parameter_class_lists + for param in getattr(project, class_list) + if not param.fit + ] + problem.otherLimits = [ + [param.min, param.max] + for class_list in RAT.project.parameter_class_lists + for param in getattr(project, class_list) + if not param.fit + ] check_indices(problem) @@ -208,8 +240,9 @@ def make_data_present(project: RAT.Project) -> list[int]: The "dataPresent" field of the problem input used in the compiled RAT code. """ - return [1 if project.data[project.data.index(contrast.data)].data.size != 0 else 0 - for contrast in project.contrasts] + return [ + 1 if project.data[project.data.index(contrast.data)].data.size != 0 else 0 for contrast in project.contrasts + ] def check_indices(problem: ProblemDefinition) -> None: @@ -222,24 +255,31 @@ def check_indices(problem: ProblemDefinition) -> None: The problem input used in the compiled RAT code. """ - index_list = {"bulkIn": "contrastBulkIns", - "bulkOut": "contrastBulkOuts", - "scalefactors": "contrastScalefactors", - "domainRatio": "contrastDomainRatios", - "backgroundParams": "contrastBackgroundParams", - "resolutionParams": "contrastResolutionParams", - } + index_list = { + "bulkIn": "contrastBulkIns", + "bulkOut": "contrastBulkOuts", + "scalefactors": "contrastScalefactors", + "domainRatio": "contrastDomainRatios", + "backgroundParams": "contrastBackgroundParams", + "resolutionParams": "contrastResolutionParams", + } # Check the indices -- note we have switched to 1-based indexing at this point for params in index_list: param_list = getattr(problem, params) - if len(param_list) > 0 and not all((element > 0 or element == -1) and element <= len(param_list) - for element in getattr(problem, index_list[params])): - elements = [element for element in getattr(problem, index_list[params]) - if not ((element > 0 or element == -1) and element <= len(param_list))] - raise IndexError(f'The problem field "{index_list[params]}" contains: {", ".join(str(i) for i in elements)}' - f', which lie outside of the range of "{params}"') - + if len(param_list) > 0 and not all( + (element > 0 or element == -1) and element <= len(param_list) + for element in getattr(problem, index_list[params]) + ): + elements = [ + element + for element in getattr(problem, index_list[params]) + if not ((element > 0 or element == -1) and element <= len(param_list)) + ] + raise IndexError( + f'The problem field "{index_list[params]}" contains: {", ".join(str(i) for i in elements)}' + f', which lie outside of the range of "{params}"', + ) def make_cells(project: RAT.Project) -> Cells: @@ -263,21 +303,24 @@ def make_cells(project: RAT.Project) -> Cells: # Set contrast parameters according to model type if project.model == LayerModels.StandardLayers: if project.calculation == Calculations.Domains: - contrast_models = [[project.domain_contrasts.index(domain_contrast, True) - for domain_contrast in contrast.model] - for contrast in project.contrasts] + contrast_models = [ + [project.domain_contrasts.index(domain_contrast, True) for domain_contrast in contrast.model] + for contrast in project.contrasts + ] else: - contrast_models = [[project.layers.index(layer, True) for layer in contrast.model] - for contrast in project.contrasts] + contrast_models = [ + [project.layers.index(layer, True) for layer in contrast.model] for contrast in project.contrasts + ] else: contrast_models = [[]] * len(project.contrasts) # Get details of defined layers layer_details = [] for layer in project.layers: - - layer_params = [project.parameters.index(getattr(layer, attribute), True) - for attribute in list(layer.model_fields.keys())[1:-2]] + layer_params = [ + project.parameters.index(getattr(layer, attribute), True) + for attribute in list(layer.model_fields.keys())[1:-2] + ] layer_params.append(project.parameters.index(layer.hydration, True) if layer.hydration else float("NaN")) layer_params.append(hydrate_id[layer.hydrate_with]) @@ -289,7 +332,6 @@ def make_cells(project: RAT.Project) -> Cells: simulation_limits = [] for contrast in project.contrasts: - data_index = project.data.index(contrast.data) all_data.append(project.data[data_index].data) data_range = project.data[data_index].data_range @@ -337,10 +379,13 @@ def make_cells(project: RAT.Project) -> Cells: cells.f17 = [[[]]] * len(project.contrasts) # Placeholder for oil chi data cells.f18 = [[0, 1]] * len(project.domain_contrasts) # This is marked as "to do" in RAT - domain_contrast_models = [[project.layers.index(layer, True) for layer in domain_contrast.model] - for domain_contrast in project.domain_contrasts] - cells.f19 = [domain_contrast_model if domain_contrast_model else 0 - for domain_contrast_model in domain_contrast_models] + domain_contrast_models = [ + [project.layers.index(layer, True) for layer in domain_contrast.model] + for domain_contrast in project.domain_contrasts + ] + cells.f19 = [ + domain_contrast_model if domain_contrast_model else 0 for domain_contrast_model in domain_contrast_models + ] cells.f20 = [param.name for param in project.domain_ratios] @@ -372,8 +417,10 @@ def get_python_handle(file_name: str, function_name: str, path: Union[str, pathl return handle -def make_controls(controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, RAT.controls.NS, - RAT.controls.Dream], checks: Checks) -> Control: +def make_controls( + controls: Union[RAT.controls.Calculate, RAT.controls.Simplex, RAT.controls.DE, RAT.controls.NS, RAT.controls.Dream], + checks: Checks, +) -> Control: """Converts the controls object to the format required by the compiled RAT code. Parameters diff --git a/RAT/models.py b/RAT/models.py index 9f8bd099..4caed0ea 100644 --- a/RAT/models.py +++ b/RAT/models.py @@ -37,12 +37,10 @@ class RATModel(BaseModel, validate_assignment=True, extra="forbid"): """A BaseModel where enums are represented by their value.""" def __repr__(self): - fields_repr = (", ".join(repr(v) if a is None else - f"{a}={v.value!r}" if isinstance(v, StrEnum) else - f"{a}={v!r}" - for a, v in self.__repr_args__() - ) - ) + fields_repr = ", ".join( + repr(v) if a is None else f"{a}={v.value!r}" if isinstance(v, StrEnum) else f"{a}={v!r}" + for a, v in self.__repr_args__() + ) return f"{self.__repr_name__()}({fields_repr})" @@ -107,8 +105,7 @@ def model_post_init(self, __context: Any) -> None: @model_validator(mode="after") def set_matlab_function_name(self): - """If we have a matlab custom function, the "function_name" should be set to the filename without the extension. - """ + """If we have a matlab custom function, the "function_name" should be set to the filename without the extension.""" if self.language == Languages.Matlab and self.function_name != pathlib.Path(self.filename).stem: self.function_name = pathlib.Path(self.filename).stem @@ -163,14 +160,20 @@ def check_ranges(self) -> "Data": if self.data.shape[0] > 0: data_min = np.min(self.data[:, 0]) data_max = np.max(self.data[:, 0]) - if "data_range" in self.model_fields_set and (self.data_range[0] < data_min or - self.data_range[1] > data_max): - raise ValueError(f"The data_range value of: {self.data_range} must lie within the min/max values of " - f"the data: [{data_min}, {data_max}]") - if "simulation_range" in self.model_fields_set and (self.simulation_range[0] > data_min or - self.simulation_range[1] < data_max): - raise ValueError(f"The simulation_range value of: {self.simulation_range} must lie outside of the " - f"min/max values of the data: [{data_min}, {data_max}]") + if "data_range" in self.model_fields_set and ( + self.data_range[0] < data_min or self.data_range[1] > data_max + ): + raise ValueError( + f"The data_range value of: {self.data_range} must lie within the min/max values of " + f"the data: [{data_min}, {data_max}]", + ) + if "simulation_range" in self.model_fields_set and ( + self.simulation_range[0] > data_min or self.simulation_range[1] < data_max + ): + raise ValueError( + f"The simulation_range value of: {self.simulation_range} must lie outside of the " + f"min/max values of the data: [{data_min}, {data_max}]", + ) return self def __eq__(self, other: object) -> bool: @@ -182,25 +185,24 @@ def __eq__(self, other: object) -> bool: other_type = other.__pydantic_generic_metadata__["origin"] or other.__class__ return ( - self_type == other_type - and self.name == other.name - and (self.data == other.data).all() - and self.data_range == other.data_range - and self.simulation_range == other.simulation_range - and self.__pydantic_private__ == other.__pydantic_private__ - and self.__pydantic_extra__ == other.__pydantic_extra__ + self_type == other_type + and self.name == other.name + and (self.data == other.data).all() + and self.data_range == other.data_range + and self.simulation_range == other.simulation_range + and self.__pydantic_private__ == other.__pydantic_private__ + and self.__pydantic_extra__ == other.__pydantic_extra__ ) else: return NotImplemented # delegate to the other item in the comparison def __repr__(self): """Only include the name if the data is empty.""" - fields_repr = (f"name={self.name!r}" if self.data.size == 0 else - ", ".join(repr(v) if a is None else - f"{a}={v!r}" - for a, v in self.__repr_args__() - ) - ) + fields_repr = ( + f"name={self.name!r}" + if self.data.size == 0 + else ", ".join(repr(v) if a is None else f"{a}={v!r}" for a, v in self.__repr_args__()) + ) return f"{self.__repr_name__()}({fields_repr})" diff --git a/RAT/outputs.py b/RAT/outputs.py index 5323dca9..6ed51bc2 100644 --- a/RAT/outputs.py +++ b/RAT/outputs.py @@ -108,12 +108,16 @@ class BayesResults(Results): chain: np.ndarray -def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResult, - bayes_results: Optional[RAT.rat_core.BayesResults] = None) -> Union[Results, BayesResults]: +def make_results( + procedure: Procedures, + output_results: RAT.rat_core.OutputResult, + bayes_results: Optional[RAT.rat_core.BayesResults] = None, +) -> Union[Results, BayesResults]: """Initialise a python Results or BayesResults object using the outputs from a RAT calculation.""" - calculation_results = CalculationResults(chiValues=output_results.calculationResults.chiValues, - sumChi=output_results.calculationResults.sumChi, - ) + calculation_results = CalculationResults( + chiValues=output_results.calculationResults.chiValues, + sumChi=output_results.calculationResults.sumChi, + ) contrast_params = ContrastParams( backgroundParams=output_results.contrastParams.backgroundParams, scalefactors=output_results.contrastParams.scalefactors, @@ -125,7 +129,6 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul ) if procedure in [Procedures.NS, Procedures.Dream]: - prediction_intervals = PredictionIntervals( reflectivity=bayes_results.predictionIntervals.reflectivity, sld=bayes_results.predictionIntervals.sld, @@ -199,7 +202,6 @@ def make_results(procedure: Procedures, output_results: RAT.rat_core.OutputResul ) else: - results = Results( reflectivity=output_results.reflectivity, simulation=output_results.simulation, diff --git a/RAT/project.py b/RAT/project.py index c8c0630c..46b2b224 100644 --- a/RAT/project.py +++ b/RAT/project.py @@ -15,67 +15,82 @@ from RAT.utils.enums import Calculations, Geometries, LayerModels, Priors, TypeOptions # Map project fields to pydantic models -model_in_classlist = {"parameters": "Parameter", - "bulk_in": "Parameter", - "bulk_out": "Parameter", - "scalefactors": "Parameter", - "domain_ratios": "Parameter", - "background_parameters": "Parameter", - "resolution_parameters": "Parameter", - "backgrounds": "Background", - "resolutions": "Resolution", - "custom_files": "CustomFile", - "data": "Data", - "layers": "Layer", - "domain_contrasts": "DomainContrast", - "contrasts": "Contrast", - } - -values_defined_in = {"backgrounds.value_1": "background_parameters", - "backgrounds.value_2": "background_parameters", - "backgrounds.value_3": "background_parameters", - "backgrounds.value_4": "background_parameters", - "backgrounds.value_5": "background_parameters", - "resolutions.value_1": "resolution_parameters", - "resolutions.value_2": "resolution_parameters", - "resolutions.value_3": "resolution_parameters", - "resolutions.value_4": "resolution_parameters", - "resolutions.value_5": "resolution_parameters", - "layers.thickness": "parameters", - "layers.SLD": "parameters", - "layers.SLD_real": "parameters", - "layers.SLD_imaginary": "parameters", - "layers.roughness": "parameters", - "contrasts.data": "data", - "contrasts.background": "backgrounds", - "contrasts.bulk_in": "bulk_in", - "contrasts.bulk_out": "bulk_out", - "contrasts.scalefactor": "scalefactors", - "contrasts.resolution": "resolutions", - "contrasts.domain_ratio": "domain_ratios", - } +model_in_classlist = { + "parameters": "Parameter", + "bulk_in": "Parameter", + "bulk_out": "Parameter", + "scalefactors": "Parameter", + "domain_ratios": "Parameter", + "background_parameters": "Parameter", + "resolution_parameters": "Parameter", + "backgrounds": "Background", + "resolutions": "Resolution", + "custom_files": "CustomFile", + "data": "Data", + "layers": "Layer", + "domain_contrasts": "DomainContrast", + "contrasts": "Contrast", +} + +values_defined_in = { + "backgrounds.value_1": "background_parameters", + "backgrounds.value_2": "background_parameters", + "backgrounds.value_3": "background_parameters", + "backgrounds.value_4": "background_parameters", + "backgrounds.value_5": "background_parameters", + "resolutions.value_1": "resolution_parameters", + "resolutions.value_2": "resolution_parameters", + "resolutions.value_3": "resolution_parameters", + "resolutions.value_4": "resolution_parameters", + "resolutions.value_5": "resolution_parameters", + "layers.thickness": "parameters", + "layers.SLD": "parameters", + "layers.SLD_real": "parameters", + "layers.SLD_imaginary": "parameters", + "layers.roughness": "parameters", + "contrasts.data": "data", + "contrasts.background": "backgrounds", + "contrasts.bulk_in": "bulk_in", + "contrasts.bulk_out": "bulk_out", + "contrasts.scalefactor": "scalefactors", + "contrasts.resolution": "resolutions", + "contrasts.domain_ratio": "domain_ratios", +} AllFields = collections.namedtuple("AllFields", ["attribute", "fields"]) -model_names_used_in = {"background_parameters": AllFields("backgrounds", ["value_1", "value_2", "value_3", "value_4", - "value_5"]), - "resolution_parameters": AllFields("resolutions", ["value_1", "value_2", "value_3", "value_4", - "value_5"]), - "parameters": AllFields("layers", ["thickness", "SLD", "SLD_real", "SLD_imaginary", - "roughness", "hydration"]), - "data": AllFields("contrasts", ["data"]), - "backgrounds": AllFields("contrasts", ["background"]), - "bulk_in": AllFields("contrasts", ["bulk_in"]), - "bulk_out": AllFields("contrasts", ["bulk_out"]), - "scalefactors": AllFields("contrasts", ["scalefactor"]), - "domain_ratios": AllFields("contrasts", ["domain_ratio"]), - "resolutions": AllFields("contrasts", ["resolution"]), - } +model_names_used_in = { + "background_parameters": AllFields("backgrounds", ["value_1", "value_2", "value_3", "value_4", "value_5"]), + "resolution_parameters": AllFields("resolutions", ["value_1", "value_2", "value_3", "value_4", "value_5"]), + "parameters": AllFields("layers", ["thickness", "SLD", "SLD_real", "SLD_imaginary", "roughness", "hydration"]), + "data": AllFields("contrasts", ["data"]), + "backgrounds": AllFields("contrasts", ["background"]), + "bulk_in": AllFields("contrasts", ["bulk_in"]), + "bulk_out": AllFields("contrasts", ["bulk_out"]), + "scalefactors": AllFields("contrasts", ["scalefactor"]), + "domain_ratios": AllFields("contrasts", ["domain_ratio"]), + "resolutions": AllFields("contrasts", ["resolution"]), +} # Note that the order of these parameters is hard-coded into RAT -parameter_class_lists = ["parameters", "background_parameters", "scalefactors", "bulk_in", "bulk_out", - "resolution_parameters", "domain_ratios"] -class_lists = [*parameter_class_lists, "backgrounds", "resolutions", "custom_files", "data", "layers", - "domain_contrasts", "contrasts"] +parameter_class_lists = [ + "parameters", + "background_parameters", + "scalefactors", + "bulk_in", + "bulk_out", + "resolution_parameters", + "domain_ratios", +] +class_lists = [ + *parameter_class_lists, + "backgrounds", + "resolutions", + "custom_files", + "data", + "layers", + "domain_contrasts", + "contrasts", +] class Project(BaseModel, validate_assignment=True, extra="forbid", arbitrary_types_allowed=True): @@ -93,36 +108,91 @@ class Project(BaseModel, validate_assignment=True, extra="forbid", arbitrary_typ parameters: ClassList = ClassList() - bulk_in: ClassList = ClassList(RAT.models.Parameter(name="SLD Air", min=0.0, value=0.0, max=0.0, fit=False, - prior_type=Priors.Uniform, mu=0.0, sigma=np.inf)) - - bulk_out: ClassList = ClassList(RAT.models.Parameter(name="SLD D2O", min=6.2e-6, value=6.35e-6, max=6.35e-6, - fit=False, prior_type=Priors.Uniform, mu=0.0, - sigma=np.inf)) - - scalefactors: ClassList = ClassList(RAT.models.Parameter(name="Scalefactor 1", min=0.02, value=0.23, max=0.25, - fit=False, prior_type=Priors.Uniform, mu=0.0, - sigma=np.inf)) - - domain_ratios: ClassList = ClassList(RAT.models.Parameter(name="Domain Ratio 1", min=0.4, value=0.5, max=0.6, - fit=False, prior_type=Priors.Uniform, mu=0.0, - sigma=np.inf)) - - background_parameters: ClassList = ClassList(RAT.models.Parameter(name="Background Param 1", min=1e-7, value=1e-6, - max=1e-5, fit=False, - prior_type=Priors.Uniform, mu=0.0, - sigma=np.inf)) - - backgrounds: ClassList = ClassList(RAT.models.Background(name="Background 1", type=TypeOptions.Constant, - value_1="Background Param 1")) - - resolution_parameters: ClassList = ClassList(RAT.models.Parameter(name="Resolution Param 1", min=0.01, value=0.03, - max=0.05, fit=False, - prior_type=Priors.Uniform, mu=0.0, - sigma=np.inf)) - - resolutions: ClassList = ClassList(RAT.models.Resolution(name="Resolution 1", type=TypeOptions.Constant, - value_1="Resolution Param 1")) + bulk_in: ClassList = ClassList( + RAT.models.Parameter( + name="SLD Air", + min=0.0, + value=0.0, + max=0.0, + fit=False, + prior_type=Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) + + bulk_out: ClassList = ClassList( + RAT.models.Parameter( + name="SLD D2O", + min=6.2e-6, + value=6.35e-6, + max=6.35e-6, + fit=False, + prior_type=Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) + + scalefactors: ClassList = ClassList( + RAT.models.Parameter( + name="Scalefactor 1", + min=0.02, + value=0.23, + max=0.25, + fit=False, + prior_type=Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) + + domain_ratios: ClassList = ClassList( + RAT.models.Parameter( + name="Domain Ratio 1", + min=0.4, + value=0.5, + max=0.6, + fit=False, + prior_type=Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) + + background_parameters: ClassList = ClassList( + RAT.models.Parameter( + name="Background Param 1", + min=1e-7, + value=1e-6, + max=1e-5, + fit=False, + prior_type=Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) + + backgrounds: ClassList = ClassList( + RAT.models.Background(name="Background 1", type=TypeOptions.Constant, value_1="Background Param 1"), + ) + + resolution_parameters: ClassList = ClassList( + RAT.models.Parameter( + name="Resolution Param 1", + min=0.01, + value=0.03, + max=0.05, + fit=False, + prior_type=Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) + + resolutions: ClassList = ClassList( + RAT.models.Resolution(name="Resolution 1", type=TypeOptions.Constant, value_1="Resolution Param 1"), + ) custom_files: ClassList = ClassList() data: ClassList = ClassList() @@ -134,9 +204,21 @@ class Project(BaseModel, validate_assignment=True, extra="forbid", arbitrary_typ _contrast_model_field: str _protected_parameters: dict - @field_validator("parameters", "bulk_in", "bulk_out", "scalefactors", "background_parameters", - "backgrounds", "resolution_parameters", "resolutions", "custom_files", "data", "layers", - "domain_contrasts", "contrasts") + @field_validator( + "parameters", + "bulk_in", + "bulk_out", + "scalefactors", + "background_parameters", + "backgrounds", + "resolution_parameters", + "resolutions", + "custom_files", + "data", + "layers", + "domain_contrasts", + "contrasts", + ) @classmethod def check_class(cls, value: ClassList, info: ValidationInfo) -> ClassList: """Each of the data fields should be a ClassList of the appropriate model.""" @@ -178,10 +260,19 @@ def model_post_init(self, __context: Any) -> None: field._class_handle = getattr(RAT.models, model) if "Substrate Roughness" not in self.parameters.get_names(): - self.parameters.insert(0, RAT.models.ProtectedParameter(name="Substrate Roughness", min=1.0, value=3.0, - max=5.0, fit=True, - prior_type=RAT.models.Priors.Uniform, mu=0.0, - sigma=np.inf)) + self.parameters.insert( + 0, + RAT.models.ProtectedParameter( + name="Substrate Roughness", + min=1.0, + value=3.0, + max=5.0, + fit=True, + prior_type=RAT.models.Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ) elif "Substrate Roughness" not in self.get_all_protected_parameters().values(): # If substrate roughness is included as a standard parameter replace it with a protected parameter substrate_roughness_values = self.parameters[self.parameters.index("Substrate Roughness")].model_dump() @@ -197,8 +288,18 @@ def model_post_init(self, __context: Any) -> None: # Wrap ClassList routines - when any of these routines are called, the wrapper will force revalidation of the # model, handle errors and reset previous values if necessary. - methods_to_wrap = ["_setitem", "_delitem", "_iadd", "append", "insert", "pop", "remove", "clear", "extend", - "set_fields"] + methods_to_wrap = [ + "_setitem", + "_delitem", + "_iadd", + "append", + "insert", + "pop", + "remove", + "clear", + "extend", + "set_fields", + ] for class_list in class_lists: attribute = getattr(self, class_list) @@ -237,9 +338,18 @@ def set_calculation(self) -> "Project": for contrast in self.contrasts: contrast_list.append(RAT.models.ContrastWithRatio(**contrast.model_dump())) self.contrasts.data = contrast_list - self.domain_ratios.data = [RAT.models.Parameter(name="Domain Ratio 1", min=0.4, value=0.5, max=0.6, - fit=False, prior_type=RAT.models.Priors.Uniform, mu=0.0, - sigma=np.inf)] + self.domain_ratios.data = [ + RAT.models.Parameter( + name="Domain Ratio 1", + min=0.4, + value=0.5, + max=0.6, + fit=False, + prior_type=RAT.models.Priors.Uniform, + mu=0.0, + sigma=np.inf, + ), + ] self.contrasts._class_handle = RAT.models.ContrastWithRatio elif self.calculation != Calculations.Domains and handle == "ContrastWithRatio": for contrast in self.contrasts: @@ -270,13 +380,17 @@ def check_contrast_model_length(self) -> "Project": if self.model == LayerModels.StandardLayers and self.calculation == Calculations.Domains: for contrast in self.contrasts: if contrast.model and len(contrast.model) != 2: - raise ValueError('For a standard layers domains calculation the "model" field of "contrasts" must ' - 'contain exactly two values.') + raise ValueError( + 'For a standard layers domains calculation the "model" field of "contrasts" must ' + "contain exactly two values.", + ) elif self.model != LayerModels.StandardLayers: for contrast in self.contrasts: if len(contrast.model) > 1: - raise ValueError('For a custom model calculation the "model" field of "contrasts" cannot contain ' - 'more than one value.') + raise ValueError( + 'For a custom model calculation the "model" field of "contrasts" cannot contain ' + "more than one value.", + ) return self @model_validator(mode="after") @@ -306,11 +420,11 @@ def update_renamed_models(self) -> "Project": new_names = getattr(self, class_list).get_names() if len(old_names) == len(new_names): name_diff = [(old, new) for (old, new) in zip(old_names, new_names) if old != new] - for (old_name, new_name) in name_diff: + for old_name, new_name in name_diff: model_names_list = getattr(self, model_names_used_in[class_list].attribute) all_matches = model_names_list.get_all_matches(old_name) fields = model_names_used_in[class_list].fields - for (index, field) in all_matches: + for index, field in all_matches: if field in fields: setattr(model_names_list[index], field, new_name) self._all_names = self.get_all_names() @@ -322,8 +436,11 @@ def cross_check_model_values(self) -> "Project": value_fields = ["value_1", "value_2", "value_3", "value_4", "value_5"] self.check_allowed_values("backgrounds", value_fields, self.background_parameters.get_names()) self.check_allowed_values("resolutions", value_fields, self.resolution_parameters.get_names()) - self.check_allowed_values("layers", ["thickness", "SLD", "SLD_real", "SLD_imaginary", "roughness"], - self.parameters.get_names()) + self.check_allowed_values( + "layers", + ["thickness", "SLD", "SLD_real", "SLD_imaginary", "roughness"], + self.parameters.get_names(), + ) self.check_allowed_values("contrasts", ["data"], self.data.get_names()) self.check_allowed_values("contrasts", ["background"], self.backgrounds.get_names()) @@ -333,8 +450,11 @@ def cross_check_model_values(self) -> "Project": self.check_allowed_values("contrasts", ["resolution"], self.resolutions.get_names()) self.check_allowed_values("contrasts", ["domain_ratio"], self.domain_ratios.get_names()) - self.check_contrast_model_allowed_values("contrasts", getattr(self, self._contrast_model_field).get_names(), - self._contrast_model_field) + self.check_contrast_model_allowed_values( + "contrasts", + getattr(self, self._contrast_model_field).get_names(), + self._contrast_model_field, + ) self.check_contrast_model_allowed_values("domain_contrasts", self.layers.get_names(), "layers") return self @@ -342,12 +462,14 @@ def cross_check_model_values(self) -> "Project": def check_protected_parameters(self) -> "Project": """Protected parameters should not be deleted. If this is attempted, raise an error.""" for class_list in parameter_class_lists: - protected_parameters = [param.name for param in getattr(self, class_list) - if isinstance(param, RAT.models.ProtectedParameter)] + protected_parameters = [ + param.name for param in getattr(self, class_list) if isinstance(param, RAT.models.ProtectedParameter) + ] # All previously existing protected parameters should be present in new list if not all(element in protected_parameters for element in self._protected_parameters[class_list]): - removed_params = [param for param in self._protected_parameters[class_list] - if param not in protected_parameters] + removed_params = [ + param for param in self._protected_parameters[class_list] if param not in protected_parameters + ] raise ValueError(f'Can\'t delete the protected parameters: {", ".join(str(i) for i in removed_params)}') self._protected_parameters = self.get_all_protected_parameters() return self @@ -369,9 +491,12 @@ def get_all_names(self): def get_all_protected_parameters(self): """Record the protected parameters defined in the project.""" - return {class_list: [param.name for param in getattr(self, class_list) - if isinstance(param, RAT.models.ProtectedParameter)] - for class_list in parameter_class_lists} + return { + class_list: [ + param.name for param in getattr(self, class_list) if isinstance(param, RAT.models.ProtectedParameter) + ] + for class_list in parameter_class_lists + } def check_allowed_values(self, attribute: str, field_list: list[str], allowed_values: list[str]) -> None: """Check the values of the given fields in the given model are in the supplied list of allowed values. @@ -396,11 +521,17 @@ def check_allowed_values(self, attribute: str, field_list: list[str], allowed_va for field in field_list: value = getattr(model, field, "") if value and value not in allowed_values: - raise ValueError(f'The value "{value}" in the "{field}" field of "{attribute}" must be defined in ' - f'"{values_defined_in[f"{attribute}.{field}"]}".') - - def check_contrast_model_allowed_values(self, contrast_attribute: str, allowed_values: list[str], - allowed_field: str) -> None: + raise ValueError( + f'The value "{value}" in the "{field}" field of "{attribute}" must be defined in ' + f'"{values_defined_in[f"{attribute}.{field}"]}".', + ) + + def check_contrast_model_allowed_values( + self, + contrast_attribute: str, + allowed_values: list[str], + allowed_field: str, + ) -> None: """The contents of the "model" field of "contrasts" and "domain_contrasts" must be defined elsewhere in the project. @@ -423,8 +554,10 @@ def check_contrast_model_allowed_values(self, contrast_attribute: str, allowed_v for contrast in class_list: model_values = contrast.model if model_values and not all(value in allowed_values for value in model_values): - raise ValueError(f'The values: "{", ".join(str(i) for i in model_values)}" in the "model" field of ' - f'"{contrast_attribute}" must be defined in "{allowed_field}".') + raise ValueError( + f'The values: "{", ".join(str(i) for i in model_values)}" in the "model" field of ' + f'"{contrast_attribute}" must be defined in "{allowed_field}".', + ) def get_contrast_model_field(self): """Get the field used to define the contents of the "model" field in contrasts. @@ -466,15 +599,18 @@ def write_script(self, obj_name: str = "problem", script: str = "project_script. indent = 4 * " " with open(script, "w") as f: - - f.write('# THIS FILE IS GENERATED FROM RAT VIA THE "WRITE_SCRIPT" ROUTINE. IT IS NOT PART OF THE RAT CODE.' - '\n\n') + f.write( + '# THIS FILE IS GENERATED FROM RAT VIA THE "WRITE_SCRIPT" ROUTINE. IT IS NOT PART OF THE RAT CODE.' + "\n\n", + ) # Need imports f.write("import RAT\nfrom RAT.models import *\nfrom numpy import array, inf\n\n") - f.write(f"{obj_name} = RAT.Project(\n{indent}name='{self.name}', calculation='{self.calculation}'," - f" model='{self.model}', geometry='{self.geometry}', absorption={self.absorption},\n") + f.write( + f"{obj_name} = RAT.Project(\n{indent}name='{self.name}', calculation='{self.calculation}'," + f" model='{self.model}', geometry='{self.geometry}', absorption={self.absorption},\n", + ) for class_list in class_lists: contents = getattr(self, class_list).data @@ -498,6 +634,7 @@ def _classlist_wrapper(self, class_list: ClassList, func: Callable): The wrapped routine. """ + @functools.wraps(func) def wrapped_func(*args, **kwargs): """Run the given function and then revalidate the "Project" model. If any exception is raised, restore @@ -518,4 +655,5 @@ def wrapped_func(*args, **kwargs): finally: del previous_state return return_value + return wrapped_func diff --git a/RAT/run.py b/RAT/run.py index b2ce25d1..f654420e 100644 --- a/RAT/run.py +++ b/RAT/run.py @@ -5,25 +5,31 @@ def run(project, controls): """Run RAT for the given project and controls inputs.""" - parameter_field = {"parameters": "params", - "bulk_in": "bulkIn", - "bulk_out": "bulkOut", - "scalefactors": "scalefactors", - "domain_ratios": "domainRatio", - "background_parameters": "backgroundParams", - "resolution_parameters": "resolutionParams", - } + parameter_field = { + "parameters": "params", + "bulk_in": "bulkIn", + "bulk_out": "bulkOut", + "scalefactors": "scalefactors", + "domain_ratios": "domainRatio", + "background_parameters": "backgroundParams", + "resolution_parameters": "resolutionParams", + } problem_definition, cells, limits, priors, cpp_controls = make_input(project, controls) - problem_definition, output_results, bayes_results = RAT.rat_core.RATMain(problem_definition, cells, limits, - cpp_controls, priors) + problem_definition, output_results, bayes_results = RAT.rat_core.RATMain( + problem_definition, + cells, + limits, + cpp_controls, + priors, + ) results = make_results(controls.procedure, output_results, bayes_results) # Update parameter values in project for class_list in RAT.project.parameter_class_lists: - for (index, value) in enumerate(getattr(problem_definition, parameter_field[class_list])): + for index, value in enumerate(getattr(problem_definition, parameter_field[class_list])): getattr(project, class_list)[index].value = value return project, results diff --git a/RAT/utils/custom_errors.py b/RAT/utils/custom_errors.py index d3d2bd8e..2dabb011 100644 --- a/RAT/utils/custom_errors.py +++ b/RAT/utils/custom_errors.py @@ -1,10 +1,12 @@ """Defines routines for custom error handling in RAT.""" + import pydantic_core -def custom_pydantic_validation_error(error_list: list[pydantic_core.ErrorDetails], - custom_error_msgs: dict[str, str] = None, - ) -> list[pydantic_core.ErrorDetails]: +def custom_pydantic_validation_error( + error_list: list[pydantic_core.ErrorDetails], + custom_error_msgs: dict[str, str] = None, +) -> list[pydantic_core.ErrorDetails]: """Run through the list of errors generated from a pydantic ValidationError, substituting the standard error for a PydanticCustomError for a given set of error types. diff --git a/RAT/utils/plotting.py b/RAT/utils/plotting.py index 0a2cc351..99748f27 100644 --- a/RAT/utils/plotting.py +++ b/RAT/utils/plotting.py @@ -1,5 +1,5 @@ -"""Plots using the matplotlib library -""" +"""Plots using the matplotlib library""" + from typing import Optional, Union import matplotlib.pyplot as plt @@ -13,8 +13,7 @@ class Figure: - """Creates a plotting figure. - """ + """Creates a plotting figure.""" def __init__(self, row: int = 1, col: int = 1): """Initializes the figure and the subplots. @@ -27,15 +26,12 @@ def __init__(self, row: int = 1, col: int = 1): The number of columns in subplot """ - self._fig, self._ax = \ - plt.subplots(row, col, num="Reflectivity Algorithms Toolbox (RAT)") + self._fig, self._ax = plt.subplots(row, col, num="Reflectivity Algorithms Toolbox (RAT)") plt.show(block=False) self._esc_pressed = False self._close_clicked = False - self._fig.canvas.mpl_connect("key_press_event", - self._process_button_press) - self._fig.canvas.mpl_connect("close_event", - self._close) + self._fig.canvas.mpl_connect("key_press_event", self._process_button_press) + self._fig.canvas.mpl_connect("close_event", self._close) def wait_for_close(self): """Waits for the user to close the figure @@ -46,19 +42,16 @@ def wait_for_close(self): plt.close(self._fig) def _process_button_press(self, event): - """Process the key_press_event. - """ + """Process the key_press_event.""" if event.key == "escape": self._esc_pressed = True def _close(self, _): - """Process the close_event. - """ + """Process the close_event.""" self._close_clicked = True -def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, - one_sided: bool, color: str): +def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, one_sided: bool, color: str): """Plots the error bars. Parameters @@ -77,14 +70,8 @@ def plot_errorbars(ax: Axes, x: np.ndarray, y: np.ndarray, err: np.ndarray, The hex representing the color of the errorbars """ - y_error = [[0]*len(err), err] if one_sided else err - ax.errorbar(x=x, - y=y, - yerr=y_error, - fmt="none", - ecolor=color, - elinewidth=1, - capsize=0) + y_error = [[0] * len(err), err] if one_sided else err + ax.errorbar(x=x, y=y, yerr=y_error, fmt="none", ecolor=color, elinewidth=1, capsize=0) ax.scatter(x=x, y=y, s=3, marker="o", color=color) @@ -96,7 +83,7 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay data : PlotEventData The plot event data that contains all the information to generate the ref and sld plots - fig : Figure, optional + fig : Figure, optional The figure class that has two subplots delay : bool, default: True Controls whether to delay 0.005s after plot is created @@ -120,45 +107,33 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay ref_plot.cla() sld_plot.cla() - for i, (r, sd, sld, layer) in enumerate(zip(data.reflectivity, - data.shiftedData, - data.sldProfiles, - data.resampledLayers)): + for i, (r, sd, sld, layer) in enumerate( + zip(data.reflectivity, data.shiftedData, data.sldProfiles, data.resampledLayers), + ): # Calculate the divisor - div = 1 if i == 0 else 2**(4*(i+1)) + div = 1 if i == 0 else 2 ** (4 * (i + 1)) # Plot the reflectivity on plot (1,1) - ref_plot.plot(r[:, 0], - r[:, 1]/div, - label=f"ref {i+1}", - linewidth=2) + ref_plot.plot(r[:, 0], r[:, 1] / div, label=f"ref {i+1}", linewidth=2) color = ref_plot.get_lines()[-1].get_color() if data.dataPresent[i]: sd_x = sd[:, 0] - sd_y, sd_e = map(lambda x: x/div, (sd[:, 1], sd[:, 2])) + sd_y, sd_e = map(lambda x: x / div, (sd[:, 1], sd[:, 2])) # Plot the errorbars indices_removed = np.flip(np.nonzero(sd_y - sd_e < 0)[0]) - sd_x_r, sd_y_r, sd_e_r = map(lambda x: - np.delete(x, indices_removed), - (sd_x, sd_y, sd_e)) + sd_x_r, sd_y_r, sd_e_r = map(lambda x: np.delete(x, indices_removed), (sd_x, sd_y, sd_e)) plot_errorbars(ref_plot, sd_x_r, sd_y_r, sd_e_r, False, color) # Plot one sided errorbars - indices_selected = [x for x in indices_removed - if x not in np.nonzero(sd_y < 0)[0]] - sd_x_s, sd_y_s, sd_e_s = map(lambda x: - [x[i] for i in indices_selected], - (sd_x, sd_y, sd_e)) + indices_selected = [x for x in indices_removed if x not in np.nonzero(sd_y < 0)[0]] + sd_x_s, sd_y_s, sd_e_s = map(lambda x: [x[i] for i in indices_selected], (sd_x, sd_y, sd_e)) plot_errorbars(ref_plot, sd_x_s, sd_y_s, sd_e_s, True, color) # Plot the slds on plot (1,2) for j in range(len(sld)): - sld_plot.plot(sld[j][:, 0], - sld[j][:, 1], - label=f"sld {i+1}", - linewidth=1) + sld_plot.plot(sld[j][:, 0], sld[j][:, 1], label=f"sld {i+1}", linewidth=1) if data.resample[i] == 1 or data.modelType == "custom xy": layers = data.resampledLayers[i][0] @@ -166,17 +141,21 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay layer = data.resampledLayers[i][j] if layers.shape[1] == 4: layer = np.delete(layer, 2, 1) - new_profile = makeSLDProfileXY(layers[0, 1], # Bulk In - layers[-1, 1], # Bulk Out - data.subRoughs[i], # roughness - layer, - len(layer), - 1.0) - - sld_plot.plot([row[0]-49 for row in new_profile], - [row[1] for row in new_profile], - color=color, - linewidth=1) + new_profile = makeSLDProfileXY( + layers[0, 1], # Bulk In + layers[-1, 1], # Bulk Out + data.subRoughs[i], # roughness + layer, + len(layer), + 1.0, + ) + + sld_plot.plot( + [row[0] - 49 for row in new_profile], + [row[1] for row in new_profile], + color=color, + linewidth=1, + ) # Format the axis ref_plot.set_yscale("log") @@ -197,8 +176,11 @@ def plot_ref_sld_helper(data: PlotEventData, fig: Optional[Figure] = None, delay return fig -def plot_ref_sld(project: RAT.Project, results: Union[RAT.outputs.Results, RAT.outputs.BayesResults], - block: bool = False): +def plot_ref_sld( + project: RAT.Project, + results: Union[RAT.outputs.Results, RAT.outputs.BayesResults], + block: bool = False, +): """Plots the reflectivity and SLD profiles. Parameters diff --git a/RAT/wrappers.py b/RAT/wrappers.py index b10d014f..3a7b72e9 100644 --- a/RAT/wrappers.py +++ b/RAT/wrappers.py @@ -41,19 +41,27 @@ def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tup The wrapper function for the MATLAB callback """ + def handle(params, bulk_in, bulk_out, contrast, domain=-1): if domain == -1: - output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, "float"), - np.array(bulk_in, "float"), - np.array(bulk_out, "float"), - float(contrast + 1), nargout=2) + output, sub_rough = getattr(self.engine, self.function_name)( + np.array(params, "float"), + np.array(bulk_in, "float"), + np.array(bulk_out, "float"), + float(contrast + 1), + nargout=2, + ) else: - output, sub_rough = getattr(self.engine, self.function_name)(np.array(params, "float"), - np.array(bulk_in, "float"), - np.array(bulk_out, "float"), - float(contrast + 1), float(domain + 1), - nargout=2) + output, sub_rough = getattr(self.engine, self.function_name)( + np.array(params, "float"), + np.array(bulk_in, "float"), + np.array(bulk_out, "float"), + float(contrast + 1), + float(domain + 1), + nargout=2, + ) return output, sub_rough + return handle @@ -81,10 +89,12 @@ def getHandle(self) -> Callable[[ArrayLike, ArrayLike, ArrayLike, int, int], Tup The wrapper function for the dynamic library callback """ + def handle(params, bulk_in, bulk_out, contrast, domain=-1): if domain == -1: output, sub_rough = self.engine.invoke(params, bulk_in, bulk_out, contrast) else: output, sub_rough = self.engine.invoke(params, bulk_in, bulk_out, contrast, domain) return output, sub_rough + return handle diff --git a/pyproject.toml b/pyproject.toml index a1f35ffa..a00ed151 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -11,7 +11,8 @@ line-length = 120 [tool.ruff.lint] select = ["E", "F", "UP", "B", "SIM", "I", "COM"] -ignore = ["SIM108"] +ignore = ["SIM108", "S101", "COM812"] [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false +mark-parentheses = false diff --git a/setup.py b/setup.py index a9fc1f3b..48f7f55e 100644 --- a/setup.py +++ b/setup.py @@ -11,8 +11,7 @@ __version__ = "0.0.0" -libevent = ("eventManager", {"sources": ["cpp/RAT/events/eventManager.cpp"], - "include_dirs": ["cpp/RAT/events/"]}) +libevent = ("eventManager", {"sources": ["cpp/RAT/events/eventManager.cpp"], "include_dirs": ["cpp/RAT/events/"]}) ext_modules = [ @@ -35,6 +34,7 @@ def has_flag(compiler, flagname): import tempfile from setuptools.errors import CompileError + with tempfile.NamedTemporaryFile("w", suffix=".cpp") as f: f.write("int main (int argc, char **argv) { return 0; }") try: @@ -112,8 +112,7 @@ def initialize_options(self): def build_libraries(self, libraries): # bug in distutils: flag not valid for c++ flag = "-Wstrict-prototypes" - if (hasattr(self.compiler, "compiler_so") - and flag in self.compiler.compiler_so): + if hasattr(self.compiler, "compiler_so") and flag in self.compiler.compiler_so: self.compiler.compiler_so.remove(flag) compiler_type = self.compiler.compiler_type @@ -122,27 +121,27 @@ def build_libraries(self, libraries): else: compile_args = ["-std=c++11", "-fPIC"] - for (lib_name, build_info) in libraries: + for lib_name, build_info in libraries: build_info["cflags"] = compile_args macros = build_info.get("macros") include_dirs = build_info.get("include_dirs") cflags = build_info.get("cflags") sources = list(build_info.get("sources")) objects = self.compiler.compile( - sources, - output_dir=self.build_temp, - macros=macros, - include_dirs=include_dirs, - extra_postargs=cflags, - debug=self.debug, - ) + sources, + output_dir=self.build_temp, + macros=macros, + include_dirs=include_dirs, + extra_postargs=cflags, + debug=self.debug, + ) language = self.compiler.detect_language(sources) self.compiler.link_shared_object( objects, get_shared_object_name(lib_name), output_dir=self.build_clib, target_lang=language, - ) + ) super().build_libraries(libraries) @@ -154,8 +153,8 @@ def build_libraries(self, libraries): author_email="", url="https://github.com/RascalSoftware/python-RAT", description="Python extension for the Reflectivity Analysis Toolbox (RAT)", - long_description = open("README.md").read(), - long_description_content_type = "text/markdown", + long_description=open("README.md").read(), + long_description_content_type="text/markdown", packages=find_packages(), include_package_data=True, package_data={"": [get_shared_object_name(libevent[0])], "RAT.examples": ["data/*.dat"]}, @@ -164,14 +163,16 @@ def build_libraries(self, libraries): ext_modules=ext_modules, python_requires=">=3.9", install_requires=["numpy >= 1.20", "prettytable >= 3.9.0", "pydantic >= 2.7.2", "matplotlib >= 3.8.3"], - extras_require={':python_version < "3.11"': ["StrEnum >= 0.4.15"], - "Dev": ["pytest>=7.4.0", "pytest-cov>=4.1.0"], - "Matlab_latest": ["matlabengine"], - "Matlab_2023b": ["matlabengine == 23.2.1"], - "Matlab_2023a": ["matlabengine == 9.14.3"], - "Matlab-2022b": ["matlabengine == 9.13.9"], - "Matlab_2022a": ["matlabengine == 9.12.19"], - "Matlab_2021b": ["matlabengine == 9.11.21"], - "Matlab_2021a": ["matlabengine == 9.10.3"]}, + extras_require={ + ':python_version < "3.11"': ["StrEnum >= 0.4.15"], + "Dev": ["pytest>=7.4.0", "pytest-cov>=4.1.0"], + "Matlab_latest": ["matlabengine"], + "Matlab_2023b": ["matlabengine == 23.2.1"], + "Matlab_2023a": ["matlabengine == 9.14.3"], + "Matlab-2022b": ["matlabengine == 9.13.9"], + "Matlab_2022a": ["matlabengine == 9.12.19"], + "Matlab_2021b": ["matlabengine == 9.11.21"], + "Matlab_2021a": ["matlabengine == 9.10.3"], + }, zip_safe=False, ) diff --git a/tests/conftest.py b/tests/conftest.py index a0278214..60dad927 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -10,198 +10,254 @@ def reflectivity_calculation_output_results(): """The C++ results object for a reflectivity calculation of the project set out in "DSPC_standard_layers.py".""" results = RAT.rat_core.OutputResult() - results.reflectivity = [np.array([[1.14030000e-02, 1.00000223e+00], - [1.38610000e-02, 1.00000223e+00], - [1.68480000e-02, 6.42692703e-02], - [2.04790000e-02, 1.69530247e-02], - [2.48920000e-02, 6.18815370e-03], - [3.02560000e-02, 2.99474163e-03], - [3.67770000e-02, 1.73558265e-03], - [4.47020000e-02, 1.03290554e-03], - [5.43360000e-02, 6.45645642e-04], - [6.60450000e-02, 5.57949197e-04], - [8.02790000e-02, 5.58974858e-04], - [9.75790000e-02, 3.49803750e-04], - [1.18610000e-01, 8.89485853e-05], - [1.44170000e-01, 4.04713900e-05], - [1.75240000e-01, 6.98590391e-06], - [2.13000000e-01, 3.60410795e-06], - [2.58910000e-01, 2.41835671e-06], - [3.14700000e-01, 2.26496635e-06], - [3.82520000e-01, 2.26396064e-06], - [4.64960000e-01, 2.24609517e-06], - [5.65160000e-01, 2.23162115e-06]]), - np.array([[1.14030000e-02, 1.53247324e-02], - [1.38610000e-02, 9.89602164e-03], - [1.68480000e-02, 6.26868663e-03], - [2.04790000e-02, 3.83918877e-03], - [2.48920000e-02, 2.21818017e-03], - [3.02560000e-02, 1.15473774e-03], - [3.67770000e-02, 4.92469653e-04], - [4.47020000e-02, 1.37287345e-04], - [5.43360000e-02, 2.28561535e-05], - [6.60450000e-02, 6.63620782e-05], - [8.02790000e-02, 1.36790069e-04], - [9.75790000e-02, 1.15354200e-04], - [1.18610000e-01, 3.73063683e-05], - [1.44170000e-01, 1.21263573e-05], - [1.75240000e-01, 6.59103452e-06], - [2.13000000e-01, 4.38321947e-06], - [2.58910000e-01, 3.40797246e-06], - [3.14700000e-01, 3.41625063e-06], - [3.82520000e-01, 3.40076933e-06], - [4.64960000e-01, 3.38892224e-06], - [5.65160000e-01, 3.38103236e-06]])] - results.simulation = [np.array([[1.14030000e-02, 1.00000223e+00], - [1.38610000e-02, 1.00000223e+00], - [1.68480000e-02, 6.42692703e-02], - [2.04790000e-02, 1.69530247e-02], - [2.48920000e-02, 6.18815370e-03], - [3.02560000e-02, 2.99474163e-03], - [3.67770000e-02, 1.73558265e-03], - [4.47020000e-02, 1.03290554e-03], - [5.43360000e-02, 6.45645642e-04], - [6.60450000e-02, 5.57949197e-04], - [8.02790000e-02, 5.58974858e-04], - [9.75790000e-02, 3.49803750e-04], - [1.18610000e-01, 8.89485853e-05], - [1.44170000e-01, 4.04713900e-05], - [1.75240000e-01, 6.98590391e-06], - [2.13000000e-01, 3.60410795e-06], - [2.58910000e-01, 2.41835671e-06], - [3.14700000e-01, 2.26496635e-06], - [3.82520000e-01, 2.26396064e-06], - [4.64960000e-01, 2.24609517e-06], - [5.65160000e-01, 2.23162115e-06]]), - np.array([[1.14030000e-02, 1.53247324e-02], - [1.38610000e-02, 9.89602164e-03], - [1.68480000e-02, 6.26868663e-03], - [2.04790000e-02, 3.83918877e-03], - [2.48920000e-02, 2.21818017e-03], - [3.02560000e-02, 1.15473774e-03], - [3.67770000e-02, 4.92469653e-04], - [4.47020000e-02, 1.37287345e-04], - [5.43360000e-02, 2.28561535e-05], - [6.60450000e-02, 6.63620782e-05], - [8.02790000e-02, 1.36790069e-04], - [9.75790000e-02, 1.15354200e-04], - [1.18610000e-01, 3.73063683e-05], - [1.44170000e-01, 1.21263573e-05], - [1.75240000e-01, 6.59103452e-06], - [2.13000000e-01, 4.38321947e-06], - [2.58910000e-01, 3.40797246e-06], - [3.14700000e-01, 3.41625063e-06], - [3.82520000e-01, 3.40076933e-06], - [4.64960000e-01, 3.38892224e-06], - [5.65160000e-01, 3.38103236e-06]])] - results.shiftedData = [np.array([[1.1403e-02, 1.0063e+00, 1.9003e-02], - [1.3861e-02, 9.0118e-01, 1.1774e-02], - [1.6848e-02, 7.0455e-02, 1.3083e-03], - [2.0479e-02, 1.7544e-02, 5.1254e-04], - [2.4892e-02, 6.4257e-03, 2.6236e-04], - [3.0256e-02, 2.7746e-03, 8.5758e-05], - [3.6777e-02, 1.8591e-03, 4.9391e-05], - [4.4702e-02, 1.1002e-03, 3.2644e-05], - [5.4336e-02, 6.6691e-04, 2.1365e-05], - [6.6045e-02, 6.0729e-04, 1.1791e-05], - [8.0279e-02, 5.8755e-04, 1.5569e-05], - [9.7579e-02, 3.2700e-04, 6.5280e-06], - [1.1861e-01, 7.8205e-05, 2.5881e-06], - [1.4417e-01, 3.3455e-05, 1.5143e-06], - [1.7524e-01, 4.9313e-06, 5.6663e-07], - [2.1300e-01, 4.1948e-06, 6.8549e-07], - [2.5891e-01, 3.9863e-06, 8.2061e-07], - [3.1470e-01, 2.0861e-06, 6.1379e-07], - [3.8252e-01, 2.1154e-06, 6.3084e-07], - [4.6496e-01, 1.9906e-06, 6.0793e-07], - [5.6516e-01, 2.3816e-06, 7.0610e-07]]), - np.array([[1.14030000e-02, 1.20493333e-02, 7.32200000e-04], - [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], - [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], - [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], - [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], - [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], - [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], - [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], - [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], - [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], - [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], - [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], - [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], - [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], - [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], - [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], - [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], - [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], - [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], - [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], - [5.65160000e-01, 2.17460000e-06, 5.31346667e-07]])] - results.layerSlds = [[np.array([[1.954000e+01, 4.001499e-06, 3.000000e+00], - [2.266000e+01, -6.586988e-08, 3.000000e+00], - [8.560000e+00, 3.672535e-06, 5.640000e+00], - [1.712000e+01, 5.980000e-06, 5.640000e+00], - [1.070000e+01, 3.100365e-06, 6.014000e+00], - [1.782000e+01, 6.751924e-07, 6.014000e+00], - [1.782000e+01, 6.751924e-07, 6.014000e+00], - [1.070000e+01, 3.100365e-06, 6.014000e+00]])], - [np.array([[1.9540000e+01, 3.1114020e-06, 3.0000000e+00], - [2.2660000e+01, -2.6387028e-07, 3.0000000e+00], - [8.5600000e+00, 1.9590700e-06, 5.6400000e+00], - [1.7120000e+01, 2.2100000e-06, 5.6400000e+00], - [1.0700000e+01, 1.7375100e-06, 6.0140000e+00], - [1.7820000e+01, 1.0164400e-08, 6.0140000e+00], - [1.7820000e+01, 1.0164400e-08, 6.0140000e+00], - [1.0700000e+01, 1.7375100e-06, 6.0140000e+00]])]] - results.sldProfiles = [[np.array([[0.00000000e+00, 2.07300000e-06], - [1.10000000e+01, 2.07300000e-06], - [2.20000000e+01, 2.07300000e-06], - [3.30000000e+01, 2.07300001e-06], - [4.40000000e+01, 2.11687361e-06], - [5.50000000e+01, 3.90933280e-06], - [6.60000000e+01, 3.51748792e-06], - [7.70000000e+01, -2.64615370e-08], - [8.80000000e+01, 8.14665196e-07], - [9.90000000e+01, 4.11508879e-06], - [1.10000000e+02, 5.58392432e-06], - [1.21000000e+02, 3.71785214e-06], - [1.32000000e+02, 1.39304140e-06], - [1.43000000e+02, 6.95745588e-07], - [1.54000000e+02, 7.84170141e-07], - [1.65000000e+02, 2.15552213e-06], - [1.76000000e+02, 4.68458332e-06], - [1.87000000e+02, 5.91563638e-06], - [1.98000000e+02, 5.97982117e-06], - [2.09000000e+02, 5.97999998e-06], - [2.20000000e+02, 5.98000000e-06], - [2.31000000e+02, 5.98000000e-06], - [2.42000000e+02, 5.98000000e-06], - [2.53000000e+02, 5.98000000e-06], - [2.64000000e+02, 5.98000000e-06]]), - np.array([[0.00000000e+00, 2.07300000e-06], - [1.10000000e+01, 2.07300000e-06], - [2.20000000e+01, 2.07300000e-06], - [3.30000000e+01, 2.07300001e-06], - [4.40000000e+01, 2.09662378e-06], - [5.50000000e+01, 3.06177428e-06], - [6.60000000e+01, 2.70974796e-06], - [7.70000000e+01, -2.34283042e-07], - [8.80000000e+01, 2.46446429e-07], - [9.90000000e+01, 1.80004279e-06], - [1.10000000e+02, 2.14886270e-06], - [1.21000000e+02, 1.70090192e-06], - [1.32000000e+02, 5.06554250e-07], - [1.43000000e+02, 2.47801381e-08], - [1.54000000e+02, 8.73866316e-08], - [1.65000000e+02, 9.86362863e-07], - [1.76000000e+02, 1.96411923e-06], - [1.87000000e+02, 2.19933821e-06], - [1.98000000e+02, 2.20997064e-06], - [2.09000000e+02, 2.21000000e-06], - [2.20000000e+02, 2.21000000e-06], - [2.31000000e+02, 2.21000000e-06], - [2.42000000e+02, 2.21000000e-06], - [2.53000000e+02, 2.21000000e-06], - [2.64000000e+02, 2.21000000e-06]])]] + results.reflectivity = [ + np.array( + [ + [1.14030000e-02, 1.00000223e00], + [1.38610000e-02, 1.00000223e00], + [1.68480000e-02, 6.42692703e-02], + [2.04790000e-02, 1.69530247e-02], + [2.48920000e-02, 6.18815370e-03], + [3.02560000e-02, 2.99474163e-03], + [3.67770000e-02, 1.73558265e-03], + [4.47020000e-02, 1.03290554e-03], + [5.43360000e-02, 6.45645642e-04], + [6.60450000e-02, 5.57949197e-04], + [8.02790000e-02, 5.58974858e-04], + [9.75790000e-02, 3.49803750e-04], + [1.18610000e-01, 8.89485853e-05], + [1.44170000e-01, 4.04713900e-05], + [1.75240000e-01, 6.98590391e-06], + [2.13000000e-01, 3.60410795e-06], + [2.58910000e-01, 2.41835671e-06], + [3.14700000e-01, 2.26496635e-06], + [3.82520000e-01, 2.26396064e-06], + [4.64960000e-01, 2.24609517e-06], + [5.65160000e-01, 2.23162115e-06], + ], + ), + np.array( + [ + [1.14030000e-02, 1.53247324e-02], + [1.38610000e-02, 9.89602164e-03], + [1.68480000e-02, 6.26868663e-03], + [2.04790000e-02, 3.83918877e-03], + [2.48920000e-02, 2.21818017e-03], + [3.02560000e-02, 1.15473774e-03], + [3.67770000e-02, 4.92469653e-04], + [4.47020000e-02, 1.37287345e-04], + [5.43360000e-02, 2.28561535e-05], + [6.60450000e-02, 6.63620782e-05], + [8.02790000e-02, 1.36790069e-04], + [9.75790000e-02, 1.15354200e-04], + [1.18610000e-01, 3.73063683e-05], + [1.44170000e-01, 1.21263573e-05], + [1.75240000e-01, 6.59103452e-06], + [2.13000000e-01, 4.38321947e-06], + [2.58910000e-01, 3.40797246e-06], + [3.14700000e-01, 3.41625063e-06], + [3.82520000e-01, 3.40076933e-06], + [4.64960000e-01, 3.38892224e-06], + [5.65160000e-01, 3.38103236e-06], + ], + ), + ] + results.simulation = [ + np.array( + [ + [1.14030000e-02, 1.00000223e00], + [1.38610000e-02, 1.00000223e00], + [1.68480000e-02, 6.42692703e-02], + [2.04790000e-02, 1.69530247e-02], + [2.48920000e-02, 6.18815370e-03], + [3.02560000e-02, 2.99474163e-03], + [3.67770000e-02, 1.73558265e-03], + [4.47020000e-02, 1.03290554e-03], + [5.43360000e-02, 6.45645642e-04], + [6.60450000e-02, 5.57949197e-04], + [8.02790000e-02, 5.58974858e-04], + [9.75790000e-02, 3.49803750e-04], + [1.18610000e-01, 8.89485853e-05], + [1.44170000e-01, 4.04713900e-05], + [1.75240000e-01, 6.98590391e-06], + [2.13000000e-01, 3.60410795e-06], + [2.58910000e-01, 2.41835671e-06], + [3.14700000e-01, 2.26496635e-06], + [3.82520000e-01, 2.26396064e-06], + [4.64960000e-01, 2.24609517e-06], + [5.65160000e-01, 2.23162115e-06], + ], + ), + np.array( + [ + [1.14030000e-02, 1.53247324e-02], + [1.38610000e-02, 9.89602164e-03], + [1.68480000e-02, 6.26868663e-03], + [2.04790000e-02, 3.83918877e-03], + [2.48920000e-02, 2.21818017e-03], + [3.02560000e-02, 1.15473774e-03], + [3.67770000e-02, 4.92469653e-04], + [4.47020000e-02, 1.37287345e-04], + [5.43360000e-02, 2.28561535e-05], + [6.60450000e-02, 6.63620782e-05], + [8.02790000e-02, 1.36790069e-04], + [9.75790000e-02, 1.15354200e-04], + [1.18610000e-01, 3.73063683e-05], + [1.44170000e-01, 1.21263573e-05], + [1.75240000e-01, 6.59103452e-06], + [2.13000000e-01, 4.38321947e-06], + [2.58910000e-01, 3.40797246e-06], + [3.14700000e-01, 3.41625063e-06], + [3.82520000e-01, 3.40076933e-06], + [4.64960000e-01, 3.38892224e-06], + [5.65160000e-01, 3.38103236e-06], + ], + ), + ] + results.shiftedData = [ + np.array( + [ + [1.1403e-02, 1.0063e00, 1.9003e-02], + [1.3861e-02, 9.0118e-01, 1.1774e-02], + [1.6848e-02, 7.0455e-02, 1.3083e-03], + [2.0479e-02, 1.7544e-02, 5.1254e-04], + [2.4892e-02, 6.4257e-03, 2.6236e-04], + [3.0256e-02, 2.7746e-03, 8.5758e-05], + [3.6777e-02, 1.8591e-03, 4.9391e-05], + [4.4702e-02, 1.1002e-03, 3.2644e-05], + [5.4336e-02, 6.6691e-04, 2.1365e-05], + [6.6045e-02, 6.0729e-04, 1.1791e-05], + [8.0279e-02, 5.8755e-04, 1.5569e-05], + [9.7579e-02, 3.2700e-04, 6.5280e-06], + [1.1861e-01, 7.8205e-05, 2.5881e-06], + [1.4417e-01, 3.3455e-05, 1.5143e-06], + [1.7524e-01, 4.9313e-06, 5.6663e-07], + [2.1300e-01, 4.1948e-06, 6.8549e-07], + [2.5891e-01, 3.9863e-06, 8.2061e-07], + [3.1470e-01, 2.0861e-06, 6.1379e-07], + [3.8252e-01, 2.1154e-06, 6.3084e-07], + [4.6496e-01, 1.9906e-06, 6.0793e-07], + [5.6516e-01, 2.3816e-06, 7.0610e-07], + ], + ), + np.array( + [ + [1.14030000e-02, 1.20493333e-02, 7.32200000e-04], + [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], + [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], + [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], + [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], + [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], + [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], + [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], + [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], + [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], + [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], + [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], + [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], + [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], + [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], + [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], + [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], + [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], + [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], + [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], + [5.65160000e-01, 2.17460000e-06, 5.31346667e-07], + ], + ), + ] + results.layerSlds = [ + [ + np.array( + [ + [1.954000e01, 4.001499e-06, 3.000000e00], + [2.266000e01, -6.586988e-08, 3.000000e00], + [8.560000e00, 3.672535e-06, 5.640000e00], + [1.712000e01, 5.980000e-06, 5.640000e00], + [1.070000e01, 3.100365e-06, 6.014000e00], + [1.782000e01, 6.751924e-07, 6.014000e00], + [1.782000e01, 6.751924e-07, 6.014000e00], + [1.070000e01, 3.100365e-06, 6.014000e00], + ], + ), + ], + [ + np.array( + [ + [1.9540000e01, 3.1114020e-06, 3.0000000e00], + [2.2660000e01, -2.6387028e-07, 3.0000000e00], + [8.5600000e00, 1.9590700e-06, 5.6400000e00], + [1.7120000e01, 2.2100000e-06, 5.6400000e00], + [1.0700000e01, 1.7375100e-06, 6.0140000e00], + [1.7820000e01, 1.0164400e-08, 6.0140000e00], + [1.7820000e01, 1.0164400e-08, 6.0140000e00], + [1.0700000e01, 1.7375100e-06, 6.0140000e00], + ], + ), + ], + ] + results.sldProfiles = [ + [ + np.array( + [ + [0.00000000e00, 2.07300000e-06], + [1.10000000e01, 2.07300000e-06], + [2.20000000e01, 2.07300000e-06], + [3.30000000e01, 2.07300001e-06], + [4.40000000e01, 2.11687361e-06], + [5.50000000e01, 3.90933280e-06], + [6.60000000e01, 3.51748792e-06], + [7.70000000e01, -2.64615370e-08], + [8.80000000e01, 8.14665196e-07], + [9.90000000e01, 4.11508879e-06], + [1.10000000e02, 5.58392432e-06], + [1.21000000e02, 3.71785214e-06], + [1.32000000e02, 1.39304140e-06], + [1.43000000e02, 6.95745588e-07], + [1.54000000e02, 7.84170141e-07], + [1.65000000e02, 2.15552213e-06], + [1.76000000e02, 4.68458332e-06], + [1.87000000e02, 5.91563638e-06], + [1.98000000e02, 5.97982117e-06], + [2.09000000e02, 5.97999998e-06], + [2.20000000e02, 5.98000000e-06], + [2.31000000e02, 5.98000000e-06], + [2.42000000e02, 5.98000000e-06], + [2.53000000e02, 5.98000000e-06], + [2.64000000e02, 5.98000000e-06], + ], + ), + np.array( + [ + [0.00000000e00, 2.07300000e-06], + [1.10000000e01, 2.07300000e-06], + [2.20000000e01, 2.07300000e-06], + [3.30000000e01, 2.07300001e-06], + [4.40000000e01, 2.09662378e-06], + [5.50000000e01, 3.06177428e-06], + [6.60000000e01, 2.70974796e-06], + [7.70000000e01, -2.34283042e-07], + [8.80000000e01, 2.46446429e-07], + [9.90000000e01, 1.80004279e-06], + [1.10000000e02, 2.14886270e-06], + [1.21000000e02, 1.70090192e-06], + [1.32000000e02, 5.06554250e-07], + [1.43000000e02, 2.47801381e-08], + [1.54000000e02, 8.73866316e-08], + [1.65000000e02, 9.86362863e-07], + [1.76000000e02, 1.96411923e-06], + [1.87000000e02, 2.19933821e-06], + [1.98000000e02, 2.20997064e-06], + [2.09000000e02, 2.21000000e-06], + [2.20000000e02, 2.21000000e-06], + [2.31000000e02, 2.21000000e-06], + [2.42000000e02, 2.21000000e-06], + [2.53000000e02, 2.21000000e-06], + [2.64000000e02, 2.21000000e-06], + ], + ), + ], + ] results.resampledLayers = [[np.array([[0.0, 0.0, 0.0]])], [np.array([[0.0, 0.0, 0.0]])]] results.calculationResults = RAT.rat_core.Calculation() results.calculationResults.chiValues = np.array([202.83057377, 1641.4024969]) @@ -214,14 +270,48 @@ def reflectivity_calculation_output_results(): results.contrastParams.resolutionParams = np.array([0.03, 0.03]) results.contrastParams.subRoughs = np.array([3.0, 3.0]) results.contrastParams.resample = np.array([0.0, 0.0]) - results.fitParams = np.array([3.000e+00, 1.954e+01, 2.266e+01, 5.252e+00, 5.640e+00, 1.712e+01, 8.560e+00, - 4.545e+01, 1.070e+01, 6.014e+00, 1.782e+01, 1.764e+01, 3.615e+01, 2.361e+01, - 2.230e-06, 3.380e-06, 5.980e-06, 2.210e-06]) - results.fitNames = ["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", - "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", - "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", - "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", - "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"] + results.fitParams = np.array( + [ + 3.000e00, + 1.954e01, + 2.266e01, + 5.252e00, + 5.640e00, + 1.712e01, + 8.560e00, + 4.545e01, + 1.070e01, + 6.014e00, + 1.782e01, + 1.764e01, + 3.615e01, + 2.361e01, + 2.230e-06, + 3.380e-06, + 5.980e-06, + 2.210e-06, + ], + ) + results.fitNames = [ + "Substrate Roughness", + "Oxide Thickness", + "SAM Tails Thickness", + "SAM Tails Hydration", + "SAM Roughness", + "CW Thickness", + "SAM Heads Thickness", + "SAM Heads Hydration", + "Bilayer Heads Thickness", + "Bilayer Roughness", + "Bilayer Tails Thickness", + "Bilayer Tails Hydration", + "Bilayer Heads Hydration", + "Oxide Hydration", + "Background parameter D2O", + "Background parameter SMW", + "D2O", + "SMW", + ] return results @@ -230,216 +320,310 @@ def reflectivity_calculation_output_results(): def reflectivity_calculation_results(): """The python results object for a reflectivity calculation of the project set out in "DSPC_standard_layers.py".""" return RAT.outputs.Results( - reflectivity=[np.array([[1.14030000e-02, 1.00000223e+00], - [1.38610000e-02, 1.00000223e+00], - [1.68480000e-02, 6.42692703e-02], - [2.04790000e-02, 1.69530247e-02], - [2.48920000e-02, 6.18815370e-03], - [3.02560000e-02, 2.99474163e-03], - [3.67770000e-02, 1.73558265e-03], - [4.47020000e-02, 1.03290554e-03], - [5.43360000e-02, 6.45645642e-04], - [6.60450000e-02, 5.57949197e-04], - [8.02790000e-02, 5.58974858e-04], - [9.75790000e-02, 3.49803750e-04], - [1.18610000e-01, 8.89485853e-05], - [1.44170000e-01, 4.04713900e-05], - [1.75240000e-01, 6.98590391e-06], - [2.13000000e-01, 3.60410795e-06], - [2.58910000e-01, 2.41835671e-06], - [3.14700000e-01, 2.26496635e-06], - [3.82520000e-01, 2.26396064e-06], - [4.64960000e-01, 2.24609517e-06], - [5.65160000e-01, 2.23162115e-06]]), - np.array([[1.14030000e-02, 1.53247324e-02], - [1.38610000e-02, 9.89602164e-03], - [1.68480000e-02, 6.26868663e-03], - [2.04790000e-02, 3.83918877e-03], - [2.48920000e-02, 2.21818017e-03], - [3.02560000e-02, 1.15473774e-03], - [3.67770000e-02, 4.92469653e-04], - [4.47020000e-02, 1.37287345e-04], - [5.43360000e-02, 2.28561535e-05], - [6.60450000e-02, 6.63620782e-05], - [8.02790000e-02, 1.36790069e-04], - [9.75790000e-02, 1.15354200e-04], - [1.18610000e-01, 3.73063683e-05], - [1.44170000e-01, 1.21263573e-05], - [1.75240000e-01, 6.59103452e-06], - [2.13000000e-01, 4.38321947e-06], - [2.58910000e-01, 3.40797246e-06], - [3.14700000e-01, 3.41625063e-06], - [3.82520000e-01, 3.40076933e-06], - [4.64960000e-01, 3.38892224e-06], - [5.65160000e-01, 3.38103236e-06]])], - simulation=[np.array([[1.14030000e-02, 1.00000223e+00], - [1.38610000e-02, 1.00000223e+00], - [1.68480000e-02, 6.42692703e-02], - [2.04790000e-02, 1.69530247e-02], - [2.48920000e-02, 6.18815370e-03], - [3.02560000e-02, 2.99474163e-03], - [3.67770000e-02, 1.73558265e-03], - [4.47020000e-02, 1.03290554e-03], - [5.43360000e-02, 6.45645642e-04], - [6.60450000e-02, 5.57949197e-04], - [8.02790000e-02, 5.58974858e-04], - [9.75790000e-02, 3.49803750e-04], - [1.18610000e-01, 8.89485853e-05], - [1.44170000e-01, 4.04713900e-05], - [1.75240000e-01, 6.98590391e-06], - [2.13000000e-01, 3.60410795e-06], - [2.58910000e-01, 2.41835671e-06], - [3.14700000e-01, 2.26496635e-06], - [3.82520000e-01, 2.26396064e-06], - [4.64960000e-01, 2.24609517e-06], - [5.65160000e-01, 2.23162115e-06]]), - np.array([[1.14030000e-02, 1.53247324e-02], - [1.38610000e-02, 9.89602164e-03], - [1.68480000e-02, 6.26868663e-03], - [2.04790000e-02, 3.83918877e-03], - [2.48920000e-02, 2.21818017e-03], - [3.02560000e-02, 1.15473774e-03], - [3.67770000e-02, 4.92469653e-04], - [4.47020000e-02, 1.37287345e-04], - [5.43360000e-02, 2.28561535e-05], - [6.60450000e-02, 6.63620782e-05], - [8.02790000e-02, 1.36790069e-04], - [9.75790000e-02, 1.15354200e-04], - [1.18610000e-01, 3.73063683e-05], - [1.44170000e-01, 1.21263573e-05], - [1.75240000e-01, 6.59103452e-06], - [2.13000000e-01, 4.38321947e-06], - [2.58910000e-01, 3.40797246e-06], - [3.14700000e-01, 3.41625063e-06], - [3.82520000e-01, 3.40076933e-06], - [4.64960000e-01, 3.38892224e-06], - [5.65160000e-01, 3.38103236e-06]])], - shiftedData=[np.array([[1.1403e-02, 1.0063e+00, 1.9003e-02], - [1.3861e-02, 9.0118e-01, 1.1774e-02], - [1.6848e-02, 7.0455e-02, 1.3083e-03], - [2.0479e-02, 1.7544e-02, 5.1254e-04], - [2.4892e-02, 6.4257e-03, 2.6236e-04], - [3.0256e-02, 2.7746e-03, 8.5758e-05], - [3.6777e-02, 1.8591e-03, 4.9391e-05], - [4.4702e-02, 1.1002e-03, 3.2644e-05], - [5.4336e-02, 6.6691e-04, 2.1365e-05], - [6.6045e-02, 6.0729e-04, 1.1791e-05], - [8.0279e-02, 5.8755e-04, 1.5569e-05], - [9.7579e-02, 3.2700e-04, 6.5280e-06], - [1.1861e-01, 7.8205e-05, 2.5881e-06], - [1.4417e-01, 3.3455e-05, 1.5143e-06], - [1.7524e-01, 4.9313e-06, 5.6663e-07], - [2.1300e-01, 4.1948e-06, 6.8549e-07], - [2.5891e-01, 3.9863e-06, 8.2061e-07], - [3.1470e-01, 2.0861e-06, 6.1379e-07], - [3.8252e-01, 2.1154e-06, 6.3084e-07], - [4.6496e-01, 1.9906e-06, 6.0793e-07], - [5.6516e-01, 2.3816e-06, 7.0610e-07]]), - np.array([[1.14030000e-02, 1.20493333e-02, 7.32200000e-04], - [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], - [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], - [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], - [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], - [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], - [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], - [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], - [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], - [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], - [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], - [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], - [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], - [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], - [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], - [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], - [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], - [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], - [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], - [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], - [5.65160000e-01, 2.17460000e-06, 5.31346667e-07]])], - layerSlds=[[np.array([[1.954000e+01, 4.001499e-06, 3.000000e+00], - [2.266000e+01, -6.586988e-08, 3.000000e+00], - [8.560000e+00, 3.672535e-06, 5.640000e+00], - [1.712000e+01, 5.980000e-06, 5.640000e+00], - [1.070000e+01, 3.100365e-06, 6.014000e+00], - [1.782000e+01, 6.751924e-07, 6.014000e+00], - [1.782000e+01, 6.751924e-07, 6.014000e+00], - [1.070000e+01, 3.100365e-06, 6.014000e+00]])], - [np.array([[1.9540000e+01, 3.1114020e-06, 3.0000000e+00], - [2.2660000e+01, -2.6387028e-07, 3.0000000e+00], - [8.5600000e+00, 1.9590700e-06, 5.6400000e+00], - [1.7120000e+01, 2.2100000e-06, 5.6400000e+00], - [1.0700000e+01, 1.7375100e-06, 6.0140000e+00], - [1.7820000e+01, 1.0164400e-08, 6.0140000e+00], - [1.7820000e+01, 1.0164400e-08, 6.0140000e+00], - [1.0700000e+01, 1.7375100e-06, 6.0140000e+00]])]], - sldProfiles=[[np.array([[0.00000000e+00, 2.07300000e-06], - [1.10000000e+01, 2.07300000e-06], - [2.20000000e+01, 2.07300000e-06], - [3.30000000e+01, 2.07300001e-06], - [4.40000000e+01, 2.11687361e-06], - [5.50000000e+01, 3.90933280e-06], - [6.60000000e+01, 3.51748792e-06], - [7.70000000e+01, -2.64615370e-08], - [8.80000000e+01, 8.14665196e-07], - [9.90000000e+01, 4.11508879e-06], - [1.10000000e+02, 5.58392432e-06], - [1.21000000e+02, 3.71785214e-06], - [1.32000000e+02, 1.39304140e-06], - [1.43000000e+02, 6.95745588e-07], - [1.54000000e+02, 7.84170141e-07], - [1.65000000e+02, 2.15552213e-06], - [1.76000000e+02, 4.68458332e-06], - [1.87000000e+02, 5.91563638e-06], - [1.98000000e+02, 5.97982117e-06], - [2.09000000e+02, 5.97999998e-06], - [2.20000000e+02, 5.98000000e-06], - [2.31000000e+02, 5.98000000e-06], - [2.42000000e+02, 5.98000000e-06], - [2.53000000e+02, 5.98000000e-06], - [2.64000000e+02, 5.98000000e-06]]), - np.array([[0.00000000e+00, 2.07300000e-06], - [1.10000000e+01, 2.07300000e-06], - [2.20000000e+01, 2.07300000e-06], - [3.30000000e+01, 2.07300001e-06], - [4.40000000e+01, 2.09662378e-06], - [5.50000000e+01, 3.06177428e-06], - [6.60000000e+01, 2.70974796e-06], - [7.70000000e+01, -2.34283042e-07], - [8.80000000e+01, 2.46446429e-07], - [9.90000000e+01, 1.80004279e-06], - [1.10000000e+02, 2.14886270e-06], - [1.21000000e+02, 1.70090192e-06], - [1.32000000e+02, 5.06554250e-07], - [1.43000000e+02, 2.47801381e-08], - [1.54000000e+02, 8.73866316e-08], - [1.65000000e+02, 9.86362863e-07], - [1.76000000e+02, 1.96411923e-06], - [1.87000000e+02, 2.19933821e-06], - [1.98000000e+02, 2.20997064e-06], - [2.09000000e+02, 2.21000000e-06], - [2.20000000e+02, 2.21000000e-06], - [2.31000000e+02, 2.21000000e-06], - [2.42000000e+02, 2.21000000e-06], - [2.53000000e+02, 2.21000000e-06], - [2.64000000e+02, 2.21000000e-06]])]], + reflectivity=[ + np.array( + [ + [1.14030000e-02, 1.00000223e00], + [1.38610000e-02, 1.00000223e00], + [1.68480000e-02, 6.42692703e-02], + [2.04790000e-02, 1.69530247e-02], + [2.48920000e-02, 6.18815370e-03], + [3.02560000e-02, 2.99474163e-03], + [3.67770000e-02, 1.73558265e-03], + [4.47020000e-02, 1.03290554e-03], + [5.43360000e-02, 6.45645642e-04], + [6.60450000e-02, 5.57949197e-04], + [8.02790000e-02, 5.58974858e-04], + [9.75790000e-02, 3.49803750e-04], + [1.18610000e-01, 8.89485853e-05], + [1.44170000e-01, 4.04713900e-05], + [1.75240000e-01, 6.98590391e-06], + [2.13000000e-01, 3.60410795e-06], + [2.58910000e-01, 2.41835671e-06], + [3.14700000e-01, 2.26496635e-06], + [3.82520000e-01, 2.26396064e-06], + [4.64960000e-01, 2.24609517e-06], + [5.65160000e-01, 2.23162115e-06], + ], + ), + np.array( + [ + [1.14030000e-02, 1.53247324e-02], + [1.38610000e-02, 9.89602164e-03], + [1.68480000e-02, 6.26868663e-03], + [2.04790000e-02, 3.83918877e-03], + [2.48920000e-02, 2.21818017e-03], + [3.02560000e-02, 1.15473774e-03], + [3.67770000e-02, 4.92469653e-04], + [4.47020000e-02, 1.37287345e-04], + [5.43360000e-02, 2.28561535e-05], + [6.60450000e-02, 6.63620782e-05], + [8.02790000e-02, 1.36790069e-04], + [9.75790000e-02, 1.15354200e-04], + [1.18610000e-01, 3.73063683e-05], + [1.44170000e-01, 1.21263573e-05], + [1.75240000e-01, 6.59103452e-06], + [2.13000000e-01, 4.38321947e-06], + [2.58910000e-01, 3.40797246e-06], + [3.14700000e-01, 3.41625063e-06], + [3.82520000e-01, 3.40076933e-06], + [4.64960000e-01, 3.38892224e-06], + [5.65160000e-01, 3.38103236e-06], + ], + ), + ], + simulation=[ + np.array( + [ + [1.14030000e-02, 1.00000223e00], + [1.38610000e-02, 1.00000223e00], + [1.68480000e-02, 6.42692703e-02], + [2.04790000e-02, 1.69530247e-02], + [2.48920000e-02, 6.18815370e-03], + [3.02560000e-02, 2.99474163e-03], + [3.67770000e-02, 1.73558265e-03], + [4.47020000e-02, 1.03290554e-03], + [5.43360000e-02, 6.45645642e-04], + [6.60450000e-02, 5.57949197e-04], + [8.02790000e-02, 5.58974858e-04], + [9.75790000e-02, 3.49803750e-04], + [1.18610000e-01, 8.89485853e-05], + [1.44170000e-01, 4.04713900e-05], + [1.75240000e-01, 6.98590391e-06], + [2.13000000e-01, 3.60410795e-06], + [2.58910000e-01, 2.41835671e-06], + [3.14700000e-01, 2.26496635e-06], + [3.82520000e-01, 2.26396064e-06], + [4.64960000e-01, 2.24609517e-06], + [5.65160000e-01, 2.23162115e-06], + ], + ), + np.array( + [ + [1.14030000e-02, 1.53247324e-02], + [1.38610000e-02, 9.89602164e-03], + [1.68480000e-02, 6.26868663e-03], + [2.04790000e-02, 3.83918877e-03], + [2.48920000e-02, 2.21818017e-03], + [3.02560000e-02, 1.15473774e-03], + [3.67770000e-02, 4.92469653e-04], + [4.47020000e-02, 1.37287345e-04], + [5.43360000e-02, 2.28561535e-05], + [6.60450000e-02, 6.63620782e-05], + [8.02790000e-02, 1.36790069e-04], + [9.75790000e-02, 1.15354200e-04], + [1.18610000e-01, 3.73063683e-05], + [1.44170000e-01, 1.21263573e-05], + [1.75240000e-01, 6.59103452e-06], + [2.13000000e-01, 4.38321947e-06], + [2.58910000e-01, 3.40797246e-06], + [3.14700000e-01, 3.41625063e-06], + [3.82520000e-01, 3.40076933e-06], + [4.64960000e-01, 3.38892224e-06], + [5.65160000e-01, 3.38103236e-06], + ], + ), + ], + shiftedData=[ + np.array( + [ + [1.1403e-02, 1.0063e00, 1.9003e-02], + [1.3861e-02, 9.0118e-01, 1.1774e-02], + [1.6848e-02, 7.0455e-02, 1.3083e-03], + [2.0479e-02, 1.7544e-02, 5.1254e-04], + [2.4892e-02, 6.4257e-03, 2.6236e-04], + [3.0256e-02, 2.7746e-03, 8.5758e-05], + [3.6777e-02, 1.8591e-03, 4.9391e-05], + [4.4702e-02, 1.1002e-03, 3.2644e-05], + [5.4336e-02, 6.6691e-04, 2.1365e-05], + [6.6045e-02, 6.0729e-04, 1.1791e-05], + [8.0279e-02, 5.8755e-04, 1.5569e-05], + [9.7579e-02, 3.2700e-04, 6.5280e-06], + [1.1861e-01, 7.8205e-05, 2.5881e-06], + [1.4417e-01, 3.3455e-05, 1.5143e-06], + [1.7524e-01, 4.9313e-06, 5.6663e-07], + [2.1300e-01, 4.1948e-06, 6.8549e-07], + [2.5891e-01, 3.9863e-06, 8.2061e-07], + [3.1470e-01, 2.0861e-06, 6.1379e-07], + [3.8252e-01, 2.1154e-06, 6.3084e-07], + [4.6496e-01, 1.9906e-06, 6.0793e-07], + [5.6516e-01, 2.3816e-06, 7.0610e-07], + ], + ), + np.array( + [ + [1.14030000e-02, 1.20493333e-02, 7.32200000e-04], + [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], + [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], + [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], + [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], + [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], + [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], + [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], + [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], + [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], + [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], + [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], + [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], + [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], + [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], + [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], + [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], + [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], + [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], + [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], + [5.65160000e-01, 2.17460000e-06, 5.31346667e-07], + ], + ), + ], + layerSlds=[ + [ + np.array( + [ + [1.954000e01, 4.001499e-06, 3.000000e00], + [2.266000e01, -6.586988e-08, 3.000000e00], + [8.560000e00, 3.672535e-06, 5.640000e00], + [1.712000e01, 5.980000e-06, 5.640000e00], + [1.070000e01, 3.100365e-06, 6.014000e00], + [1.782000e01, 6.751924e-07, 6.014000e00], + [1.782000e01, 6.751924e-07, 6.014000e00], + [1.070000e01, 3.100365e-06, 6.014000e00], + ], + ), + ], + [ + np.array( + [ + [1.9540000e01, 3.1114020e-06, 3.0000000e00], + [2.2660000e01, -2.6387028e-07, 3.0000000e00], + [8.5600000e00, 1.9590700e-06, 5.6400000e00], + [1.7120000e01, 2.2100000e-06, 5.6400000e00], + [1.0700000e01, 1.7375100e-06, 6.0140000e00], + [1.7820000e01, 1.0164400e-08, 6.0140000e00], + [1.7820000e01, 1.0164400e-08, 6.0140000e00], + [1.0700000e01, 1.7375100e-06, 6.0140000e00], + ], + ), + ], + ], + sldProfiles=[ + [ + np.array( + [ + [0.00000000e00, 2.07300000e-06], + [1.10000000e01, 2.07300000e-06], + [2.20000000e01, 2.07300000e-06], + [3.30000000e01, 2.07300001e-06], + [4.40000000e01, 2.11687361e-06], + [5.50000000e01, 3.90933280e-06], + [6.60000000e01, 3.51748792e-06], + [7.70000000e01, -2.64615370e-08], + [8.80000000e01, 8.14665196e-07], + [9.90000000e01, 4.11508879e-06], + [1.10000000e02, 5.58392432e-06], + [1.21000000e02, 3.71785214e-06], + [1.32000000e02, 1.39304140e-06], + [1.43000000e02, 6.95745588e-07], + [1.54000000e02, 7.84170141e-07], + [1.65000000e02, 2.15552213e-06], + [1.76000000e02, 4.68458332e-06], + [1.87000000e02, 5.91563638e-06], + [1.98000000e02, 5.97982117e-06], + [2.09000000e02, 5.97999998e-06], + [2.20000000e02, 5.98000000e-06], + [2.31000000e02, 5.98000000e-06], + [2.42000000e02, 5.98000000e-06], + [2.53000000e02, 5.98000000e-06], + [2.64000000e02, 5.98000000e-06], + ], + ), + np.array( + [ + [0.00000000e00, 2.07300000e-06], + [1.10000000e01, 2.07300000e-06], + [2.20000000e01, 2.07300000e-06], + [3.30000000e01, 2.07300001e-06], + [4.40000000e01, 2.09662378e-06], + [5.50000000e01, 3.06177428e-06], + [6.60000000e01, 2.70974796e-06], + [7.70000000e01, -2.34283042e-07], + [8.80000000e01, 2.46446429e-07], + [9.90000000e01, 1.80004279e-06], + [1.10000000e02, 2.14886270e-06], + [1.21000000e02, 1.70090192e-06], + [1.32000000e02, 5.06554250e-07], + [1.43000000e02, 2.47801381e-08], + [1.54000000e02, 8.73866316e-08], + [1.65000000e02, 9.86362863e-07], + [1.76000000e02, 1.96411923e-06], + [1.87000000e02, 2.19933821e-06], + [1.98000000e02, 2.20997064e-06], + [2.09000000e02, 2.21000000e-06], + [2.20000000e02, 2.21000000e-06], + [2.31000000e02, 2.21000000e-06], + [2.42000000e02, 2.21000000e-06], + [2.53000000e02, 2.21000000e-06], + [2.64000000e02, 2.21000000e-06], + ], + ), + ], + ], resampledLayers=[[np.array([[0.0, 0.0, 0.0]])], [np.array([[0.0, 0.0, 0.0]])]], - calculationResults=RAT.outputs.CalculationResults(chiValues=np.array([202.83057377, 1641.4024969]), - sumChi=1844.2330706690975), - contrastParams=RAT.outputs.ContrastParams(backgroundParams=np.array([2.23e-06, 3.38e-06]), - scalefactors=np.array([0.1, 0.15]), - bulkIn=np.array([2.073e-06, 2.073e-06]), - bulkOut=np.array([5.98e-06, 2.21e-06]), - resolutionParams=np.array([0.03, 0.03]), - subRoughs=np.array([3.0, 3.0]), - resample=np.array([0.0, 0.0])), - fitParams=np.array([3.000e+00, 1.954e+01, 2.266e+01, 5.252e+00, 5.640e+00, 1.712e+01, 8.560e+00, 4.545e+01, - 1.070e+01, 6.014e+00, 1.782e+01, 1.764e+01, 3.615e+01, 2.361e+01, 2.230e-06, 3.380e-06, - 5.980e-06, 2.210e-06]), - fitNames=["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", - "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", - "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", - "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", - "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"], + calculationResults=RAT.outputs.CalculationResults( + chiValues=np.array([202.83057377, 1641.4024969]), + sumChi=1844.2330706690975, + ), + contrastParams=RAT.outputs.ContrastParams( + backgroundParams=np.array([2.23e-06, 3.38e-06]), + scalefactors=np.array([0.1, 0.15]), + bulkIn=np.array([2.073e-06, 2.073e-06]), + bulkOut=np.array([5.98e-06, 2.21e-06]), + resolutionParams=np.array([0.03, 0.03]), + subRoughs=np.array([3.0, 3.0]), + resample=np.array([0.0, 0.0]), + ), + fitParams=np.array( + [ + 3.000e00, + 1.954e01, + 2.266e01, + 5.252e00, + 5.640e00, + 1.712e01, + 8.560e00, + 4.545e01, + 1.070e01, + 6.014e00, + 1.782e01, + 1.764e01, + 3.615e01, + 2.361e01, + 2.230e-06, + 3.380e-06, + 5.980e-06, + 2.210e-06, + ], + ), + fitNames=[ + "Substrate Roughness", + "Oxide Thickness", + "SAM Tails Thickness", + "SAM Tails Hydration", + "SAM Roughness", + "CW Thickness", + "SAM Heads Thickness", + "SAM Heads Hydration", + "Bilayer Heads Thickness", + "Bilayer Roughness", + "Bilayer Tails Thickness", + "Bilayer Tails Hydration", + "Bilayer Heads Hydration", + "Oxide Hydration", + "Background parameter D2O", + "Background parameter SMW", + "D2O", + "SMW", + ], ) @@ -451,209 +635,265 @@ def dream_output_results(): and fitParams are taken from an optimisation with the parameters: nSamples=50000, nChains=10. """ results = RAT.rat_core.OutputResult() - results.reflectivity = [np.array([[1.14030000e-02, 1.45891447e+01], - [1.38610000e-02, 1.45739839e+01], - [1.68480000e-02, 1.45684725e+01], - [2.04790000e-02, 1.45658452e+01], - [2.48920000e-02, 1.45641513e+01], - [3.02560000e-02, 1.45628311e+01], - [3.67770000e-02, 1.45618510e+01], - [4.47020000e-02, 1.45613427e+01], - [5.43360000e-02, 1.45612940e+01], - [6.60450000e-02, 1.45613971e+01], - [8.02790000e-02, 1.45613678e+01], - [9.75790000e-02, 1.45612960e+01], - [1.18610000e-01, 1.45612799e+01], - [1.44170000e-01, 1.45612749e+01], - [1.75240000e-01, 1.45612726e+01], - [2.13000000e-01, 1.45612723e+01], - [2.58910000e-01, 1.45612724e+01], - [3.14700000e-01, 1.45612723e+01], - [3.82520000e-01, 1.45612723e+01], - [4.64960000e-01, 1.45612723e+01], - [5.65160000e-01, 1.45612723e+01]]), - np.array([[1.14030000e-02, 1.00000316e+00], - [1.38610000e-02, 1.21238320e-01], - [1.68480000e-02, 2.13433068e-02], - [2.04790000e-02, 8.71162144e-03], - [2.48920000e-02, 5.72807902e-03], - [3.02560000e-02, 3.71827525e-03], - [3.67770000e-02, 1.72067772e-03], - [4.47020000e-02, 3.51471141e-04], - [5.43360000e-02, 2.03975460e-05], - [6.60450000e-02, 1.89221181e-04], - [8.02790000e-02, 1.49280488e-04], - [9.75790000e-02, 3.86439083e-05], - [1.18610000e-01, 1.63612094e-05], - [1.44170000e-01, 6.75770325e-06], - [1.75240000e-01, 3.45955607e-06], - [2.13000000e-01, 3.20880782e-06], - [2.58910000e-01, 3.32068345e-06], - [3.14700000e-01, 3.21671807e-06], - [3.82520000e-01, 3.15612021e-06], - [4.64960000e-01, 3.15645546e-06], - [5.65160000e-01, 3.15511510e-06]])] - results.simulation = [np.array([[1.14030000e-02, 1.45891447e+01], - [1.38610000e-02, 1.45739839e+01], - [1.68480000e-02, 1.45684725e+01], - [2.04790000e-02, 1.45658452e+01], - [2.48920000e-02, 1.45641513e+01], - [3.02560000e-02, 1.45628311e+01], - [3.67770000e-02, 1.45618510e+01], - [4.47020000e-02, 1.45613427e+01], - [5.43360000e-02, 1.45612940e+01], - [6.60450000e-02, 1.45613971e+01], - [8.02790000e-02, 1.45613678e+01], - [9.75790000e-02, 1.45612960e+01], - [1.18610000e-01, 1.45612799e+01], - [1.44170000e-01, 1.45612749e+01], - [1.75240000e-01, 1.45612726e+01], - [2.13000000e-01, 1.45612723e+01], - [2.58910000e-01, 1.45612724e+01], - [3.14700000e-01, 1.45612723e+01], - [3.82520000e-01, 1.45612723e+01], - [4.64960000e-01, 1.45612723e+01], - [5.65160000e-01, 1.45612723e+01]]), - np.array([[1.14030000e-02, 1.00000316e+00], - [1.38610000e-02, 1.21238320e-01], - [1.68480000e-02, 2.13433068e-02], - [2.04790000e-02, 8.71162144e-03], - [2.48920000e-02, 5.72807902e-03], - [3.02560000e-02, 3.71827525e-03], - [3.67770000e-02, 1.72067772e-03], - [4.47020000e-02, 3.51471141e-04], - [5.43360000e-02, 2.03975460e-05], - [6.60450000e-02, 1.89221181e-04], - [8.02790000e-02, 1.49280488e-04], - [9.75790000e-02, 3.86439083e-05], - [1.18610000e-01, 1.63612094e-05], - [1.44170000e-01, 6.75770325e-06], - [1.75240000e-01, 3.45955607e-06], - [2.13000000e-01, 3.20880782e-06], - [2.58910000e-01, 3.32068345e-06], - [3.14700000e-01, 3.21671807e-06], - [3.82520000e-01, 3.15612021e-06], - [4.64960000e-01, 3.15645546e-06], - [5.65160000e-01, 3.15511510e-06]])] - results.shiftedData = [np.array([[1.1403e-02, 1.0063e+00, 1.9003e-02], - [1.3861e-02, 9.0118e-01, 1.1774e-02], - [1.6848e-02, 7.0455e-02, 1.3083e-03], - [2.0479e-02, 1.7544e-02, 5.1254e-04], - [2.4892e-02, 6.4257e-03, 2.6236e-04], - [3.0256e-02, 2.7746e-03, 8.5758e-05], - [3.6777e-02, 1.8591e-03, 4.9391e-05], - [4.4702e-02, 1.1002e-03, 3.2644e-05], - [5.4336e-02, 6.6691e-04, 2.1365e-05], - [6.6045e-02, 6.0729e-04, 1.1791e-05], - [8.0279e-02, 5.8755e-04, 1.5569e-05], - [9.7579e-02, 3.2700e-04, 6.5280e-06], - [1.1861e-01, 7.8205e-05, 2.5881e-06], - [1.4417e-01, 3.3455e-05, 1.5143e-06], - [1.7524e-01, 4.9313e-06, 5.6663e-07], - [2.1300e-01, 4.1948e-06, 6.8549e-07], - [2.5891e-01, 3.9863e-06, 8.2061e-07], - [3.1470e-01, 2.0861e-06, 6.1379e-07], - [3.8252e-01, 2.1154e-06, 6.3084e-07], - [4.6496e-01, 1.9906e-06, 6.0793e-07], - [5.6516e-01, 2.3816e-06, 7.0610e-07]]), - np.array([[1.14030000e-02, 1.20493333e-02, 7.32200000e-04], - [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], - [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], - [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], - [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], - [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], - [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], - [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], - [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], - [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], - [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], - [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], - [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], - [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], - [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], - [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], - [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], - [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], - [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], - [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], - [5.65160000e-01, 2.17460000e-06, 5.31346667e-07]])] - results.layerSlds = [[np.array([[3.15755349e+01, 3.35278238e-06, 4.16625659e+00], - [3.61791464e+01, 7.68327921e-07, 4.16625659e+00], - [1.00488530e+01, 2.06044530e-06, 2.78042232e+01], - [1.08043784e+01, 3.29384190e-06, 2.78042232e+01], - [2.42251646e+01, 2.35556998e-06, 1.55593097e+01], - [1.49022278e+01, 7.42138004e-07, 1.55593097e+01], - [1.49022278e+01, 7.42138004e-07, 1.55593097e+01], - [2.42251646e+01, 2.35556998e-06, 1.55593097e+01]])], - [np.array([[3.15755349e+01, 4.11636356e-06, 4.16625659e+00], - [3.61791464e+01, 1.39268494e-06, 4.16625659e+00], - [1.00488530e+01, 2.45715680e-06, 2.78042232e+01], - [1.08043784e+01, 5.26668495e-06, 2.78042232e+01], - [2.42251646e+01, 3.31348777e-06, 1.55593097e+01], - [1.49022278e+01, 1.37428245e-06, 1.55593097e+01], - [1.49022278e+01, 1.37428245e-06, 1.55593097e+01], - [2.42251646e+01, 3.31348777e-06, 1.55593097e+01]])]] - results.sldProfiles = [[np.array([[0.00000000e+00, 2.07301741e-06], - [1.10000000e+01, 2.07309604e-06], - [2.20000000e+01, 2.07345780e-06], - [3.30000000e+01, 2.07491687e-06], - [4.40000000e+01, 2.17562259e-06], - [5.50000000e+01, 3.22650495e-06], - [6.60000000e+01, 3.40913848e-06], - [7.70000000e+01, 3.13506253e-06], - [8.80000000e+01, 1.20456289e-06], - [9.90000000e+01, 1.27138002e-06], - [1.10000000e+02, 1.56285495e-06], - [1.21000000e+02, 1.84518709e-06], - [1.32000000e+02, 2.00498449e-06], - [1.43000000e+02, 1.96260665e-06], - [1.54000000e+02, 1.71966879e-06], - [1.65000000e+02, 1.39383267e-06], - [1.76000000e+02, 1.22970195e-06], - [1.87000000e+02, 1.41565526e-06], - [1.98000000e+02, 1.88172657e-06], - [2.09000000e+02, 2.40655867e-06], - [2.20000000e+02, 2.83570420e-06], - [2.31000000e+02, 3.11210082e-06], - [2.42000000e+02, 3.24277802e-06], - [2.53000000e+02, 3.28427452e-06], - [2.64000000e+02, 3.29268847e-06], - [2.75000000e+02, 3.29375418e-06], - [2.86000000e+02, 3.29383773e-06], - [2.97000000e+02, 3.29384178e-06], - [3.08000000e+02, 3.29384190e-06]]), - np.array([[0.00000000e+00, 2.07301819e-06], - [1.10000000e+01, 2.07310296e-06], - [2.20000000e+01, 2.07350415e-06], - [3.30000000e+01, 2.07518460e-06], - [4.40000000e+01, 2.23394767e-06], - [5.50000000e+01, 3.90646736e-06], - [6.60000000e+01, 4.18619279e-06], - [7.70000000e+01, 3.91675004e-06], - [8.80000000e+01, 1.92451547e-06], - [9.90000000e+01, 2.06988861e-06], - [1.10000000e+02, 2.47614934e-06], - [1.21000000e+02, 2.84889288e-06], - [1.32000000e+02, 3.00529286e-06], - [1.43000000e+02, 2.86310144e-06], - [1.54000000e+02, 2.49967205e-06], - [1.65000000e+02, 2.09673058e-06], - [1.76000000e+02, 1.92431424e-06], - [1.87000000e+02, 2.18829605e-06], - [1.98000000e+02, 2.83032190e-06], - [2.09000000e+02, 3.62584708e-06], - [2.20000000e+02, 4.36871153e-06], - [2.31000000e+02, 4.89806866e-06], - [2.42000000e+02, 5.16145928e-06], - [2.53000000e+02, 5.24684298e-06], - [2.64000000e+02, 5.26428707e-06], - [2.75000000e+02, 5.26650242e-06], - [2.86000000e+02, 5.26667628e-06], - [2.97000000e+02, 5.26668469e-06], - [3.08000000e+02, 5.26668495e-06]])]] - results.resampledLayers = [[np.array([[0., 0., 0.]])], [np.array([[0., 0., 0.]])]] + results.reflectivity = [ + np.array( + [ + [1.14030000e-02, 1.45891447e01], + [1.38610000e-02, 1.45739839e01], + [1.68480000e-02, 1.45684725e01], + [2.04790000e-02, 1.45658452e01], + [2.48920000e-02, 1.45641513e01], + [3.02560000e-02, 1.45628311e01], + [3.67770000e-02, 1.45618510e01], + [4.47020000e-02, 1.45613427e01], + [5.43360000e-02, 1.45612940e01], + [6.60450000e-02, 1.45613971e01], + [8.02790000e-02, 1.45613678e01], + [9.75790000e-02, 1.45612960e01], + [1.18610000e-01, 1.45612799e01], + [1.44170000e-01, 1.45612749e01], + [1.75240000e-01, 1.45612726e01], + [2.13000000e-01, 1.45612723e01], + [2.58910000e-01, 1.45612724e01], + [3.14700000e-01, 1.45612723e01], + [3.82520000e-01, 1.45612723e01], + [4.64960000e-01, 1.45612723e01], + [5.65160000e-01, 1.45612723e01], + ], + ), + np.array( + [ + [1.14030000e-02, 1.00000316e00], + [1.38610000e-02, 1.21238320e-01], + [1.68480000e-02, 2.13433068e-02], + [2.04790000e-02, 8.71162144e-03], + [2.48920000e-02, 5.72807902e-03], + [3.02560000e-02, 3.71827525e-03], + [3.67770000e-02, 1.72067772e-03], + [4.47020000e-02, 3.51471141e-04], + [5.43360000e-02, 2.03975460e-05], + [6.60450000e-02, 1.89221181e-04], + [8.02790000e-02, 1.49280488e-04], + [9.75790000e-02, 3.86439083e-05], + [1.18610000e-01, 1.63612094e-05], + [1.44170000e-01, 6.75770325e-06], + [1.75240000e-01, 3.45955607e-06], + [2.13000000e-01, 3.20880782e-06], + [2.58910000e-01, 3.32068345e-06], + [3.14700000e-01, 3.21671807e-06], + [3.82520000e-01, 3.15612021e-06], + [4.64960000e-01, 3.15645546e-06], + [5.65160000e-01, 3.15511510e-06], + ], + ), + ] + results.simulation = [ + np.array( + [ + [1.14030000e-02, 1.45891447e01], + [1.38610000e-02, 1.45739839e01], + [1.68480000e-02, 1.45684725e01], + [2.04790000e-02, 1.45658452e01], + [2.48920000e-02, 1.45641513e01], + [3.02560000e-02, 1.45628311e01], + [3.67770000e-02, 1.45618510e01], + [4.47020000e-02, 1.45613427e01], + [5.43360000e-02, 1.45612940e01], + [6.60450000e-02, 1.45613971e01], + [8.02790000e-02, 1.45613678e01], + [9.75790000e-02, 1.45612960e01], + [1.18610000e-01, 1.45612799e01], + [1.44170000e-01, 1.45612749e01], + [1.75240000e-01, 1.45612726e01], + [2.13000000e-01, 1.45612723e01], + [2.58910000e-01, 1.45612724e01], + [3.14700000e-01, 1.45612723e01], + [3.82520000e-01, 1.45612723e01], + [4.64960000e-01, 1.45612723e01], + [5.65160000e-01, 1.45612723e01], + ], + ), + np.array( + [ + [1.14030000e-02, 1.00000316e00], + [1.38610000e-02, 1.21238320e-01], + [1.68480000e-02, 2.13433068e-02], + [2.04790000e-02, 8.71162144e-03], + [2.48920000e-02, 5.72807902e-03], + [3.02560000e-02, 3.71827525e-03], + [3.67770000e-02, 1.72067772e-03], + [4.47020000e-02, 3.51471141e-04], + [5.43360000e-02, 2.03975460e-05], + [6.60450000e-02, 1.89221181e-04], + [8.02790000e-02, 1.49280488e-04], + [9.75790000e-02, 3.86439083e-05], + [1.18610000e-01, 1.63612094e-05], + [1.44170000e-01, 6.75770325e-06], + [1.75240000e-01, 3.45955607e-06], + [2.13000000e-01, 3.20880782e-06], + [2.58910000e-01, 3.32068345e-06], + [3.14700000e-01, 3.21671807e-06], + [3.82520000e-01, 3.15612021e-06], + [4.64960000e-01, 3.15645546e-06], + [5.65160000e-01, 3.15511510e-06], + ], + ), + ] + results.shiftedData = [ + np.array( + [ + [1.1403e-02, 1.0063e00, 1.9003e-02], + [1.3861e-02, 9.0118e-01, 1.1774e-02], + [1.6848e-02, 7.0455e-02, 1.3083e-03], + [2.0479e-02, 1.7544e-02, 5.1254e-04], + [2.4892e-02, 6.4257e-03, 2.6236e-04], + [3.0256e-02, 2.7746e-03, 8.5758e-05], + [3.6777e-02, 1.8591e-03, 4.9391e-05], + [4.4702e-02, 1.1002e-03, 3.2644e-05], + [5.4336e-02, 6.6691e-04, 2.1365e-05], + [6.6045e-02, 6.0729e-04, 1.1791e-05], + [8.0279e-02, 5.8755e-04, 1.5569e-05], + [9.7579e-02, 3.2700e-04, 6.5280e-06], + [1.1861e-01, 7.8205e-05, 2.5881e-06], + [1.4417e-01, 3.3455e-05, 1.5143e-06], + [1.7524e-01, 4.9313e-06, 5.6663e-07], + [2.1300e-01, 4.1948e-06, 6.8549e-07], + [2.5891e-01, 3.9863e-06, 8.2061e-07], + [3.1470e-01, 2.0861e-06, 6.1379e-07], + [3.8252e-01, 2.1154e-06, 6.3084e-07], + [4.6496e-01, 1.9906e-06, 6.0793e-07], + [5.6516e-01, 2.3816e-06, 7.0610e-07], + ], + ), + np.array( + [ + [1.14030000e-02, 1.20493333e-02, 7.32200000e-04], + [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], + [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], + [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], + [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], + [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], + [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], + [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], + [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], + [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], + [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], + [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], + [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], + [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], + [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], + [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], + [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], + [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], + [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], + [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], + [5.65160000e-01, 2.17460000e-06, 5.31346667e-07], + ], + ), + ] + results.layerSlds = [ + [ + np.array( + [ + [3.15755349e01, 3.35278238e-06, 4.16625659e00], + [3.61791464e01, 7.68327921e-07, 4.16625659e00], + [1.00488530e01, 2.06044530e-06, 2.78042232e01], + [1.08043784e01, 3.29384190e-06, 2.78042232e01], + [2.42251646e01, 2.35556998e-06, 1.55593097e01], + [1.49022278e01, 7.42138004e-07, 1.55593097e01], + [1.49022278e01, 7.42138004e-07, 1.55593097e01], + [2.42251646e01, 2.35556998e-06, 1.55593097e01], + ], + ), + ], + [ + np.array( + [ + [3.15755349e01, 4.11636356e-06, 4.16625659e00], + [3.61791464e01, 1.39268494e-06, 4.16625659e00], + [1.00488530e01, 2.45715680e-06, 2.78042232e01], + [1.08043784e01, 5.26668495e-06, 2.78042232e01], + [2.42251646e01, 3.31348777e-06, 1.55593097e01], + [1.49022278e01, 1.37428245e-06, 1.55593097e01], + [1.49022278e01, 1.37428245e-06, 1.55593097e01], + [2.42251646e01, 3.31348777e-06, 1.55593097e01], + ], + ), + ], + ] + results.sldProfiles = [ + [ + np.array( + [ + [0.00000000e00, 2.07301741e-06], + [1.10000000e01, 2.07309604e-06], + [2.20000000e01, 2.07345780e-06], + [3.30000000e01, 2.07491687e-06], + [4.40000000e01, 2.17562259e-06], + [5.50000000e01, 3.22650495e-06], + [6.60000000e01, 3.40913848e-06], + [7.70000000e01, 3.13506253e-06], + [8.80000000e01, 1.20456289e-06], + [9.90000000e01, 1.27138002e-06], + [1.10000000e02, 1.56285495e-06], + [1.21000000e02, 1.84518709e-06], + [1.32000000e02, 2.00498449e-06], + [1.43000000e02, 1.96260665e-06], + [1.54000000e02, 1.71966879e-06], + [1.65000000e02, 1.39383267e-06], + [1.76000000e02, 1.22970195e-06], + [1.87000000e02, 1.41565526e-06], + [1.98000000e02, 1.88172657e-06], + [2.09000000e02, 2.40655867e-06], + [2.20000000e02, 2.83570420e-06], + [2.31000000e02, 3.11210082e-06], + [2.42000000e02, 3.24277802e-06], + [2.53000000e02, 3.28427452e-06], + [2.64000000e02, 3.29268847e-06], + [2.75000000e02, 3.29375418e-06], + [2.86000000e02, 3.29383773e-06], + [2.97000000e02, 3.29384178e-06], + [3.08000000e02, 3.29384190e-06], + ], + ), + np.array( + [ + [0.00000000e00, 2.07301819e-06], + [1.10000000e01, 2.07310296e-06], + [2.20000000e01, 2.07350415e-06], + [3.30000000e01, 2.07518460e-06], + [4.40000000e01, 2.23394767e-06], + [5.50000000e01, 3.90646736e-06], + [6.60000000e01, 4.18619279e-06], + [7.70000000e01, 3.91675004e-06], + [8.80000000e01, 1.92451547e-06], + [9.90000000e01, 2.06988861e-06], + [1.10000000e02, 2.47614934e-06], + [1.21000000e02, 2.84889288e-06], + [1.32000000e02, 3.00529286e-06], + [1.43000000e02, 2.86310144e-06], + [1.54000000e02, 2.49967205e-06], + [1.65000000e02, 2.09673058e-06], + [1.76000000e02, 1.92431424e-06], + [1.87000000e02, 2.18829605e-06], + [1.98000000e02, 2.83032190e-06], + [2.09000000e02, 3.62584708e-06], + [2.20000000e02, 4.36871153e-06], + [2.31000000e02, 4.89806866e-06], + [2.42000000e02, 5.16145928e-06], + [2.53000000e02, 5.24684298e-06], + [2.64000000e02, 5.26428707e-06], + [2.75000000e02, 5.26650242e-06], + [2.86000000e02, 5.26667628e-06], + [2.97000000e02, 5.26668469e-06], + [3.08000000e02, 5.26668495e-06], + ], + ), + ], + ] + results.resampledLayers = [[np.array([[0.0, 0.0, 0.0]])], [np.array([[0.0, 0.0, 0.0]])]] results.calculationResults = RAT.rat_core.Calculation() - results.calculationResults.chiValues = np.array([4.6077885, 7.00028098]), + results.calculationResults.chiValues = (np.array([4.6077885, 7.00028098]),) results.calculationResults.sumChi = 11.608069475997699 results.contrastParams = RAT.rat_core.ContrastParams() results.contrastParams.backgroundParams = np.array([2.37113128e-06, 1.99006694e-06]) @@ -663,16 +903,48 @@ def dream_output_results(): results.contrastParams.resolutionParams = np.array([0.03, 0.03]) results.contrastParams.subRoughs = np.array([6.19503045, 6.19503045]) results.contrastParams.resample = np.array([0.0, 0.0]) - results.fitParams = np.array([6.19503045e+00, 1.84420960e+01, 2.11039621e+01, 8.75538121e+00, - 3.72292994e+00, 1.84624551e+01, 1.02316734e+01, 2.31156093e+01, - 1.09906265e+01, 5.71005361e+00, 1.67933822e+01, 1.72009856e+01, - 3.00260126e+01, 2.94448999e+01, 2.37113128e-06, 1.99006694e-06, - 6.01489149e-06, 1.59371685e-06]) - results.fitNames = ["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", - "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", - "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", - "Bilayer Tails Hydration", "Bilayer Heads Hydration", "Oxide Hydration", - "Background parameter D2O", "Background parameter SMW", "D2O", "SMW"] + results.fitParams = np.array( + [ + 6.19503045e00, + 1.84420960e01, + 2.11039621e01, + 8.75538121e00, + 3.72292994e00, + 1.84624551e01, + 1.02316734e01, + 2.31156093e01, + 1.09906265e01, + 5.71005361e00, + 1.67933822e01, + 1.72009856e01, + 3.00260126e01, + 2.94448999e01, + 2.37113128e-06, + 1.99006694e-06, + 6.01489149e-06, + 1.59371685e-06, + ], + ) + results.fitNames = [ + "Substrate Roughness", + "Oxide Thickness", + "SAM Tails Thickness", + "SAM Tails Hydration", + "SAM Roughness", + "CW Thickness", + "SAM Heads Thickness", + "SAM Heads Hydration", + "Bilayer Heads Thickness", + "Bilayer Roughness", + "Bilayer Tails Thickness", + "Bilayer Tails Hydration", + "Bilayer Heads Hydration", + "Oxide Hydration", + "Background parameter D2O", + "Background parameter SMW", + "D2O", + "SMW", + ] return results @@ -685,376 +957,1599 @@ def dream_bayes(): """ bayes = RAT.rat_core.BayesResults() bayes.predictionIntervals = RAT.rat_core.PredictionIntervals() - bayes.predictionIntervals.reflectivity = [np.array([[1.00000560e+00, 7.07687612e-01, 2.08315160e-02, 2.40787966e-03, - 2.17627660e-03, 2.54301700e-03, 1.76309827e-03, 6.15269679e-04, - 3.43679710e-05, 7.93625275e-05, 1.27760549e-04, 3.35799941e-05, - 6.75048751e-06, 7.64258431e-06, 6.05800395e-06, 5.60288298e-06, - 5.60333677e-06, 5.60223341e-06, 5.60206667e-06, 5.60206314e-06, - 5.60206314e-06], - [1.00000560e+00, 7.07687612e-01, 2.08315160e-02, 2.40787966e-03, - 2.17627660e-03, 2.54301700e-03, 1.76309827e-03, 6.15269679e-04, - 3.43679710e-05, 7.93625275e-05, 1.27760549e-04, 3.35799941e-05, - 6.75048751e-06, 7.64258431e-06, 6.05800395e-06, 5.60288298e-06, - 5.60333677e-06, 5.60223341e-06, 5.60206667e-06, 5.60206314e-06, - 5.60206314e-06], - [1.55201098e+01, 1.53748894e+01, 1.50402011e+01, 1.50299478e+01, - 1.50290525e+01, 1.50287935e+01, 1.50282126e+01, 1.50276044e+01, - 1.50273248e+01, 1.50273185e+01, 1.50272997e+01, 1.50272498e+01, - 1.50272364e+01, 1.50272347e+01, 1.50272335e+01, 1.50272330e+01, - 1.50272329e+01, 1.50272328e+01, 1.50272328e+01, 1.50272328e+01, - 1.50272328e+01], - [2.91397425e+01, 2.91324974e+01, 2.91281368e+01, 2.91255472e+01, - 2.91240295e+01, 2.91231836e+01, 2.91227893e+01, 2.91226872e+01, - 2.91226902e+01, 2.91226359e+01, 2.91225541e+01, 2.91225457e+01, - 2.91225448e+01, 2.91225407e+01, 2.91225398e+01, 2.91225393e+01, - 2.91225391e+01, 2.91225390e+01, 2.91225390e+01, 2.91225389e+01, - 2.91225389e+01], - [2.91397425e+01, 2.91324974e+01, 2.91281368e+01, 2.91255472e+01, - 2.91240295e+01, 2.91231836e+01, 2.91227893e+01, 2.91226872e+01, - 2.91226902e+01, 2.91226359e+01, 2.91225541e+01, 2.91225457e+01, - 2.91225448e+01, 2.91225407e+01, 2.91225398e+01, 2.91225393e+01, - 2.91225391e+01, 2.91225390e+01, 2.91225390e+01, 2.91225389e+01, - 2.91225389e+01]]), - np.array([[7.82198834e-01, 2.84434275e-02, 3.42472318e-03, 1.45801296e-03, - 2.02213149e-03, 2.00826891e-03, 1.27577246e-03, 4.27099544e-04, - 2.07304219e-05, 4.88683542e-05, 4.14000873e-05, 1.57801531e-05, - 2.08705254e-06, 2.27936213e-06, 1.09950377e-06, 7.09021331e-07, - 7.09539552e-07, 7.08228574e-07, 7.08101644e-07, 7.08098642e-07, - 7.08098641e-07], - [7.82198834e-01, 2.84434275e-02, 3.42472318e-03, 1.45801296e-03, - 2.02213149e-03, 2.00826891e-03, 1.27577246e-03, 4.27099544e-04, - 2.07304219e-05, 4.88683542e-05, 4.14000873e-05, 1.57801531e-05, - 2.08705254e-06, 2.27936213e-06, 1.09950377e-06, 7.09021331e-07, - 7.09539552e-07, 7.08228574e-07, 7.08101644e-07, 7.08098642e-07, - 7.08098641e-07], - [8.94587126e-01, 4.07514176e-01, 3.66355642e-02, 1.22207690e-02, - 5.91350591e-03, 3.17495745e-03, 1.50593011e-03, 5.80889579e-04, - 2.44654124e-04, 1.40925782e-04, 6.26965047e-05, 1.91064075e-05, - 9.45189755e-06, 6.16421495e-06, 5.17046678e-06, 4.24101973e-06, - 3.82067554e-06, 3.55352666e-06, 3.38981154e-06, 3.29631224e-06, - 3.25061722e-06], - [1.00000560e+00, 7.63076662e-01, 6.77868181e-02, 2.23160672e-02, - 9.56355479e-03, 4.26929321e-03, 1.72181442e-03, 7.25142247e-04, - 4.54691084e-04, 2.27274223e-04, 8.54009496e-05, 2.26525796e-05, - 1.63600080e-05, 9.80814666e-06, 8.98896697e-06, 7.55397947e-06, - 6.73887287e-06, 6.22237216e-06, 5.90521384e-06, 5.72401646e-06, - 5.63546023e-06], - [1.00000560e+00, 7.63076662e-01, 6.77868181e-02, 2.23160672e-02, - 9.56355479e-03, 4.26929321e-03, 1.72181442e-03, 7.25142247e-04, - 4.54691084e-04, 2.27274223e-04, 8.54009496e-05, 2.26525796e-05, - 1.63600080e-05, 9.80814666e-06, 8.98896697e-06, 7.55397947e-06, - 6.73887287e-06, 6.22237216e-06, 5.90521384e-06, 5.72401646e-06, - 5.63546023e-06]])] - bayes.predictionIntervals.sld = [[np.array([[-2.26996619e-06, -2.26938579e-06, -2.26877469e-06, ..., - 9.14568096e-07, 9.14567880e-07, 9.14567682e-07], - [-2.26996619e-06, -2.26938579e-06, -2.26877469e-06, ..., - 9.14568096e-07, 9.14567880e-07, 9.14567682e-07], - [-1.67970553e-07, -1.67671068e-07, -1.67355735e-07, ..., - 3.31763635e-06, 3.31763624e-06, 3.31763614e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.87958515e-06, 5.87958515e-06, 5.87958515e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.87958515e-06, 5.87958515e-06, 5.87958515e-06]])], - [np.array([[-1.35107208e-06, -1.34966627e-06, -1.34817823e-06, ..., - 4.65378475e-06, 4.65378475e-06, 4.65378475e-06], - [-1.35107208e-06, -1.34966627e-06, -1.34817823e-06, ..., - 4.65378475e-06, 4.65378475e-06, 4.65378475e-06], - [3.06178804e-07, 3.06904206e-07, 3.07672036e-07, ..., - 4.92477587e-06, 4.92477714e-06, 4.92477830e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.17896134e-06, 5.17896381e-06, 5.17896605e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.17896134e-06, 5.17896381e-06, 5.17896605e-06]])]] - bayes.predictionIntervals.reflectivityXData = [np.array([[0.011403, 0.013861, 0.016848, 0.020479, 0.024892, 0.030256, - 0.036777, 0.044702, 0.054336, 0.066045, 0.080279, 0.097579, - 0.11861, 0.14417, 0.17524, 0.213, 0.25891, 0.3147, - 0.38252, 0.46496, 0.56516]]), - np.array([[0.011403, 0.013861, 0.016848, 0.020479, 0.024892, 0.030256, - 0.036777, 0.044702, 0.054336, 0.066045, 0.080279, 0.097579, - 0.11861, 0.14417, 0.17524, 0.213, 0.25891, 0.3147, - 0.38252, 0.46496, 0.56516]])] - bayes.predictionIntervals.sldXData = [[np.array([[0., 11., 22., 33., 44., 55., 66., 77., 88., 99., 110., - 121., 132., 143., 154., 165., 176., 187., 198., 209., 220., 231., - 242., 253., 264., 275., 286., 297., 308., 319.]]), - np.array([[0., 11., 22., 33., 44., 55., 66., 77., 88., 99., 110., - 121., 132., 143., 154., 165., 176., 187., 198., 209., 220., 231., - 242., 253., 264., 275., 286., 297., 308., 319.]])]] - bayes.predictionIntervals.sampleChi = np.array([1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16]) + bayes.predictionIntervals.reflectivity = [ + np.array( + [ + [ + 1.00000560e00, + 7.07687612e-01, + 2.08315160e-02, + 2.40787966e-03, + 2.17627660e-03, + 2.54301700e-03, + 1.76309827e-03, + 6.15269679e-04, + 3.43679710e-05, + 7.93625275e-05, + 1.27760549e-04, + 3.35799941e-05, + 6.75048751e-06, + 7.64258431e-06, + 6.05800395e-06, + 5.60288298e-06, + 5.60333677e-06, + 5.60223341e-06, + 5.60206667e-06, + 5.60206314e-06, + 5.60206314e-06, + ], + [ + 1.00000560e00, + 7.07687612e-01, + 2.08315160e-02, + 2.40787966e-03, + 2.17627660e-03, + 2.54301700e-03, + 1.76309827e-03, + 6.15269679e-04, + 3.43679710e-05, + 7.93625275e-05, + 1.27760549e-04, + 3.35799941e-05, + 6.75048751e-06, + 7.64258431e-06, + 6.05800395e-06, + 5.60288298e-06, + 5.60333677e-06, + 5.60223341e-06, + 5.60206667e-06, + 5.60206314e-06, + 5.60206314e-06, + ], + [ + 1.55201098e01, + 1.53748894e01, + 1.50402011e01, + 1.50299478e01, + 1.50290525e01, + 1.50287935e01, + 1.50282126e01, + 1.50276044e01, + 1.50273248e01, + 1.50273185e01, + 1.50272997e01, + 1.50272498e01, + 1.50272364e01, + 1.50272347e01, + 1.50272335e01, + 1.50272330e01, + 1.50272329e01, + 1.50272328e01, + 1.50272328e01, + 1.50272328e01, + 1.50272328e01, + ], + [ + 2.91397425e01, + 2.91324974e01, + 2.91281368e01, + 2.91255472e01, + 2.91240295e01, + 2.91231836e01, + 2.91227893e01, + 2.91226872e01, + 2.91226902e01, + 2.91226359e01, + 2.91225541e01, + 2.91225457e01, + 2.91225448e01, + 2.91225407e01, + 2.91225398e01, + 2.91225393e01, + 2.91225391e01, + 2.91225390e01, + 2.91225390e01, + 2.91225389e01, + 2.91225389e01, + ], + [ + 2.91397425e01, + 2.91324974e01, + 2.91281368e01, + 2.91255472e01, + 2.91240295e01, + 2.91231836e01, + 2.91227893e01, + 2.91226872e01, + 2.91226902e01, + 2.91226359e01, + 2.91225541e01, + 2.91225457e01, + 2.91225448e01, + 2.91225407e01, + 2.91225398e01, + 2.91225393e01, + 2.91225391e01, + 2.91225390e01, + 2.91225390e01, + 2.91225389e01, + 2.91225389e01, + ], + ], + ), + np.array( + [ + [ + 7.82198834e-01, + 2.84434275e-02, + 3.42472318e-03, + 1.45801296e-03, + 2.02213149e-03, + 2.00826891e-03, + 1.27577246e-03, + 4.27099544e-04, + 2.07304219e-05, + 4.88683542e-05, + 4.14000873e-05, + 1.57801531e-05, + 2.08705254e-06, + 2.27936213e-06, + 1.09950377e-06, + 7.09021331e-07, + 7.09539552e-07, + 7.08228574e-07, + 7.08101644e-07, + 7.08098642e-07, + 7.08098641e-07, + ], + [ + 7.82198834e-01, + 2.84434275e-02, + 3.42472318e-03, + 1.45801296e-03, + 2.02213149e-03, + 2.00826891e-03, + 1.27577246e-03, + 4.27099544e-04, + 2.07304219e-05, + 4.88683542e-05, + 4.14000873e-05, + 1.57801531e-05, + 2.08705254e-06, + 2.27936213e-06, + 1.09950377e-06, + 7.09021331e-07, + 7.09539552e-07, + 7.08228574e-07, + 7.08101644e-07, + 7.08098642e-07, + 7.08098641e-07, + ], + [ + 8.94587126e-01, + 4.07514176e-01, + 3.66355642e-02, + 1.22207690e-02, + 5.91350591e-03, + 3.17495745e-03, + 1.50593011e-03, + 5.80889579e-04, + 2.44654124e-04, + 1.40925782e-04, + 6.26965047e-05, + 1.91064075e-05, + 9.45189755e-06, + 6.16421495e-06, + 5.17046678e-06, + 4.24101973e-06, + 3.82067554e-06, + 3.55352666e-06, + 3.38981154e-06, + 3.29631224e-06, + 3.25061722e-06, + ], + [ + 1.00000560e00, + 7.63076662e-01, + 6.77868181e-02, + 2.23160672e-02, + 9.56355479e-03, + 4.26929321e-03, + 1.72181442e-03, + 7.25142247e-04, + 4.54691084e-04, + 2.27274223e-04, + 8.54009496e-05, + 2.26525796e-05, + 1.63600080e-05, + 9.80814666e-06, + 8.98896697e-06, + 7.55397947e-06, + 6.73887287e-06, + 6.22237216e-06, + 5.90521384e-06, + 5.72401646e-06, + 5.63546023e-06, + ], + [ + 1.00000560e00, + 7.63076662e-01, + 6.77868181e-02, + 2.23160672e-02, + 9.56355479e-03, + 4.26929321e-03, + 1.72181442e-03, + 7.25142247e-04, + 4.54691084e-04, + 2.27274223e-04, + 8.54009496e-05, + 2.26525796e-05, + 1.63600080e-05, + 9.80814666e-06, + 8.98896697e-06, + 7.55397947e-06, + 6.73887287e-06, + 6.22237216e-06, + 5.90521384e-06, + 5.72401646e-06, + 5.63546023e-06, + ], + ], + ), + ] + bayes.predictionIntervals.sld = [ + [ + np.array( + [ + [ + -2.26996619e-06, + -2.26938579e-06, + -2.26877469e-06, + ..., + 9.14568096e-07, + 9.14567880e-07, + 9.14567682e-07, + ], + [ + -2.26996619e-06, + -2.26938579e-06, + -2.26877469e-06, + ..., + 9.14568096e-07, + 9.14567880e-07, + 9.14567682e-07, + ], + [ + -1.67970553e-07, + -1.67671068e-07, + -1.67355735e-07, + ..., + 3.31763635e-06, + 3.31763624e-06, + 3.31763614e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.87958515e-06, + 5.87958515e-06, + 5.87958515e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.87958515e-06, + 5.87958515e-06, + 5.87958515e-06, + ], + ], + ), + ], + [ + np.array( + [ + [ + -1.35107208e-06, + -1.34966627e-06, + -1.34817823e-06, + ..., + 4.65378475e-06, + 4.65378475e-06, + 4.65378475e-06, + ], + [ + -1.35107208e-06, + -1.34966627e-06, + -1.34817823e-06, + ..., + 4.65378475e-06, + 4.65378475e-06, + 4.65378475e-06, + ], + [ + 3.06178804e-07, + 3.06904206e-07, + 3.07672036e-07, + ..., + 4.92477587e-06, + 4.92477714e-06, + 4.92477830e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.17896134e-06, + 5.17896381e-06, + 5.17896605e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.17896134e-06, + 5.17896381e-06, + 5.17896605e-06, + ], + ], + ), + ], + ] + bayes.predictionIntervals.reflectivityXData = [ + np.array( + [ + [ + 0.011403, + 0.013861, + 0.016848, + 0.020479, + 0.024892, + 0.030256, + 0.036777, + 0.044702, + 0.054336, + 0.066045, + 0.080279, + 0.097579, + 0.11861, + 0.14417, + 0.17524, + 0.213, + 0.25891, + 0.3147, + 0.38252, + 0.46496, + 0.56516, + ], + ], + ), + np.array( + [ + [ + 0.011403, + 0.013861, + 0.016848, + 0.020479, + 0.024892, + 0.030256, + 0.036777, + 0.044702, + 0.054336, + 0.066045, + 0.080279, + 0.097579, + 0.11861, + 0.14417, + 0.17524, + 0.213, + 0.25891, + 0.3147, + 0.38252, + 0.46496, + 0.56516, + ], + ], + ), + ] + bayes.predictionIntervals.sldXData = [ + [ + np.array( + [ + [ + 0.0, + 11.0, + 22.0, + 33.0, + 44.0, + 55.0, + 66.0, + 77.0, + 88.0, + 99.0, + 110.0, + 121.0, + 132.0, + 143.0, + 154.0, + 165.0, + 176.0, + 187.0, + 198.0, + 209.0, + 220.0, + 231.0, + 242.0, + 253.0, + 264.0, + 275.0, + 286.0, + 297.0, + 308.0, + 319.0, + ], + ], + ), + np.array( + [ + [ + 0.0, + 11.0, + 22.0, + 33.0, + 44.0, + 55.0, + 66.0, + 77.0, + 88.0, + 99.0, + 110.0, + 121.0, + 132.0, + 143.0, + 154.0, + 165.0, + 176.0, + 187.0, + 198.0, + 209.0, + 220.0, + 231.0, + 242.0, + 253.0, + 264.0, + 275.0, + 286.0, + 297.0, + 308.0, + 319.0, + ], + ], + ), + ], + ] + bayes.predictionIntervals.sampleChi = np.array( + [ + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + ], + ) bayes.confidenceIntervals = RAT.rat_core.ConfidenceIntervals() - bayes.confidenceIntervals.percentile65 = np.array([[-1.29074586e-231, 8.33251318e+000, 1.75397363e+001, 1.75397363e+001, - 9.85302945e+000, 9.85302945e+000, 8.34197863e+000, 8.34197863e+000, - 1.65750684e+001, 1.45435510e+001, 1.45435510e+001, 1.52609047e+001, - 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 7.08098641e-007, 4.65378475e-006], - [8.33251318e+000, 5.48185565e+001, 5.48185565e+001, 4.57554170e+001, - 4.57554170e+001, 1.17557273e+001, 1.17557273e+001, 3.18752608e+001, - 3.18752608e+001, 1.65750684e+001, 1.52609047e+001, 4.88237113e+001, - 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, - 5.87958515e-006, 5.87958515e-006]]) - bayes.confidenceIntervals.percentile95 = np.array([[-1.29074586e-231, 8.33251318e+000, 1.75397363e+001, 1.75397363e+001, - 9.85302945e+000, 9.85302945e+000, 8.34197863e+000, 8.34197863e+000, - 1.65750684e+001, 1.45435510e+001, 1.45435510e+001, 1.52609047e+001, - 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 7.08098641e-007, 4.65378475e-006], - [8.33251318e+000, 5.48185565e+001, 5.48185565e+001, 4.57554170e+001, - 4.57554170e+001, 1.17557273e+001, 1.17557273e+001, 3.18752608e+001, - 3.18752608e+001, 1.65750684e+001, 1.52609047e+001, 4.88237113e+001, - 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, - 5.87958515e-006, 5.87958515e-006]]) - bayes.confidenceIntervals.mean = np.array([[4.16625659e+00, 3.15755349e+01, 3.61791464e+01, 3.16475766e+01, - 2.78042232e+01, 1.08043784e+01, 1.00488530e+01, 2.01086197e+01, - 2.42251646e+01, 1.55593097e+01, 1.49022278e+01, 3.20423080e+01, - 4.85551946e+01, 3.87046084e+01, 1.45612723e+01, 3.15508089e-06, - 3.29384190e-06, 5.26668495e-06]]) + bayes.confidenceIntervals.percentile65 = np.array( + [ + [ + -1.29074586e-231, + 8.33251318e000, + 1.75397363e001, + 1.75397363e001, + 9.85302945e000, + 9.85302945e000, + 8.34197863e000, + 8.34197863e000, + 1.65750684e001, + 1.45435510e001, + 1.45435510e001, + 1.52609047e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 7.08098641e-007, + 4.65378475e-006, + ], + [ + 8.33251318e000, + 5.48185565e001, + 5.48185565e001, + 4.57554170e001, + 4.57554170e001, + 1.17557273e001, + 1.17557273e001, + 3.18752608e001, + 3.18752608e001, + 1.65750684e001, + 1.52609047e001, + 4.88237113e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 5.87958515e-006, + 5.87958515e-006, + ], + ], + ) + bayes.confidenceIntervals.percentile95 = np.array( + [ + [ + -1.29074586e-231, + 8.33251318e000, + 1.75397363e001, + 1.75397363e001, + 9.85302945e000, + 9.85302945e000, + 8.34197863e000, + 8.34197863e000, + 1.65750684e001, + 1.45435510e001, + 1.45435510e001, + 1.52609047e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 7.08098641e-007, + 4.65378475e-006, + ], + [ + 8.33251318e000, + 5.48185565e001, + 5.48185565e001, + 4.57554170e001, + 4.57554170e001, + 1.17557273e001, + 1.17557273e001, + 3.18752608e001, + 3.18752608e001, + 1.65750684e001, + 1.52609047e001, + 4.88237113e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 5.87958515e-006, + 5.87958515e-006, + ], + ], + ) + bayes.confidenceIntervals.mean = np.array( + [ + [ + 4.16625659e00, + 3.15755349e01, + 3.61791464e01, + 3.16475766e01, + 2.78042232e01, + 1.08043784e01, + 1.00488530e01, + 2.01086197e01, + 2.42251646e01, + 1.55593097e01, + 1.49022278e01, + 3.20423080e01, + 4.85551946e01, + 3.87046084e01, + 1.45612723e01, + 3.15508089e-06, + 3.29384190e-06, + 5.26668495e-06, + ], + ], + ) bayes.dreamParams = RAT.rat_core.DreamParams() bayes.dreamParams.nParams = 18.0 bayes.dreamParams.nChains = 1.0 @@ -1074,50 +2569,113 @@ def dream_bayes(): bayes.dreamParams.ABC = 0 bayes.dreamParams.IO = 0 bayes.dreamParams.storeOutput = 0 - bayes.dreamParams.R = np.array([[0.]]) + bayes.dreamParams.R = np.array([[0.0]]) bayes.dreamOutput = RAT.rat_core.DreamOutput() - bayes.dreamOutput.allChains = np.array([[[8.33251318e+00], - [5.48185565e+01], - [1.75397363e+01], - [4.57554170e+01], - [9.85302945e+00], - [1.17557273e+01], - [8.34197863e+00], - [3.18752608e+01], - [1.65750684e+01], - [1.45435510e+01], - [1.52609047e+01], - [4.88237113e+01], - [4.82866779e+01], - [2.91225389e+01], - [5.60206314e-06], - [7.08098641e-07], - [5.87958515e-06], - [4.65378475e-06], - [-4.86509830e+01], - [-5.63741277e+05]]]) + bayes.dreamOutput.allChains = np.array( + [ + [ + [8.33251318e00], + [5.48185565e01], + [1.75397363e01], + [4.57554170e01], + [9.85302945e00], + [1.17557273e01], + [8.34197863e00], + [3.18752608e01], + [1.65750684e01], + [1.45435510e01], + [1.52609047e01], + [4.88237113e01], + [4.82866779e01], + [2.91225389e01], + [5.60206314e-06], + [7.08098641e-07], + [5.87958515e-06], + [4.65378475e-06], + [-4.86509830e01], + [-5.63741277e05], + ], + ], + ) bayes.dreamOutput.outlierChains = np.array([[0.0, 0.0]]) bayes.dreamOutput.runtime = 2.6e-06 bayes.dreamOutput.iteration = 2.0 bayes.dreamOutput.modelOutput = 0.0 bayes.dreamOutput.AR = np.array([[1.0, np.nan]]) - bayes.dreamOutput.R_stat = np.array([[1.0, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, - np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]]) + bayes.dreamOutput.R_stat = np.array( + [ + [ + 1.0, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + ], + ], + ) bayes.dreamOutput.CR = np.array([[1.00000000, 0.33333333, 0.33333333, 0.33333333]]) bayes.nestedSamplerOutput = RAT.rat_core.NestedSamplerOutput() bayes.nestedSamplerOutput.logZ = 0.0 bayes.nestedSamplerOutput.nestSamples = np.array([[0.0, 0.0]]) bayes.nestedSamplerOutput.postSamples = np.array([[0.0, 0.0]]) - bayes.chain = np.array([[-1.29231905e-231, 8.33251318e+000, 5.48185565e+001, 1.75397363e+001, - 4.57554170e+001, 9.85302945e+000, 1.17557273e+001, 8.34197863e+000, - 3.18752608e+001, 1.65750684e+001, 1.45435510e+001, 1.52609047e+001, - 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, - 7.08098641e-007, 5.87958515e-006], - [8.33251318e+000, 5.48185565e+001, 1.75397363e+001, 4.57554170e+001, - 9.85302945e+000, 1.17557273e+001, 8.34197863e+000, 3.18752608e+001, - 1.65750684e+001, 1.45435510e+001, 1.52609047e+001, 4.88237113e+001, - 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 5.87958515e-006, 4.65378475e-006]]) + bayes.chain = np.array( + [ + [ + -1.29231905e-231, + 8.33251318e000, + 5.48185565e001, + 1.75397363e001, + 4.57554170e001, + 9.85302945e000, + 1.17557273e001, + 8.34197863e000, + 3.18752608e001, + 1.65750684e001, + 1.45435510e001, + 1.52609047e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 5.87958515e-006, + ], + [ + 8.33251318e000, + 5.48185565e001, + 1.75397363e001, + 4.57554170e001, + 9.85302945e000, + 1.17557273e001, + 8.34197863e000, + 3.18752608e001, + 1.65750684e001, + 1.45435510e001, + 1.52609047e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 5.87958515e-006, + 4.65378475e-006, + ], + ], + ) return bayes @@ -1130,207 +2688,263 @@ def dream_results(): and fitParams are taken from an optimisation with the parameters: nSamples=50000, nChains=10. """ return RAT.outputs.BayesResults( - reflectivity=[np.array([[1.14030000e-02, 1.45891447e+01], - [1.38610000e-02, 1.45739839e+01], - [1.68480000e-02, 1.45684725e+01], - [2.04790000e-02, 1.45658452e+01], - [2.48920000e-02, 1.45641513e+01], - [3.02560000e-02, 1.45628311e+01], - [3.67770000e-02, 1.45618510e+01], - [4.47020000e-02, 1.45613427e+01], - [5.43360000e-02, 1.45612940e+01], - [6.60450000e-02, 1.45613971e+01], - [8.02790000e-02, 1.45613678e+01], - [9.75790000e-02, 1.45612960e+01], - [1.18610000e-01, 1.45612799e+01], - [1.44170000e-01, 1.45612749e+01], - [1.75240000e-01, 1.45612726e+01], - [2.13000000e-01, 1.45612723e+01], - [2.58910000e-01, 1.45612724e+01], - [3.14700000e-01, 1.45612723e+01], - [3.82520000e-01, 1.45612723e+01], - [4.64960000e-01, 1.45612723e+01], - [5.65160000e-01, 1.45612723e+01]]), - np.array([[1.14030000e-02, 1.00000316e+00], - [1.38610000e-02, 1.21238320e-01], - [1.68480000e-02, 2.13433068e-02], - [2.04790000e-02, 8.71162144e-03], - [2.48920000e-02, 5.72807902e-03], - [3.02560000e-02, 3.71827525e-03], - [3.67770000e-02, 1.72067772e-03], - [4.47020000e-02, 3.51471141e-04], - [5.43360000e-02, 2.03975460e-05], - [6.60450000e-02, 1.89221181e-04], - [8.02790000e-02, 1.49280488e-04], - [9.75790000e-02, 3.86439083e-05], - [1.18610000e-01, 1.63612094e-05], - [1.44170000e-01, 6.75770325e-06], - [1.75240000e-01, 3.45955607e-06], - [2.13000000e-01, 3.20880782e-06], - [2.58910000e-01, 3.32068345e-06], - [3.14700000e-01, 3.21671807e-06], - [3.82520000e-01, 3.15612021e-06], - [4.64960000e-01, 3.15645546e-06], - [5.65160000e-01, 3.15511510e-06]])], - simulation=[np.array([[1.14030000e-02, 1.45891447e+01], - [1.38610000e-02, 1.45739839e+01], - [1.68480000e-02, 1.45684725e+01], - [2.04790000e-02, 1.45658452e+01], - [2.48920000e-02, 1.45641513e+01], - [3.02560000e-02, 1.45628311e+01], - [3.67770000e-02, 1.45618510e+01], - [4.47020000e-02, 1.45613427e+01], - [5.43360000e-02, 1.45612940e+01], - [6.60450000e-02, 1.45613971e+01], - [8.02790000e-02, 1.45613678e+01], - [9.75790000e-02, 1.45612960e+01], - [1.18610000e-01, 1.45612799e+01], - [1.44170000e-01, 1.45612749e+01], - [1.75240000e-01, 1.45612726e+01], - [2.13000000e-01, 1.45612723e+01], - [2.58910000e-01, 1.45612724e+01], - [3.14700000e-01, 1.45612723e+01], - [3.82520000e-01, 1.45612723e+01], - [4.64960000e-01, 1.45612723e+01], - [5.65160000e-01, 1.45612723e+01]]), - np.array([[1.14030000e-02, 1.00000316e+00], - [1.38610000e-02, 1.21238320e-01], - [1.68480000e-02, 2.13433068e-02], - [2.04790000e-02, 8.71162144e-03], - [2.48920000e-02, 5.72807902e-03], - [3.02560000e-02, 3.71827525e-03], - [3.67770000e-02, 1.72067772e-03], - [4.47020000e-02, 3.51471141e-04], - [5.43360000e-02, 2.03975460e-05], - [6.60450000e-02, 1.89221181e-04], - [8.02790000e-02, 1.49280488e-04], - [9.75790000e-02, 3.86439083e-05], - [1.18610000e-01, 1.63612094e-05], - [1.44170000e-01, 6.75770325e-06], - [1.75240000e-01, 3.45955607e-06], - [2.13000000e-01, 3.20880782e-06], - [2.58910000e-01, 3.32068345e-06], - [3.14700000e-01, 3.21671807e-06], - [3.82520000e-01, 3.15612021e-06], - [4.64960000e-01, 3.15645546e-06], - [5.65160000e-01, 3.15511510e-06]])], - shiftedData=[np.array([[1.1403e-02, 1.0063e+00, 1.9003e-02], - [1.3861e-02, 9.0118e-01, 1.1774e-02], - [1.6848e-02, 7.0455e-02, 1.3083e-03], - [2.0479e-02, 1.7544e-02, 5.1254e-04], - [2.4892e-02, 6.4257e-03, 2.6236e-04], - [3.0256e-02, 2.7746e-03, 8.5758e-05], - [3.6777e-02, 1.8591e-03, 4.9391e-05], - [4.4702e-02, 1.1002e-03, 3.2644e-05], - [5.4336e-02, 6.6691e-04, 2.1365e-05], - [6.6045e-02, 6.0729e-04, 1.1791e-05], - [8.0279e-02, 5.8755e-04, 1.5569e-05], - [9.7579e-02, 3.2700e-04, 6.5280e-06], - [1.1861e-01, 7.8205e-05, 2.5881e-06], - [1.4417e-01, 3.3455e-05, 1.5143e-06], - [1.7524e-01, 4.9313e-06, 5.6663e-07], - [2.1300e-01, 4.1948e-06, 6.8549e-07], - [2.5891e-01, 3.9863e-06, 8.2061e-07], - [3.1470e-01, 2.0861e-06, 6.1379e-07], - [3.8252e-01, 2.1154e-06, 6.3084e-07], - [4.6496e-01, 1.9906e-06, 6.0793e-07], - [5.6516e-01, 2.3816e-06, 7.0610e-07]]), - np.array([[1.14030000e-02, 1.20493333e-02, 7.32200000e-04], - [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], - [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], - [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], - [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], - [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], - [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], - [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], - [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], - [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], - [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], - [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], - [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], - [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], - [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], - [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], - [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], - [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], - [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], - [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], - [5.65160000e-01, 2.17460000e-06, 5.31346667e-07]])], - layerSlds=[[np.array([[3.15755349e+01, 3.35278238e-06, 4.16625659e+00], - [3.61791464e+01, 7.68327921e-07, 4.16625659e+00], - [1.00488530e+01, 2.06044530e-06, 2.78042232e+01], - [1.08043784e+01, 3.29384190e-06, 2.78042232e+01], - [2.42251646e+01, 2.35556998e-06, 1.55593097e+01], - [1.49022278e+01, 7.42138004e-07, 1.55593097e+01], - [1.49022278e+01, 7.42138004e-07, 1.55593097e+01], - [2.42251646e+01, 2.35556998e-06, 1.55593097e+01]])], - [np.array([[3.15755349e+01, 4.11636356e-06, 4.16625659e+00], - [3.61791464e+01, 1.39268494e-06, 4.16625659e+00], - [1.00488530e+01, 2.45715680e-06, 2.78042232e+01], - [1.08043784e+01, 5.26668495e-06, 2.78042232e+01], - [2.42251646e+01, 3.31348777e-06, 1.55593097e+01], - [1.49022278e+01, 1.37428245e-06, 1.55593097e+01], - [1.49022278e+01, 1.37428245e-06, 1.55593097e+01], - [2.42251646e+01, 3.31348777e-06, 1.55593097e+01]])]], - sldProfiles=[[np.array([[0.00000000e+00, 2.07301741e-06], - [1.10000000e+01, 2.07309604e-06], - [2.20000000e+01, 2.07345780e-06], - [3.30000000e+01, 2.07491687e-06], - [4.40000000e+01, 2.17562259e-06], - [5.50000000e+01, 3.22650495e-06], - [6.60000000e+01, 3.40913848e-06], - [7.70000000e+01, 3.13506253e-06], - [8.80000000e+01, 1.20456289e-06], - [9.90000000e+01, 1.27138002e-06], - [1.10000000e+02, 1.56285495e-06], - [1.21000000e+02, 1.84518709e-06], - [1.32000000e+02, 2.00498449e-06], - [1.43000000e+02, 1.96260665e-06], - [1.54000000e+02, 1.71966879e-06], - [1.65000000e+02, 1.39383267e-06], - [1.76000000e+02, 1.22970195e-06], - [1.87000000e+02, 1.41565526e-06], - [1.98000000e+02, 1.88172657e-06], - [2.09000000e+02, 2.40655867e-06], - [2.20000000e+02, 2.83570420e-06], - [2.31000000e+02, 3.11210082e-06], - [2.42000000e+02, 3.24277802e-06], - [2.53000000e+02, 3.28427452e-06], - [2.64000000e+02, 3.29268847e-06], - [2.75000000e+02, 3.29375418e-06], - [2.86000000e+02, 3.29383773e-06], - [2.97000000e+02, 3.29384178e-06], - [3.08000000e+02, 3.29384190e-06]]), - np.array([[0.00000000e+00, 2.07301819e-06], - [1.10000000e+01, 2.07310296e-06], - [2.20000000e+01, 2.07350415e-06], - [3.30000000e+01, 2.07518460e-06], - [4.40000000e+01, 2.23394767e-06], - [5.50000000e+01, 3.90646736e-06], - [6.60000000e+01, 4.18619279e-06], - [7.70000000e+01, 3.91675004e-06], - [8.80000000e+01, 1.92451547e-06], - [9.90000000e+01, 2.06988861e-06], - [1.10000000e+02, 2.47614934e-06], - [1.21000000e+02, 2.84889288e-06], - [1.32000000e+02, 3.00529286e-06], - [1.43000000e+02, 2.86310144e-06], - [1.54000000e+02, 2.49967205e-06], - [1.65000000e+02, 2.09673058e-06], - [1.76000000e+02, 1.92431424e-06], - [1.87000000e+02, 2.18829605e-06], - [1.98000000e+02, 2.83032190e-06], - [2.09000000e+02, 3.62584708e-06], - [2.20000000e+02, 4.36871153e-06], - [2.31000000e+02, 4.89806866e-06], - [2.42000000e+02, 5.16145928e-06], - [2.53000000e+02, 5.24684298e-06], - [2.64000000e+02, 5.26428707e-06], - [2.75000000e+02, 5.26650242e-06], - [2.86000000e+02, 5.26667628e-06], - [2.97000000e+02, 5.26668469e-06], - [3.08000000e+02, 5.26668495e-06]])]], - resampledLayers=[[np.array([[0., 0., 0.]])], [np.array([[0., 0., 0.]])]], + reflectivity=[ + np.array( + [ + [1.14030000e-02, 1.45891447e01], + [1.38610000e-02, 1.45739839e01], + [1.68480000e-02, 1.45684725e01], + [2.04790000e-02, 1.45658452e01], + [2.48920000e-02, 1.45641513e01], + [3.02560000e-02, 1.45628311e01], + [3.67770000e-02, 1.45618510e01], + [4.47020000e-02, 1.45613427e01], + [5.43360000e-02, 1.45612940e01], + [6.60450000e-02, 1.45613971e01], + [8.02790000e-02, 1.45613678e01], + [9.75790000e-02, 1.45612960e01], + [1.18610000e-01, 1.45612799e01], + [1.44170000e-01, 1.45612749e01], + [1.75240000e-01, 1.45612726e01], + [2.13000000e-01, 1.45612723e01], + [2.58910000e-01, 1.45612724e01], + [3.14700000e-01, 1.45612723e01], + [3.82520000e-01, 1.45612723e01], + [4.64960000e-01, 1.45612723e01], + [5.65160000e-01, 1.45612723e01], + ], + ), + np.array( + [ + [1.14030000e-02, 1.00000316e00], + [1.38610000e-02, 1.21238320e-01], + [1.68480000e-02, 2.13433068e-02], + [2.04790000e-02, 8.71162144e-03], + [2.48920000e-02, 5.72807902e-03], + [3.02560000e-02, 3.71827525e-03], + [3.67770000e-02, 1.72067772e-03], + [4.47020000e-02, 3.51471141e-04], + [5.43360000e-02, 2.03975460e-05], + [6.60450000e-02, 1.89221181e-04], + [8.02790000e-02, 1.49280488e-04], + [9.75790000e-02, 3.86439083e-05], + [1.18610000e-01, 1.63612094e-05], + [1.44170000e-01, 6.75770325e-06], + [1.75240000e-01, 3.45955607e-06], + [2.13000000e-01, 3.20880782e-06], + [2.58910000e-01, 3.32068345e-06], + [3.14700000e-01, 3.21671807e-06], + [3.82520000e-01, 3.15612021e-06], + [4.64960000e-01, 3.15645546e-06], + [5.65160000e-01, 3.15511510e-06], + ], + ), + ], + simulation=[ + np.array( + [ + [1.14030000e-02, 1.45891447e01], + [1.38610000e-02, 1.45739839e01], + [1.68480000e-02, 1.45684725e01], + [2.04790000e-02, 1.45658452e01], + [2.48920000e-02, 1.45641513e01], + [3.02560000e-02, 1.45628311e01], + [3.67770000e-02, 1.45618510e01], + [4.47020000e-02, 1.45613427e01], + [5.43360000e-02, 1.45612940e01], + [6.60450000e-02, 1.45613971e01], + [8.02790000e-02, 1.45613678e01], + [9.75790000e-02, 1.45612960e01], + [1.18610000e-01, 1.45612799e01], + [1.44170000e-01, 1.45612749e01], + [1.75240000e-01, 1.45612726e01], + [2.13000000e-01, 1.45612723e01], + [2.58910000e-01, 1.45612724e01], + [3.14700000e-01, 1.45612723e01], + [3.82520000e-01, 1.45612723e01], + [4.64960000e-01, 1.45612723e01], + [5.65160000e-01, 1.45612723e01], + ], + ), + np.array( + [ + [1.14030000e-02, 1.00000316e00], + [1.38610000e-02, 1.21238320e-01], + [1.68480000e-02, 2.13433068e-02], + [2.04790000e-02, 8.71162144e-03], + [2.48920000e-02, 5.72807902e-03], + [3.02560000e-02, 3.71827525e-03], + [3.67770000e-02, 1.72067772e-03], + [4.47020000e-02, 3.51471141e-04], + [5.43360000e-02, 2.03975460e-05], + [6.60450000e-02, 1.89221181e-04], + [8.02790000e-02, 1.49280488e-04], + [9.75790000e-02, 3.86439083e-05], + [1.18610000e-01, 1.63612094e-05], + [1.44170000e-01, 6.75770325e-06], + [1.75240000e-01, 3.45955607e-06], + [2.13000000e-01, 3.20880782e-06], + [2.58910000e-01, 3.32068345e-06], + [3.14700000e-01, 3.21671807e-06], + [3.82520000e-01, 3.15612021e-06], + [4.64960000e-01, 3.15645546e-06], + [5.65160000e-01, 3.15511510e-06], + ], + ), + ], + shiftedData=[ + np.array( + [ + [1.1403e-02, 1.0063e00, 1.9003e-02], + [1.3861e-02, 9.0118e-01, 1.1774e-02], + [1.6848e-02, 7.0455e-02, 1.3083e-03], + [2.0479e-02, 1.7544e-02, 5.1254e-04], + [2.4892e-02, 6.4257e-03, 2.6236e-04], + [3.0256e-02, 2.7746e-03, 8.5758e-05], + [3.6777e-02, 1.8591e-03, 4.9391e-05], + [4.4702e-02, 1.1002e-03, 3.2644e-05], + [5.4336e-02, 6.6691e-04, 2.1365e-05], + [6.6045e-02, 6.0729e-04, 1.1791e-05], + [8.0279e-02, 5.8755e-04, 1.5569e-05], + [9.7579e-02, 3.2700e-04, 6.5280e-06], + [1.1861e-01, 7.8205e-05, 2.5881e-06], + [1.4417e-01, 3.3455e-05, 1.5143e-06], + [1.7524e-01, 4.9313e-06, 5.6663e-07], + [2.1300e-01, 4.1948e-06, 6.8549e-07], + [2.5891e-01, 3.9863e-06, 8.2061e-07], + [3.1470e-01, 2.0861e-06, 6.1379e-07], + [3.8252e-01, 2.1154e-06, 6.3084e-07], + [4.6496e-01, 1.9906e-06, 6.0793e-07], + [5.6516e-01, 2.3816e-06, 7.0610e-07], + ], + ), + np.array( + [ + [1.14030000e-02, 1.20493333e-02, 7.32200000e-04], + [1.38610000e-02, 6.71800000e-03, 3.71853333e-04], + [1.68480000e-02, 4.10506667e-03, 2.23106667e-04], + [2.04790000e-02, 2.43306667e-03, 1.46873333e-04], + [2.48920000e-02, 1.40940000e-03, 9.59200000e-05], + [3.02560000e-02, 7.43400000e-04, 3.50226667e-05], + [3.67770000e-02, 3.51466667e-04, 1.59573333e-05], + [4.47020000e-02, 9.80666667e-05, 7.29466667e-06], + [5.43360000e-02, 1.73500000e-05, 2.59200000e-06], + [6.60450000e-02, 4.86286667e-05, 2.46453333e-06], + [8.02790000e-02, 9.71733333e-05, 4.68166667e-06], + [9.75790000e-02, 7.06533333e-05, 2.32180000e-06], + [1.18610000e-01, 1.93046667e-05, 1.00813333e-06], + [1.44170000e-01, 6.61193333e-06, 5.29773333e-07], + [1.75240000e-01, 3.23726667e-06, 3.65933333e-07], + [2.13000000e-01, 3.29920000e-06, 4.74273333e-07], + [2.58910000e-01, 1.71180000e-06, 4.24720000e-07], + [3.14700000e-01, 2.24020000e-06, 5.08946667e-07], + [3.82520000e-01, 2.73306667e-06, 5.71513333e-07], + [4.64960000e-01, 2.36153333e-06, 5.24806667e-07], + [5.65160000e-01, 2.17460000e-06, 5.31346667e-07], + ], + ), + ], + layerSlds=[ + [ + np.array( + [ + [3.15755349e01, 3.35278238e-06, 4.16625659e00], + [3.61791464e01, 7.68327921e-07, 4.16625659e00], + [1.00488530e01, 2.06044530e-06, 2.78042232e01], + [1.08043784e01, 3.29384190e-06, 2.78042232e01], + [2.42251646e01, 2.35556998e-06, 1.55593097e01], + [1.49022278e01, 7.42138004e-07, 1.55593097e01], + [1.49022278e01, 7.42138004e-07, 1.55593097e01], + [2.42251646e01, 2.35556998e-06, 1.55593097e01], + ], + ), + ], + [ + np.array( + [ + [3.15755349e01, 4.11636356e-06, 4.16625659e00], + [3.61791464e01, 1.39268494e-06, 4.16625659e00], + [1.00488530e01, 2.45715680e-06, 2.78042232e01], + [1.08043784e01, 5.26668495e-06, 2.78042232e01], + [2.42251646e01, 3.31348777e-06, 1.55593097e01], + [1.49022278e01, 1.37428245e-06, 1.55593097e01], + [1.49022278e01, 1.37428245e-06, 1.55593097e01], + [2.42251646e01, 3.31348777e-06, 1.55593097e01], + ], + ), + ], + ], + sldProfiles=[ + [ + np.array( + [ + [0.00000000e00, 2.07301741e-06], + [1.10000000e01, 2.07309604e-06], + [2.20000000e01, 2.07345780e-06], + [3.30000000e01, 2.07491687e-06], + [4.40000000e01, 2.17562259e-06], + [5.50000000e01, 3.22650495e-06], + [6.60000000e01, 3.40913848e-06], + [7.70000000e01, 3.13506253e-06], + [8.80000000e01, 1.20456289e-06], + [9.90000000e01, 1.27138002e-06], + [1.10000000e02, 1.56285495e-06], + [1.21000000e02, 1.84518709e-06], + [1.32000000e02, 2.00498449e-06], + [1.43000000e02, 1.96260665e-06], + [1.54000000e02, 1.71966879e-06], + [1.65000000e02, 1.39383267e-06], + [1.76000000e02, 1.22970195e-06], + [1.87000000e02, 1.41565526e-06], + [1.98000000e02, 1.88172657e-06], + [2.09000000e02, 2.40655867e-06], + [2.20000000e02, 2.83570420e-06], + [2.31000000e02, 3.11210082e-06], + [2.42000000e02, 3.24277802e-06], + [2.53000000e02, 3.28427452e-06], + [2.64000000e02, 3.29268847e-06], + [2.75000000e02, 3.29375418e-06], + [2.86000000e02, 3.29383773e-06], + [2.97000000e02, 3.29384178e-06], + [3.08000000e02, 3.29384190e-06], + ], + ), + np.array( + [ + [0.00000000e00, 2.07301819e-06], + [1.10000000e01, 2.07310296e-06], + [2.20000000e01, 2.07350415e-06], + [3.30000000e01, 2.07518460e-06], + [4.40000000e01, 2.23394767e-06], + [5.50000000e01, 3.90646736e-06], + [6.60000000e01, 4.18619279e-06], + [7.70000000e01, 3.91675004e-06], + [8.80000000e01, 1.92451547e-06], + [9.90000000e01, 2.06988861e-06], + [1.10000000e02, 2.47614934e-06], + [1.21000000e02, 2.84889288e-06], + [1.32000000e02, 3.00529286e-06], + [1.43000000e02, 2.86310144e-06], + [1.54000000e02, 2.49967205e-06], + [1.65000000e02, 2.09673058e-06], + [1.76000000e02, 1.92431424e-06], + [1.87000000e02, 2.18829605e-06], + [1.98000000e02, 2.83032190e-06], + [2.09000000e02, 3.62584708e-06], + [2.20000000e02, 4.36871153e-06], + [2.31000000e02, 4.89806866e-06], + [2.42000000e02, 5.16145928e-06], + [2.53000000e02, 5.24684298e-06], + [2.64000000e02, 5.26428707e-06], + [2.75000000e02, 5.26650242e-06], + [2.86000000e02, 5.26667628e-06], + [2.97000000e02, 5.26668469e-06], + [3.08000000e02, 5.26668495e-06], + ], + ), + ], + ], + resampledLayers=[[np.array([[0.0, 0.0, 0.0]])], [np.array([[0.0, 0.0, 0.0]])]], calculationResults=RAT.outputs.CalculationResults( chiValues=np.array([4.6077885, 7.00028098]), sumChi=11.608069475997699, @@ -1344,388 +2958,1643 @@ def dream_results(): subRoughs=np.array([6.19503045, 6.19503045]), resample=np.array([0.0, 0.0]), ), - fitParams=np.array([6.19503045e+00, 1.84420960e+01, 2.11039621e+01, 8.75538121e+00, - 3.72292994e+00, 1.84624551e+01, 1.02316734e+01, 2.31156093e+01, - 1.09906265e+01, 5.71005361e+00, 1.67933822e+01, 1.72009856e+01, - 3.00260126e+01, 2.94448999e+01, 2.37113128e-06, 1.99006694e-06, - 6.01489149e-06, 1.59371685e-06]), - fitNames=["Substrate Roughness", "Oxide Thickness", "SAM Tails Thickness", "SAM Tails Hydration", - "SAM Roughness", "CW Thickness", "SAM Heads Thickness", "SAM Heads Hydration", - "Bilayer Heads Thickness", "Bilayer Roughness", "Bilayer Tails Thickness", "Bilayer Tails Hydration", - "Bilayer Heads Hydration", "Oxide Hydration", "Background parameter D2O", "Background parameter SMW", - "D2O", "SMW"], + fitParams=np.array( + [ + 6.19503045e00, + 1.84420960e01, + 2.11039621e01, + 8.75538121e00, + 3.72292994e00, + 1.84624551e01, + 1.02316734e01, + 2.31156093e01, + 1.09906265e01, + 5.71005361e00, + 1.67933822e01, + 1.72009856e01, + 3.00260126e01, + 2.94448999e01, + 2.37113128e-06, + 1.99006694e-06, + 6.01489149e-06, + 1.59371685e-06, + ], + ), + fitNames=[ + "Substrate Roughness", + "Oxide Thickness", + "SAM Tails Thickness", + "SAM Tails Hydration", + "SAM Roughness", + "CW Thickness", + "SAM Heads Thickness", + "SAM Heads Hydration", + "Bilayer Heads Thickness", + "Bilayer Roughness", + "Bilayer Tails Thickness", + "Bilayer Tails Hydration", + "Bilayer Heads Hydration", + "Oxide Hydration", + "Background parameter D2O", + "Background parameter SMW", + "D2O", + "SMW", + ], predictionIntervals=RAT.outputs.PredictionIntervals( - reflectivity=[np.array([[1.00000560e+00, 7.07687612e-01, 2.08315160e-02, 2.40787966e-03, - 2.17627660e-03, 2.54301700e-03, 1.76309827e-03, 6.15269679e-04, - 3.43679710e-05, 7.93625275e-05, 1.27760549e-04, 3.35799941e-05, - 6.75048751e-06, 7.64258431e-06, 6.05800395e-06, 5.60288298e-06, - 5.60333677e-06, 5.60223341e-06, 5.60206667e-06, 5.60206314e-06, - 5.60206314e-06], - [1.00000560e+00, 7.07687612e-01, 2.08315160e-02, 2.40787966e-03, - 2.17627660e-03, 2.54301700e-03, 1.76309827e-03, 6.15269679e-04, - 3.43679710e-05, 7.93625275e-05, 1.27760549e-04, 3.35799941e-05, - 6.75048751e-06, 7.64258431e-06, 6.05800395e-06, 5.60288298e-06, - 5.60333677e-06, 5.60223341e-06, 5.60206667e-06, 5.60206314e-06, - 5.60206314e-06], - [1.55201098e+01, 1.53748894e+01, 1.50402011e+01, 1.50299478e+01, - 1.50290525e+01, 1.50287935e+01, 1.50282126e+01, 1.50276044e+01, - 1.50273248e+01, 1.50273185e+01, 1.50272997e+01, 1.50272498e+01, - 1.50272364e+01, 1.50272347e+01, 1.50272335e+01, 1.50272330e+01, - 1.50272329e+01, 1.50272328e+01, 1.50272328e+01, 1.50272328e+01, - 1.50272328e+01], - [2.91397425e+01, 2.91324974e+01, 2.91281368e+01, 2.91255472e+01, - 2.91240295e+01, 2.91231836e+01, 2.91227893e+01, 2.91226872e+01, - 2.91226902e+01, 2.91226359e+01, 2.91225541e+01, 2.91225457e+01, - 2.91225448e+01, 2.91225407e+01, 2.91225398e+01, 2.91225393e+01, - 2.91225391e+01, 2.91225390e+01, 2.91225390e+01, 2.91225389e+01, - 2.91225389e+01], - [2.91397425e+01, 2.91324974e+01, 2.91281368e+01, 2.91255472e+01, - 2.91240295e+01, 2.91231836e+01, 2.91227893e+01, 2.91226872e+01, - 2.91226902e+01, 2.91226359e+01, 2.91225541e+01, 2.91225457e+01, - 2.91225448e+01, 2.91225407e+01, 2.91225398e+01, 2.91225393e+01, - 2.91225391e+01, 2.91225390e+01, 2.91225390e+01, 2.91225389e+01, - 2.91225389e+01]]), - np.array([[7.82198834e-01, 2.84434275e-02, 3.42472318e-03, 1.45801296e-03, - 2.02213149e-03, 2.00826891e-03, 1.27577246e-03, 4.27099544e-04, - 2.07304219e-05, 4.88683542e-05, 4.14000873e-05, 1.57801531e-05, - 2.08705254e-06, 2.27936213e-06, 1.09950377e-06, 7.09021331e-07, - 7.09539552e-07, 7.08228574e-07, 7.08101644e-07, 7.08098642e-07, - 7.08098641e-07], - [7.82198834e-01, 2.84434275e-02, 3.42472318e-03, 1.45801296e-03, - 2.02213149e-03, 2.00826891e-03, 1.27577246e-03, 4.27099544e-04, - 2.07304219e-05, 4.88683542e-05, 4.14000873e-05, 1.57801531e-05, - 2.08705254e-06, 2.27936213e-06, 1.09950377e-06, 7.09021331e-07, - 7.09539552e-07, 7.08228574e-07, 7.08101644e-07, 7.08098642e-07, - 7.08098641e-07], - [8.94587126e-01, 4.07514176e-01, 3.66355642e-02, 1.22207690e-02, - 5.91350591e-03, 3.17495745e-03, 1.50593011e-03, 5.80889579e-04, - 2.44654124e-04, 1.40925782e-04, 6.26965047e-05, 1.91064075e-05, - 9.45189755e-06, 6.16421495e-06, 5.17046678e-06, 4.24101973e-06, - 3.82067554e-06, 3.55352666e-06, 3.38981154e-06, 3.29631224e-06, - 3.25061722e-06], - [1.00000560e+00, 7.63076662e-01, 6.77868181e-02, 2.23160672e-02, - 9.56355479e-03, 4.26929321e-03, 1.72181442e-03, 7.25142247e-04, - 4.54691084e-04, 2.27274223e-04, 8.54009496e-05, 2.26525796e-05, - 1.63600080e-05, 9.80814666e-06, 8.98896697e-06, 7.55397947e-06, - 6.73887287e-06, 6.22237216e-06, 5.90521384e-06, 5.72401646e-06, - 5.63546023e-06], - [1.00000560e+00, 7.63076662e-01, 6.77868181e-02, 2.23160672e-02, - 9.56355479e-03, 4.26929321e-03, 1.72181442e-03, 7.25142247e-04, - 4.54691084e-04, 2.27274223e-04, 8.54009496e-05, 2.26525796e-05, - 1.63600080e-05, 9.80814666e-06, 8.98896697e-06, 7.55397947e-06, - 6.73887287e-06, 6.22237216e-06, 5.90521384e-06, 5.72401646e-06, - 5.63546023e-06]])], - sld=[[np.array([[-2.26996619e-06, -2.26938579e-06, -2.26877469e-06, ..., - 9.14568096e-07, 9.14567880e-07, 9.14567682e-07], - [-2.26996619e-06, -2.26938579e-06, -2.26877469e-06, ..., - 9.14568096e-07, 9.14567880e-07, 9.14567682e-07], - [-1.67970553e-07, -1.67671068e-07, -1.67355735e-07, ..., - 3.31763635e-06, 3.31763624e-06, 3.31763614e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.87958515e-06, 5.87958515e-06, 5.87958515e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.87958515e-06, 5.87958515e-06, 5.87958515e-06]])], - [np.array([[-1.35107208e-06, -1.34966627e-06, -1.34817823e-06, ..., - 4.65378475e-06, 4.65378475e-06, 4.65378475e-06], - [-1.35107208e-06, -1.34966627e-06, -1.34817823e-06, ..., - 4.65378475e-06, 4.65378475e-06, 4.65378475e-06], - [3.06178804e-07, 3.06904206e-07, 3.07672036e-07, ..., - 4.92477587e-06, 4.92477714e-06, 4.92477830e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.17896134e-06, 5.17896381e-06, 5.17896605e-06], - [2.07300000e-06, 2.07300000e-06, 2.07300001e-06, ..., - 5.17896134e-06, 5.17896381e-06, 5.17896605e-06]])]], - reflectivityXData=[np.array([[0.011403, 0.013861, 0.016848, 0.020479, 0.024892, 0.030256, - 0.036777, 0.044702, 0.054336, 0.066045, 0.080279, 0.097579, - 0.118610, 0.144170, 0.175240, 0.213000, 0.258910, 0.314700, - 0.382520, 0.464960, 0.565160]]), - np.array([[0.011403, 0.013861, 0.016848, 0.020479, 0.024892, 0.030256, - 0.036777, 0.044702, 0.054336, 0.066045, 0.080279, 0.097579, - 0.118610, 0.144170, 0.175240, 0.213000, 0.258910, 0.314700, - 0.382520, 0.464960, 0.565160]])], - sldXData=[[np.array([[0., 11., 22., 33., 44., 55., 66., 77., 88., 99., 110., - 121., 132., 143., 154., 165., 176., 187., 198., 209., 220., 231., - 242., 253., 264., 275., 286., 297., 308., 319.]]), - np.array([[0., 11., 22., 33., 44., 55., 66., 77., 88., 99., 110., - 121., 132., 143., 154., 165., 176., 187., 198., 209., 220., 231., - 242., 253., 264., 275., 286., 297., 308., 319.]])]], - sampleChi=np.array([1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16, - 1.12748255e+06, 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, - 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, 1.12748255e+06, - 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, 1.46133559e+16, - 1.46133559e+16, 1.12748255e+06, 1.12748255e+06, 1.46133559e+16]), + reflectivity=[ + np.array( + [ + [ + 1.00000560e00, + 7.07687612e-01, + 2.08315160e-02, + 2.40787966e-03, + 2.17627660e-03, + 2.54301700e-03, + 1.76309827e-03, + 6.15269679e-04, + 3.43679710e-05, + 7.93625275e-05, + 1.27760549e-04, + 3.35799941e-05, + 6.75048751e-06, + 7.64258431e-06, + 6.05800395e-06, + 5.60288298e-06, + 5.60333677e-06, + 5.60223341e-06, + 5.60206667e-06, + 5.60206314e-06, + 5.60206314e-06, + ], + [ + 1.00000560e00, + 7.07687612e-01, + 2.08315160e-02, + 2.40787966e-03, + 2.17627660e-03, + 2.54301700e-03, + 1.76309827e-03, + 6.15269679e-04, + 3.43679710e-05, + 7.93625275e-05, + 1.27760549e-04, + 3.35799941e-05, + 6.75048751e-06, + 7.64258431e-06, + 6.05800395e-06, + 5.60288298e-06, + 5.60333677e-06, + 5.60223341e-06, + 5.60206667e-06, + 5.60206314e-06, + 5.60206314e-06, + ], + [ + 1.55201098e01, + 1.53748894e01, + 1.50402011e01, + 1.50299478e01, + 1.50290525e01, + 1.50287935e01, + 1.50282126e01, + 1.50276044e01, + 1.50273248e01, + 1.50273185e01, + 1.50272997e01, + 1.50272498e01, + 1.50272364e01, + 1.50272347e01, + 1.50272335e01, + 1.50272330e01, + 1.50272329e01, + 1.50272328e01, + 1.50272328e01, + 1.50272328e01, + 1.50272328e01, + ], + [ + 2.91397425e01, + 2.91324974e01, + 2.91281368e01, + 2.91255472e01, + 2.91240295e01, + 2.91231836e01, + 2.91227893e01, + 2.91226872e01, + 2.91226902e01, + 2.91226359e01, + 2.91225541e01, + 2.91225457e01, + 2.91225448e01, + 2.91225407e01, + 2.91225398e01, + 2.91225393e01, + 2.91225391e01, + 2.91225390e01, + 2.91225390e01, + 2.91225389e01, + 2.91225389e01, + ], + [ + 2.91397425e01, + 2.91324974e01, + 2.91281368e01, + 2.91255472e01, + 2.91240295e01, + 2.91231836e01, + 2.91227893e01, + 2.91226872e01, + 2.91226902e01, + 2.91226359e01, + 2.91225541e01, + 2.91225457e01, + 2.91225448e01, + 2.91225407e01, + 2.91225398e01, + 2.91225393e01, + 2.91225391e01, + 2.91225390e01, + 2.91225390e01, + 2.91225389e01, + 2.91225389e01, + ], + ], + ), + np.array( + [ + [ + 7.82198834e-01, + 2.84434275e-02, + 3.42472318e-03, + 1.45801296e-03, + 2.02213149e-03, + 2.00826891e-03, + 1.27577246e-03, + 4.27099544e-04, + 2.07304219e-05, + 4.88683542e-05, + 4.14000873e-05, + 1.57801531e-05, + 2.08705254e-06, + 2.27936213e-06, + 1.09950377e-06, + 7.09021331e-07, + 7.09539552e-07, + 7.08228574e-07, + 7.08101644e-07, + 7.08098642e-07, + 7.08098641e-07, + ], + [ + 7.82198834e-01, + 2.84434275e-02, + 3.42472318e-03, + 1.45801296e-03, + 2.02213149e-03, + 2.00826891e-03, + 1.27577246e-03, + 4.27099544e-04, + 2.07304219e-05, + 4.88683542e-05, + 4.14000873e-05, + 1.57801531e-05, + 2.08705254e-06, + 2.27936213e-06, + 1.09950377e-06, + 7.09021331e-07, + 7.09539552e-07, + 7.08228574e-07, + 7.08101644e-07, + 7.08098642e-07, + 7.08098641e-07, + ], + [ + 8.94587126e-01, + 4.07514176e-01, + 3.66355642e-02, + 1.22207690e-02, + 5.91350591e-03, + 3.17495745e-03, + 1.50593011e-03, + 5.80889579e-04, + 2.44654124e-04, + 1.40925782e-04, + 6.26965047e-05, + 1.91064075e-05, + 9.45189755e-06, + 6.16421495e-06, + 5.17046678e-06, + 4.24101973e-06, + 3.82067554e-06, + 3.55352666e-06, + 3.38981154e-06, + 3.29631224e-06, + 3.25061722e-06, + ], + [ + 1.00000560e00, + 7.63076662e-01, + 6.77868181e-02, + 2.23160672e-02, + 9.56355479e-03, + 4.26929321e-03, + 1.72181442e-03, + 7.25142247e-04, + 4.54691084e-04, + 2.27274223e-04, + 8.54009496e-05, + 2.26525796e-05, + 1.63600080e-05, + 9.80814666e-06, + 8.98896697e-06, + 7.55397947e-06, + 6.73887287e-06, + 6.22237216e-06, + 5.90521384e-06, + 5.72401646e-06, + 5.63546023e-06, + ], + [ + 1.00000560e00, + 7.63076662e-01, + 6.77868181e-02, + 2.23160672e-02, + 9.56355479e-03, + 4.26929321e-03, + 1.72181442e-03, + 7.25142247e-04, + 4.54691084e-04, + 2.27274223e-04, + 8.54009496e-05, + 2.26525796e-05, + 1.63600080e-05, + 9.80814666e-06, + 8.98896697e-06, + 7.55397947e-06, + 6.73887287e-06, + 6.22237216e-06, + 5.90521384e-06, + 5.72401646e-06, + 5.63546023e-06, + ], + ], + ), + ], + sld=[ + [ + np.array( + [ + [ + -2.26996619e-06, + -2.26938579e-06, + -2.26877469e-06, + ..., + 9.14568096e-07, + 9.14567880e-07, + 9.14567682e-07, + ], + [ + -2.26996619e-06, + -2.26938579e-06, + -2.26877469e-06, + ..., + 9.14568096e-07, + 9.14567880e-07, + 9.14567682e-07, + ], + [ + -1.67970553e-07, + -1.67671068e-07, + -1.67355735e-07, + ..., + 3.31763635e-06, + 3.31763624e-06, + 3.31763614e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.87958515e-06, + 5.87958515e-06, + 5.87958515e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.87958515e-06, + 5.87958515e-06, + 5.87958515e-06, + ], + ], + ), + ], + [ + np.array( + [ + [ + -1.35107208e-06, + -1.34966627e-06, + -1.34817823e-06, + ..., + 4.65378475e-06, + 4.65378475e-06, + 4.65378475e-06, + ], + [ + -1.35107208e-06, + -1.34966627e-06, + -1.34817823e-06, + ..., + 4.65378475e-06, + 4.65378475e-06, + 4.65378475e-06, + ], + [ + 3.06178804e-07, + 3.06904206e-07, + 3.07672036e-07, + ..., + 4.92477587e-06, + 4.92477714e-06, + 4.92477830e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.17896134e-06, + 5.17896381e-06, + 5.17896605e-06, + ], + [ + 2.07300000e-06, + 2.07300000e-06, + 2.07300001e-06, + ..., + 5.17896134e-06, + 5.17896381e-06, + 5.17896605e-06, + ], + ], + ), + ], + ], + reflectivityXData=[ + np.array( + [ + [ + 0.011403, + 0.013861, + 0.016848, + 0.020479, + 0.024892, + 0.030256, + 0.036777, + 0.044702, + 0.054336, + 0.066045, + 0.080279, + 0.097579, + 0.118610, + 0.144170, + 0.175240, + 0.213000, + 0.258910, + 0.314700, + 0.382520, + 0.464960, + 0.565160, + ], + ], + ), + np.array( + [ + [ + 0.011403, + 0.013861, + 0.016848, + 0.020479, + 0.024892, + 0.030256, + 0.036777, + 0.044702, + 0.054336, + 0.066045, + 0.080279, + 0.097579, + 0.118610, + 0.144170, + 0.175240, + 0.213000, + 0.258910, + 0.314700, + 0.382520, + 0.464960, + 0.565160, + ], + ], + ), + ], + sldXData=[ + [ + np.array( + [ + [ + 0.0, + 11.0, + 22.0, + 33.0, + 44.0, + 55.0, + 66.0, + 77.0, + 88.0, + 99.0, + 110.0, + 121.0, + 132.0, + 143.0, + 154.0, + 165.0, + 176.0, + 187.0, + 198.0, + 209.0, + 220.0, + 231.0, + 242.0, + 253.0, + 264.0, + 275.0, + 286.0, + 297.0, + 308.0, + 319.0, + ], + ], + ), + np.array( + [ + [ + 0.0, + 11.0, + 22.0, + 33.0, + 44.0, + 55.0, + 66.0, + 77.0, + 88.0, + 99.0, + 110.0, + 121.0, + 132.0, + 143.0, + 154.0, + 165.0, + 176.0, + 187.0, + 198.0, + 209.0, + 220.0, + 231.0, + 242.0, + 253.0, + 264.0, + 275.0, + 286.0, + 297.0, + 308.0, + 319.0, + ], + ], + ), + ], + ], + sampleChi=np.array( + [ + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.46133559e16, + 1.12748255e06, + 1.12748255e06, + 1.46133559e16, + ], + ), ), confidenceIntervals=RAT.outputs.ConfidenceIntervals( - percentile65=np.array([[-1.29074586e-231, 8.33251318e+000, 1.75397363e+001, 1.75397363e+001, - 9.85302945e+000, 9.85302945e+000, 8.34197863e+000, 8.34197863e+000, - 1.65750684e+001, 1.45435510e+001, 1.45435510e+001, 1.52609047e+001, - 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 7.08098641e-007, 4.65378475e-006], - [8.33251318e+000, 5.48185565e+001, 5.48185565e+001, 4.57554170e+001, - 4.57554170e+001, 1.17557273e+001, 1.17557273e+001, 3.18752608e+001, - 3.18752608e+001, 1.65750684e+001, 1.52609047e+001, 4.88237113e+001, - 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, - 5.87958515e-006, 5.87958515e-006]]), - percentile95=np.array([[-1.29074586e-231, 8.33251318e+000, 1.75397363e+001, 1.75397363e+001, - 9.85302945e+000, 9.85302945e+000, 8.34197863e+000, 8.34197863e+000, - 1.65750684e+001, 1.45435510e+001, 1.45435510e+001, 1.52609047e+001, - 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 7.08098641e-007, 4.65378475e-006], - [8.33251318e+000, 5.48185565e+001, 5.48185565e+001, 4.57554170e+001, - 4.57554170e+001, 1.17557273e+001, 1.17557273e+001, 3.18752608e+001, - 3.18752608e+001, 1.65750684e+001, 1.52609047e+001, 4.88237113e+001, - 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, - 5.87958515e-006, 5.87958515e-006]]), - mean=np.array([[4.16625659e+00, 3.15755349e+01, 3.61791464e+01, 3.16475766e+01, - 2.78042232e+01, 1.08043784e+01, 1.00488530e+01, 2.01086197e+01, - 2.42251646e+01, 1.55593097e+01, 1.49022278e+01, 3.20423080e+01, - 4.85551946e+01, 3.87046084e+01, 1.45612723e+01, 3.15508089e-06, - 3.29384190e-06, 5.26668495e-06]]), + percentile65=np.array( + [ + [ + -1.29074586e-231, + 8.33251318e000, + 1.75397363e001, + 1.75397363e001, + 9.85302945e000, + 9.85302945e000, + 8.34197863e000, + 8.34197863e000, + 1.65750684e001, + 1.45435510e001, + 1.45435510e001, + 1.52609047e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 7.08098641e-007, + 4.65378475e-006, + ], + [ + 8.33251318e000, + 5.48185565e001, + 5.48185565e001, + 4.57554170e001, + 4.57554170e001, + 1.17557273e001, + 1.17557273e001, + 3.18752608e001, + 3.18752608e001, + 1.65750684e001, + 1.52609047e001, + 4.88237113e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 5.87958515e-006, + 5.87958515e-006, + ], + ], + ), + percentile95=np.array( + [ + [ + -1.29074586e-231, + 8.33251318e000, + 1.75397363e001, + 1.75397363e001, + 9.85302945e000, + 9.85302945e000, + 8.34197863e000, + 8.34197863e000, + 1.65750684e001, + 1.45435510e001, + 1.45435510e001, + 1.52609047e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 7.08098641e-007, + 4.65378475e-006, + ], + [ + 8.33251318e000, + 5.48185565e001, + 5.48185565e001, + 4.57554170e001, + 4.57554170e001, + 1.17557273e001, + 1.17557273e001, + 3.18752608e001, + 3.18752608e001, + 1.65750684e001, + 1.52609047e001, + 4.88237113e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 5.87958515e-006, + 5.87958515e-006, + ], + ], + ), + mean=np.array( + [ + [ + 4.16625659e00, + 3.15755349e01, + 3.61791464e01, + 3.16475766e01, + 2.78042232e01, + 1.08043784e01, + 1.00488530e01, + 2.01086197e01, + 2.42251646e01, + 1.55593097e01, + 1.49022278e01, + 3.20423080e01, + 4.85551946e01, + 3.87046084e01, + 1.45612723e01, + 3.15508089e-06, + 3.29384190e-06, + 5.26668495e-06, + ], + ], + ), ), dreamParams=RAT.outputs.DreamParams( nParams=18.0, @@ -1746,51 +4615,114 @@ def dream_results(): ABC=False, IO=False, storeOutput=False, - R=np.array([[0.]]), + R=np.array([[0.0]]), ), dreamOutput=RAT.outputs.DreamOutput( - allChains=np.array([[[8.33251318e+00], - [5.48185565e+01], - [1.75397363e+01], - [4.57554170e+01], - [9.85302945e+00], - [1.17557273e+01], - [8.34197863e+00], - [3.18752608e+01], - [1.65750684e+01], - [1.45435510e+01], - [1.52609047e+01], - [4.88237113e+01], - [4.82866779e+01], - [2.91225389e+01], - [5.60206314e-06], - [7.08098641e-07], - [5.87958515e-06], - [4.65378475e-06], - [-4.86509830e+01], - [-5.63741277e+05]]]), + allChains=np.array( + [ + [ + [8.33251318e00], + [5.48185565e01], + [1.75397363e01], + [4.57554170e01], + [9.85302945e00], + [1.17557273e01], + [8.34197863e00], + [3.18752608e01], + [1.65750684e01], + [1.45435510e01], + [1.52609047e01], + [4.88237113e01], + [4.82866779e01], + [2.91225389e01], + [5.60206314e-06], + [7.08098641e-07], + [5.87958515e-06], + [4.65378475e-06], + [-4.86509830e01], + [-5.63741277e05], + ], + ], + ), outlierChains=np.array([[0.0, 0.0]]), runtime=2.6e-06, iteration=2.0, modelOutput=0.0, AR=np.array([[1.0, np.nan]]), - R_stat=np.array([[1.0, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, - np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan, np.nan]]), - CR=np.array([[1., 0.33333333, 0.33333333, 0.33333333]]), + R_stat=np.array( + [ + [ + 1.0, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + np.nan, + ], + ], + ), + CR=np.array([[1.0, 0.33333333, 0.33333333, 0.33333333]]), ), nestedSamplerOutput=RAT.outputs.NestedSamplerOutput( logZ=0.0, nestSamples=np.array([[0.0, 0.0]]), postSamples=np.array([[0.0, 0.0]]), ), - chain=np.array([[-1.29231905e-231, 8.33251318e+000, 5.48185565e+001, 1.75397363e+001, - 4.57554170e+001, 9.85302945e+000, 1.17557273e+001, 8.34197863e+000, - 3.18752608e+001, 1.65750684e+001, 1.45435510e+001, 1.52609047e+001, - 4.88237113e+001, 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, - 7.08098641e-007, 5.87958515e-006], - [8.33251318e+000, 5.48185565e+001, 1.75397363e+001, 4.57554170e+001, - 9.85302945e+000, 1.17557273e+001, 8.34197863e+000, 3.18752608e+001, - 1.65750684e+001, 1.45435510e+001, 1.52609047e+001, 4.88237113e+001, - 4.82866779e+001, 2.91225389e+001, 5.60206314e-006, 7.08098641e-007, - 5.87958515e-006, 4.65378475e-006]]), + chain=np.array( + [ + [ + -1.29231905e-231, + 8.33251318e000, + 5.48185565e001, + 1.75397363e001, + 4.57554170e001, + 9.85302945e000, + 1.17557273e001, + 8.34197863e000, + 3.18752608e001, + 1.65750684e001, + 1.45435510e001, + 1.52609047e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 5.87958515e-006, + ], + [ + 8.33251318e000, + 5.48185565e001, + 1.75397363e001, + 4.57554170e001, + 9.85302945e000, + 1.17557273e001, + 8.34197863e000, + 3.18752608e001, + 1.65750684e001, + 1.45435510e001, + 1.52609047e001, + 4.88237113e001, + 4.82866779e001, + 2.91225389e001, + 5.60206314e-006, + 7.08098641e-007, + 5.87958515e-006, + 4.65378475e-006, + ], + ], + ), ) diff --git a/tests/test_classlist.py b/tests/test_classlist.py index ad818e39..4839fb4a 100644 --- a/tests/test_classlist.py +++ b/tests/test_classlist.py @@ -26,7 +26,7 @@ def two_name_class_list(): @pytest.fixture def two_name_class_list_table(): """The table representation of the ClassList defined in the "two_name_class_list" fixture.""" - return( + return ( "+-------+-------+\n" "| index | name |\n" "+-------+-------+\n" @@ -43,12 +43,15 @@ def three_name_class_list(): class TestInitialisation: - @pytest.mark.parametrize("input_object", [ - (InputAttributes()), - (InputAttributes(name="Alice")), - (InputAttributes(surname="Morgan")), - "Alice", - ]) + @pytest.mark.parametrize( + "input_object", + [ + (InputAttributes()), + (InputAttributes(name="Alice")), + (InputAttributes(surname="Morgan")), + "Alice", + ], + ) def test_input_object(self, input_object: Any) -> None: """For an input of an object, the ClassList should contain a one-element list containing the object and _class_handle should be set to the type of the object. @@ -57,16 +60,19 @@ def test_input_object(self, input_object: Any) -> None: assert class_list.data == [input_object] assert isinstance(input_object, class_list._class_handle) - @pytest.mark.parametrize("input_sequence", [ - ([InputAttributes()]), - ([InputAttributes(name="Alice")]), - ([InputAttributes(surname="Morgan")]), - ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), - (InputAttributes(),), - (InputAttributes(name="Alice"),), - (InputAttributes(surname="Morgan"),), - (InputAttributes(name="Alice"), InputAttributes(name="Bob")), - ]) + @pytest.mark.parametrize( + "input_sequence", + [ + ([InputAttributes()]), + ([InputAttributes(name="Alice")]), + ([InputAttributes(surname="Morgan")]), + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), + (InputAttributes(),), + (InputAttributes(name="Alice"),), + (InputAttributes(surname="Morgan"),), + (InputAttributes(name="Alice"), InputAttributes(name="Bob")), + ], + ) def test_input_sequence(self, input_sequence: Sequence[object]) -> None: """For an input of a sequence, the ClassList should be a list equal to the input sequence, and _class_handle should be set to the type of the objects within. @@ -76,10 +82,13 @@ def test_input_sequence(self, input_sequence: Sequence[object]) -> None: for element in input_sequence: assert isinstance(element, class_list._class_handle) - @pytest.mark.parametrize("input_sequence", [ - ([InputAttributes(name="Alice"), SubInputAttributes(name="Bob")]), - ([SubInputAttributes(name="Alice"), InputAttributes(name="Bob")]), - ]) + @pytest.mark.parametrize( + "input_sequence", + [ + ([InputAttributes(name="Alice"), SubInputAttributes(name="Bob")]), + ([SubInputAttributes(name="Alice"), InputAttributes(name="Bob")]), + ], + ) def test_input_sequence_subclass(self, input_sequence: Sequence[object]) -> None: """For an input of a sequence containing objects of a class and its subclasses, the ClassList should be a list equal to the input sequence, and _class_handle should be set to the type of the parent class. @@ -98,25 +107,35 @@ def test_empty_input(self, empty_input: Sequence[object]) -> None: assert class_list.data == [] assert not hasattr(class_list, "_class_handle") - @pytest.mark.parametrize("input_list", [ - ([InputAttributes(name="Alice"), dict(name="Bob")]), - ]) + @pytest.mark.parametrize( + "input_list", + [ + ([InputAttributes(name="Alice"), dict(name="Bob")]), + ], + ) def test_different_classes(self, input_list: Sequence[object]) -> None: """If we initialise a ClassList with an input containing multiple classes, we should raise a ValueError.""" - with pytest.raises(ValueError, - match=f"Input list contains elements of type other than '{type(input_list[0]).__name__}'"): + with pytest.raises( + ValueError, + match=f"Input list contains elements of type other than '{type(input_list[0]).__name__}'", + ): ClassList(input_list) - @pytest.mark.parametrize("input_list, name_field", [ - ([InputAttributes(name="Alice"), InputAttributes(name="Alice")], "name"), - ([InputAttributes(id="Alice"), InputAttributes(id="Alice")], "id"), - ]) + @pytest.mark.parametrize( + "input_list, name_field", + [ + ([InputAttributes(name="Alice"), InputAttributes(name="Alice")], "name"), + ([InputAttributes(id="Alice"), InputAttributes(id="Alice")], "id"), + ], + ) def test_identical_name_fields(self, input_list: Sequence[object], name_field: str) -> None: """If we initialise a ClassList with input objects with identical values of the name_field, we should raise a ValueError. """ - with pytest.raises(ValueError, - match=f"Input list contains objects with the same value of the {name_field} attribute"): + with pytest.raises( + ValueError, + match=f"Input list contains objects with the same value of the {name_field} attribute", + ): ClassList(input_list, name_field=name_field) @@ -133,9 +152,12 @@ def test_repr_empty_table() -> None: assert repr(ClassList(empty_attributes)) == repr([empty_attributes]) -@pytest.mark.parametrize("input_list", [ - (["Alice", "Bob"]), -]) +@pytest.mark.parametrize( + "input_list", + [ + (["Alice", "Bob"]), + ], +) def test_repr_list(input_list: list[str]) -> None: """For classes without the __dict__ attribute, we should be able to print the ClassList as a list.""" class_list = ClassList(input_list) @@ -147,11 +169,16 @@ def test_repr_empty_classlist() -> None: assert repr(ClassList()) == repr([]) -@pytest.mark.parametrize(["new_item", "expected_classlist"], [ - (InputAttributes(name="Eve"), ClassList([InputAttributes(name="Eve"), InputAttributes(name="Bob")])), - (InputAttributes(name="John", surname="Luther"), - ClassList([InputAttributes(name="John", surname="Luther"), InputAttributes(name="Bob")])), -]) +@pytest.mark.parametrize( + ["new_item", "expected_classlist"], + [ + (InputAttributes(name="Eve"), ClassList([InputAttributes(name="Eve"), InputAttributes(name="Bob")])), + ( + InputAttributes(name="John", surname="Luther"), + ClassList([InputAttributes(name="John", surname="Luther"), InputAttributes(name="Bob")]), + ), + ], +) def test_setitem(two_name_class_list: ClassList, new_item: InputAttributes, expected_classlist: ClassList) -> None: """We should be able to set values in an element of a ClassList using a new object.""" class_list = two_name_class_list @@ -159,18 +186,24 @@ def test_setitem(two_name_class_list: ClassList, new_item: InputAttributes, expe assert class_list == expected_classlist -@pytest.mark.parametrize("new_item", [ - (InputAttributes(name="Bob")), -]) +@pytest.mark.parametrize( + "new_item", + [ + (InputAttributes(name="Bob")), + ], +) def test_setitem_same_name_field(two_name_class_list: ClassList, new_item: InputAttributes) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" with pytest.raises(ValueError, match="Input list contains objects with the same value of the name attribute"): two_name_class_list[0] = new_item -@pytest.mark.parametrize("new_values", [ - "Bob", -]) +@pytest.mark.parametrize( + "new_values", + [ + "Bob", + ], +) def test_setitem_different_classes(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" with pytest.raises(ValueError, match="Input list contains elements of type other than 'InputAttributes'"): @@ -191,12 +224,15 @@ def test_delitem_not_present(two_name_class_list: ClassList) -> None: del class_list[2] -@pytest.mark.parametrize("added_list", [ - (ClassList(InputAttributes(name="Eve"))), - ([InputAttributes(name="Eve")]), - (InputAttributes(name="Eve"),), - (InputAttributes(name="Eve")), -]) +@pytest.mark.parametrize( + "added_list", + [ + (ClassList(InputAttributes(name="Eve"))), + ([InputAttributes(name="Eve")]), + (InputAttributes(name="Eve"),), + (InputAttributes(name="Eve")), + ], +) def test_iadd(two_name_class_list: ClassList, added_list: Iterable, three_name_class_list: ClassList) -> None: """We should be able to use the "+=" operator to add iterables to a ClassList. Individual objects should be wrapped in a list before being added. @@ -206,11 +242,14 @@ def test_iadd(two_name_class_list: ClassList, added_list: Iterable, three_name_c assert class_list == three_name_class_list -@pytest.mark.parametrize("added_list", [ - (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")])), - ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), - (InputAttributes(name="Alice"), InputAttributes(name="Bob")), -]) +@pytest.mark.parametrize( + "added_list", + [ + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")])), + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), + (InputAttributes(name="Alice"), InputAttributes(name="Bob")), + ], +) def test_iadd_empty_classlist(added_list: Sequence, two_name_class_list: ClassList) -> None: """We should be able to use the "+=" operator to add iterables to an empty ClassList, whilst also setting _class_handle. @@ -224,75 +263,108 @@ def test_iadd_empty_classlist(added_list: Sequence, two_name_class_list: ClassLi def test_mul(two_name_class_list: ClassList) -> None: """If we use the "*" operator on a ClassList, we should raise a TypeError.""" n = 2 - with pytest.raises(TypeError, match=re.escape(f"unsupported operand type(s) for *: " - f"'{two_name_class_list.__class__.__name__}' and " - f"'{n.__class__.__name__}'")): + with pytest.raises( + TypeError, + match=re.escape( + f"unsupported operand type(s) for *: " + f"'{two_name_class_list.__class__.__name__}' and " + f"'{n.__class__.__name__}'", + ), + ): two_name_class_list * n def test_rmul(two_name_class_list: ClassList) -> None: """If we use the "*" operator on a ClassList, we should raise a TypeError.""" n = 2 - with pytest.raises(TypeError, match=re.escape(f"unsupported operand type(s) for *: " - f"'{n.__class__.__name__}' and " - f"'{two_name_class_list.__class__.__name__}'")): + with pytest.raises( + TypeError, + match=re.escape( + f"unsupported operand type(s) for *: " + f"'{n.__class__.__name__}' and " + f"'{two_name_class_list.__class__.__name__}'", + ), + ): n * two_name_class_list def test_imul(two_name_class_list: ClassList) -> None: """If we use the "*=" operator on a ClassList, we should raise a TypeError.""" n = 2 - with pytest.raises(TypeError, match=re.escape(f"unsupported operand type(s) for *=: " - f"'{two_name_class_list.__class__.__name__}' and " - f"'{n.__class__.__name__}'")): + with pytest.raises( + TypeError, + match=re.escape( + f"unsupported operand type(s) for *=: " + f"'{two_name_class_list.__class__.__name__}' and " + f"'{n.__class__.__name__}'", + ), + ): two_name_class_list *= n -@pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Eve")), -]) -def test_append_object(two_name_class_list: ClassList, - new_object: object, - three_name_class_list: ClassList) -> None: +@pytest.mark.parametrize( + "new_object", + [ + (InputAttributes(name="Eve")), + ], +) +def test_append_object(two_name_class_list: ClassList, new_object: object, three_name_class_list: ClassList) -> None: """We should be able to append to a ClassList using a new object.""" class_list = two_name_class_list class_list.append(new_object) assert class_list == three_name_class_list -@pytest.mark.parametrize("new_values", [ - ({"name": "Eve"}), -]) -def test_append_kwargs(two_name_class_list: ClassList, - new_values: dict[str, Any], - three_name_class_list: ClassList) -> None: +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Eve"}), + ], +) +def test_append_kwargs( + two_name_class_list: ClassList, + new_values: dict[str, Any], + three_name_class_list: ClassList, +) -> None: """We should be able to append to a ClassList using keyword arguments.""" class_list = two_name_class_list class_list.append(**new_values) assert class_list == three_name_class_list -@pytest.mark.parametrize(["new_object", "new_values"], [ - (InputAttributes(name="Eve"), {"name": "John"}), -]) -def test_append_object_and_kwargs(two_name_class_list: ClassList, - new_object: object, - new_values: dict[str, Any], - three_name_class_list: ClassList) -> None: +@pytest.mark.parametrize( + ["new_object", "new_values"], + [ + (InputAttributes(name="Eve"), {"name": "John"}), + ], +) +def test_append_object_and_kwargs( + two_name_class_list: ClassList, + new_object: object, + new_values: dict[str, Any], + three_name_class_list: ClassList, +) -> None: """If we append to a ClassList using a new object and keyword arguments, we raise a warning, and append the object, discarding the keyword arguments. """ class_list = two_name_class_list with pytest.warns(SyntaxWarning): - warnings.warn("ClassList.append() called with both an object and keyword arguments. " - "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) + warnings.warn( + "ClassList.append() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", + SyntaxWarning, + stacklevel=2, + ) class_list.append(new_object, **new_values) assert class_list == three_name_class_list -@pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Alice")), -]) +@pytest.mark.parametrize( + "new_object", + [ + (InputAttributes(name="Alice")), + ], +) def test_append_object_empty_classlist(new_object: object, one_name_class_list: ClassList) -> None: """We should be able to append to an empty ClassList using a new object, whilst also setting _class_handle.""" class_list = ClassList() @@ -301,84 +373,121 @@ def test_append_object_empty_classlist(new_object: object, one_name_class_list: assert isinstance(new_object, class_list._class_handle) -@pytest.mark.parametrize("new_values", [ - ({"name": "Alice"}), -]) +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Alice"}), + ], +) def test_append_kwargs_empty_classlist(new_values: dict[str, Any]) -> None: """If we append to an empty ClassList using keyword arguments we should raise a TypeError.""" class_list = ClassList() - with pytest.raises(TypeError, match=re.escape("ClassList.append() called with keyword arguments for a ClassList " - "without a class defined. Call ClassList.append() with an object to " - "define the class.")): + with pytest.raises( + TypeError, + match=re.escape( + "ClassList.append() called with keyword arguments for a ClassList " + "without a class defined. Call ClassList.append() with an object to " + "define the class.", + ), + ): class_list.append(**new_values) -@pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Alice")), -]) +@pytest.mark.parametrize( + "new_object", + [ + (InputAttributes(name="Alice")), + ], +) def test_append_object_same_name_field(two_name_class_list: ClassList, new_object: object) -> None: """If we append an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" - with pytest.raises(ValueError, match=f"Input list contains objects with the same value of the " - f"{two_name_class_list.name_field} attribute"): + with pytest.raises( + ValueError, + match=f"Input list contains objects with the same value of the " f"{two_name_class_list.name_field} attribute", + ): two_name_class_list.append(new_object) -@pytest.mark.parametrize("new_values", [ - ({"name": "Alice"}), -]) +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Alice"}), + ], +) def test_append_kwargs_same_name_field(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we append an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" - with pytest.raises(ValueError, match=f"Input arguments contain the {two_name_class_list.name_field} " - f"'{new_values[two_name_class_list.name_field]}', " - f"which is already specified in the ClassList"): + with pytest.raises( + ValueError, + match=f"Input arguments contain the {two_name_class_list.name_field} " + f"'{new_values[two_name_class_list.name_field]}', " + f"which is already specified in the ClassList", + ): two_name_class_list.append(**new_values) -@pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Eve")), -]) +@pytest.mark.parametrize( + "new_object", + [ + (InputAttributes(name="Eve")), + ], +) def test_insert_object(two_name_class_list: ClassList, new_object: object) -> None: """We should be able to insert an object within a ClassList using a new object.""" two_name_class_list.insert(1, new_object) - assert two_name_class_list == ClassList([InputAttributes(name="Alice"), - InputAttributes(name="Eve"), - InputAttributes(name="Bob")]) + assert two_name_class_list == ClassList( + [InputAttributes(name="Alice"), InputAttributes(name="Eve"), InputAttributes(name="Bob")], + ) -@pytest.mark.parametrize("new_values", [ - ({"name": "Eve"}), -]) +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Eve"}), + ], +) def test_insert_kwargs(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """We should be able to insert an object within a ClassList using keyword arguments.""" two_name_class_list.insert(1, **new_values) - assert two_name_class_list == ClassList([InputAttributes(name="Alice"), - InputAttributes(name="Eve"), - InputAttributes(name="Bob")]) - - -@pytest.mark.parametrize(["new_object", "new_values"], [ - (InputAttributes(name="Eve"), {"name": "John"}), -]) -def test_insert_object_and_kwargs(two_name_class_list: ClassList, - new_object: object, - new_values: dict[str, Any], - three_name_class_list: ClassList) -> None: + assert two_name_class_list == ClassList( + [InputAttributes(name="Alice"), InputAttributes(name="Eve"), InputAttributes(name="Bob")], + ) + + +@pytest.mark.parametrize( + ["new_object", "new_values"], + [ + (InputAttributes(name="Eve"), {"name": "John"}), + ], +) +def test_insert_object_and_kwargs( + two_name_class_list: ClassList, + new_object: object, + new_values: dict[str, Any], + three_name_class_list: ClassList, +) -> None: """If call insert() on a ClassList using a new object and keyword arguments, we raise a warning, and append the object, discarding the keyword arguments. """ class_list = two_name_class_list with pytest.warns(SyntaxWarning): - warnings.warn("ClassList.insert() called with both an object and keyword arguments. " - "The keyword arguments will be ignored.", SyntaxWarning, stacklevel=2) + warnings.warn( + "ClassList.insert() called with both an object and keyword arguments. " + "The keyword arguments will be ignored.", + SyntaxWarning, + stacklevel=2, + ) class_list.insert(1, new_object, **new_values) - assert class_list == ClassList([InputAttributes(name="Alice"), - InputAttributes(name="Eve"), - InputAttributes(name="Bob")]) + assert class_list == ClassList( + [InputAttributes(name="Alice"), InputAttributes(name="Eve"), InputAttributes(name="Bob")], + ) -@pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Alice")), -]) +@pytest.mark.parametrize( + "new_object", + [ + (InputAttributes(name="Alice")), + ], +) def test_insert_object_empty_classlist(new_object: object, one_name_class_list: ClassList) -> None: """We should be able to insert a new object into an empty ClassList, whilst also setting _class_handle.""" class_list = ClassList() @@ -387,43 +496,65 @@ def test_insert_object_empty_classlist(new_object: object, one_name_class_list: assert isinstance(new_object, class_list._class_handle) -@pytest.mark.parametrize("new_values", [ - ({"name": "Alice"}), -]) +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Alice"}), + ], +) def test_insert_kwargs_empty_classlist(new_values: dict[str, Any]) -> None: """If we append to an empty ClassList using keyword arguments we should raise a TypeError.""" class_list = ClassList() - with pytest.raises(TypeError, match=re.escape("ClassList.insert() called with keyword arguments for a ClassList " - "without a class defined. Call ClassList.insert() with an object to " - "define the class.")): + with pytest.raises( + TypeError, + match=re.escape( + "ClassList.insert() called with keyword arguments for a ClassList " + "without a class defined. Call ClassList.insert() with an object to " + "define the class.", + ), + ): class_list.insert(0, **new_values) -@pytest.mark.parametrize("new_object", [ - (InputAttributes(name="Alice")), -]) +@pytest.mark.parametrize( + "new_object", + [ + (InputAttributes(name="Alice")), + ], +) def test_insert_object_same_name(two_name_class_list: ClassList, new_object: object) -> None: """If we insert an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" - with pytest.raises(ValueError, match=f"Input list contains objects with the same value of the " - f"{two_name_class_list.name_field} attribute"): + with pytest.raises( + ValueError, + match=f"Input list contains objects with the same value of the " f"{two_name_class_list.name_field} attribute", + ): two_name_class_list.insert(1, new_object) -@pytest.mark.parametrize("new_values", [ - ({"name": "Alice"}), -]) +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Alice"}), + ], +) def test_insert_kwargs_same_name(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we insert an object with an already-specified name_field value to a ClassList we should raise a ValueError.""" - with pytest.raises(ValueError, match=f"Input arguments contain the {two_name_class_list.name_field} " - f"'{new_values[two_name_class_list.name_field]}', " - f"which is already specified in the ClassList"): + with pytest.raises( + ValueError, + match=f"Input arguments contain the {two_name_class_list.name_field} " + f"'{new_values[two_name_class_list.name_field]}', " + f"which is already specified in the ClassList", + ): two_name_class_list.insert(1, **new_values) -@pytest.mark.parametrize("remove_value", [ - "Bob", - (InputAttributes(name="Bob")), -]) +@pytest.mark.parametrize( + "remove_value", + [ + "Bob", + (InputAttributes(name="Bob")), + ], +) def test_remove(two_name_class_list: ClassList, remove_value: Union[object, str]) -> None: """We should be able to remove an object either by the value of the name_field or by specifying the object itself. @@ -432,22 +563,28 @@ def test_remove(two_name_class_list: ClassList, remove_value: Union[object, str] assert two_name_class_list == ClassList([InputAttributes(name="Alice")]) -@pytest.mark.parametrize("remove_value", [ - "Eve", - (InputAttributes(name="Eve")), -]) +@pytest.mark.parametrize( + "remove_value", + [ + "Eve", + (InputAttributes(name="Eve")), + ], +) def test_remove_not_present(two_name_class_list: ClassList, remove_value: Union[object, str]) -> None: """If we remove an object not included in the ClassList we should raise a ValueError.""" with pytest.raises(ValueError, match=re.escape("list.remove(x): x not in list")): two_name_class_list.remove(remove_value) -@pytest.mark.parametrize(["count_value", "expected_count"], [ - ("Bob", 1), - (InputAttributes(name="Bob"), 1), - ("Eve", 0), - (InputAttributes(name="Eve"), 0), -]) +@pytest.mark.parametrize( + ["count_value", "expected_count"], + [ + ("Bob", 1), + (InputAttributes(name="Bob"), 1), + ("Eve", 0), + (InputAttributes(name="Eve"), 0), + ], +) def test_count(two_name_class_list: ClassList, count_value: Union[object, str], expected_count: int) -> None: """We should be able to determine the number of times an object is in the ClassList using either the object itself or its name_field value. @@ -455,10 +592,13 @@ def test_count(two_name_class_list: ClassList, count_value: Union[object, str], assert two_name_class_list.count(count_value) == expected_count -@pytest.mark.parametrize(["index_value", "expected_index"], [ - ("Bob", 1), - (InputAttributes(name="Bob"), 1), -]) +@pytest.mark.parametrize( + ["index_value", "expected_index"], + [ + ("Bob", 1), + (InputAttributes(name="Bob"), 1), + ], +) def test_index(two_name_class_list: ClassList, index_value: Union[object, str], expected_index: int) -> None: """We should be able to find the index of an object in the ClassList either by its name_field value or by specifying the object itself. @@ -466,21 +606,32 @@ def test_index(two_name_class_list: ClassList, index_value: Union[object, str], assert two_name_class_list.index(index_value) == expected_index -@pytest.mark.parametrize(["index_value", "offset", "expected_index"], [ - ("Bob", 1, 2), - (InputAttributes(name="Bob"), -3, -2), -]) -def test_index_offset(two_name_class_list: ClassList, index_value: Union[object, str], offset: int, - expected_index: int) -> None: +@pytest.mark.parametrize( + ["index_value", "offset", "expected_index"], + [ + ("Bob", 1, 2), + (InputAttributes(name="Bob"), -3, -2), + ], +) +def test_index_offset( + two_name_class_list: ClassList, + index_value: Union[object, str], + offset: int, + expected_index: int, +) -> None: """We should be able to find the index of an object in the ClassList either by its name_field value or by specifying the object itself. When using an offset, the value of the index should be shifted accordingly. """ assert two_name_class_list.index(index_value, offset) == expected_index -@pytest.mark.parametrize("index_value", [ - "Eve", - (InputAttributes(name="Eve")), -]) + +@pytest.mark.parametrize( + "index_value", + [ + "Eve", + (InputAttributes(name="Eve")), + ], +) def test_index_not_present(two_name_class_list: ClassList, index_value: Union[object, str]) -> None: """If we try to find the index of an object not included in the ClassList we should raise a ValueError.""" # with pytest.raises(ValueError, match=f"'{index_value}' is not in list") as e: @@ -488,12 +639,15 @@ def test_index_not_present(two_name_class_list: ClassList, index_value: Union[ob two_name_class_list.index(index_value) -@pytest.mark.parametrize("extended_list", [ - (ClassList(InputAttributes(name="Eve"))), - ([InputAttributes(name="Eve")]), - (InputAttributes(name="Eve"),), - (InputAttributes(name="Eve")), -]) +@pytest.mark.parametrize( + "extended_list", + [ + (ClassList(InputAttributes(name="Eve"))), + ([InputAttributes(name="Eve")]), + (InputAttributes(name="Eve"),), + (InputAttributes(name="Eve")), + ], +) def test_extend(two_name_class_list: ClassList, extended_list: Sequence, three_name_class_list: ClassList) -> None: """We should be able to extend a ClassList using another ClassList or a sequence. Individual objects should be wrapped in a list before being added. @@ -503,11 +657,14 @@ def test_extend(two_name_class_list: ClassList, extended_list: Sequence, three_n assert class_list == three_name_class_list -@pytest.mark.parametrize("extended_list", [ - (ClassList(InputAttributes(name="Alice"))), - ([InputAttributes(name="Alice")]), - (InputAttributes(name="Alice"),), -]) +@pytest.mark.parametrize( + "extended_list", + [ + (ClassList(InputAttributes(name="Alice"))), + ([InputAttributes(name="Alice")]), + (InputAttributes(name="Alice"),), + ], +) def test_extend_empty_classlist(extended_list: Sequence, one_name_class_list: ClassList) -> None: """We should be able to extend a ClassList using another ClassList or a sequence""" class_list = ClassList() @@ -516,40 +673,61 @@ def test_extend_empty_classlist(extended_list: Sequence, one_name_class_list: Cl assert isinstance(extended_list[-1], class_list._class_handle) -@pytest.mark.parametrize(["new_values", "expected_classlist"], [ - ({"name": "Eve"}, ClassList([InputAttributes(name="Eve"), InputAttributes(name="Bob")])), - ({"name": "John", "surname": "Luther"}, - ClassList([InputAttributes(name="John", surname="Luther"), InputAttributes(name="Bob")])), -]) -def test_set_fields(two_name_class_list: ClassList, new_values: dict[str, Any], expected_classlist: ClassList)\ - -> None: +@pytest.mark.parametrize( + ["new_values", "expected_classlist"], + [ + ({"name": "Eve"}, ClassList([InputAttributes(name="Eve"), InputAttributes(name="Bob")])), + ( + {"name": "John", "surname": "Luther"}, + ClassList([InputAttributes(name="John", surname="Luther"), InputAttributes(name="Bob")]), + ), + ], +) +def test_set_fields(two_name_class_list: ClassList, new_values: dict[str, Any], expected_classlist: ClassList) -> None: """We should be able to set field values in an element of a ClassList using keyword arguments.""" class_list = two_name_class_list class_list.set_fields(0, **new_values) assert class_list == expected_classlist -@pytest.mark.parametrize("new_values", [ - ({"name": "Bob"}), -]) +@pytest.mark.parametrize( + "new_values", + [ + ({"name": "Bob"}), + ], +) def test_set_fields_same_name_field(two_name_class_list: ClassList, new_values: dict[str, Any]) -> None: """If we set the name_field of an object in the ClassList to one already defined, we should raise a ValueError.""" - with pytest.raises(ValueError, match=f"Input arguments contain the {two_name_class_list.name_field} " - f"'{new_values[two_name_class_list.name_field]}', " - f"which is already specified in the ClassList"): + with pytest.raises( + ValueError, + match=f"Input arguments contain the {two_name_class_list.name_field} " + f"'{new_values[two_name_class_list.name_field]}', " + f"which is already specified in the ClassList", + ): two_name_class_list.set_fields(0, **new_values) -@pytest.mark.parametrize(["class_list", "expected_names"], [ - (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), ["Alice", "Bob"]), - (ClassList([InputAttributes(id="Alice"), InputAttributes(id="Bob")], name_field="id"), ["Alice", "Bob"]), - (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")], name_field="id"), []), - (ClassList([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), []), - (ClassList([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")]), ["Alice"]), - (ClassList([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")], - name_field="surname"), ["Morgan", "Terwilliger"]), - (ClassList(InputAttributes()), []), -]) +@pytest.mark.parametrize( + ["class_list", "expected_names"], + [ + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), ["Alice", "Bob"]), + (ClassList([InputAttributes(id="Alice"), InputAttributes(id="Bob")], name_field="id"), ["Alice", "Bob"]), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")], name_field="id"), []), + (ClassList([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), []), + ( + ClassList([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")]), + ["Alice"], + ), + ( + ClassList( + [InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")], + name_field="surname", + ), + ["Morgan", "Terwilliger"], + ), + (ClassList(InputAttributes()), []), + ], +) def test_get_names(class_list: ClassList, expected_names: list[str]) -> None: """We should get a list of the values of the name_field attribute from each object with it defined in the ClassList. @@ -557,21 +735,27 @@ def test_get_names(class_list: ClassList, expected_names: list[str]) -> None: assert class_list.get_names() == expected_names -@pytest.mark.parametrize(["class_list", "expected_matches"], [ - (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), [(0, "name")]), - (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob", id="Alice")]), [(0, "name"), (1, "id")]), - (ClassList([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), []), - (ClassList(InputAttributes()), []), -]) +@pytest.mark.parametrize( + ["class_list", "expected_matches"], + [ + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), [(0, "name")]), + (ClassList([InputAttributes(name="Alice"), InputAttributes(name="Bob", id="Alice")]), [(0, "name"), (1, "id")]), + (ClassList([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), []), + (ClassList(InputAttributes()), []), + ], +) def test_get_all_matches(class_list: ClassList, expected_matches: list[tuple]) -> None: """We should get a list of (index, field) tuples matching the given value in the ClassList.""" assert class_list.get_all_matches("Alice") == expected_matches -@pytest.mark.parametrize("input_dict", [ - ({"name": "Eve"}), - ({"surname": "Polastri"}), -]) +@pytest.mark.parametrize( + "input_dict", + [ + ({"name": "Eve"}), + ({"surname": "Polastri"}), + ], +) def test__validate_name_field(two_name_class_list: ClassList, input_dict: dict[str, Any]) -> None: """We should not raise an error if the input values do not contain a name_field value defined in an object in the ClassList. @@ -579,24 +763,33 @@ def test__validate_name_field(two_name_class_list: ClassList, input_dict: dict[s assert two_name_class_list._validate_name_field(input_dict) is None -@pytest.mark.parametrize("input_dict", [ - ({"name": "Alice"}), -]) +@pytest.mark.parametrize( + "input_dict", + [ + ({"name": "Alice"}), + ], +) def test__validate_name_field_not_unique(two_name_class_list: ClassList, input_dict: dict[str, Any]) -> None: """We should raise a ValueError if we input values containing a name_field defined in an object in the ClassList.""" - with pytest.raises(ValueError, match=f"Input arguments contain the {two_name_class_list.name_field} " - f"'{input_dict[two_name_class_list.name_field]}', " - f"which is already specified in the ClassList"): + with pytest.raises( + ValueError, + match=f"Input arguments contain the {two_name_class_list.name_field} " + f"'{input_dict[two_name_class_list.name_field]}', " + f"which is already specified in the ClassList", + ): two_name_class_list._validate_name_field(input_dict) -@pytest.mark.parametrize("input_list", [ - ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), - ([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), - ([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")]), - ([InputAttributes()]), - ([]), -]) +@pytest.mark.parametrize( + "input_list", + [ + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), + ([InputAttributes(surname="Morgan"), InputAttributes(surname="Terwilliger")]), + ([InputAttributes(name="Alice", surname="Morgan"), InputAttributes(surname="Terwilliger")]), + ([InputAttributes()]), + ([]), + ], +) def test__check_unique_name_fields(two_name_class_list: ClassList, input_list: Iterable) -> None: """We should not raise an error if an input list contains objects with different name_field values, or if the name_field is not defined. @@ -604,60 +797,84 @@ def test__check_unique_name_fields(two_name_class_list: ClassList, input_list: I assert two_name_class_list._check_unique_name_fields(input_list) is None -@pytest.mark.parametrize("input_list", [ - ([InputAttributes(name="Alice"), InputAttributes(name="Alice")]), -]) +@pytest.mark.parametrize( + "input_list", + [ + ([InputAttributes(name="Alice"), InputAttributes(name="Alice")]), + ], +) def test__check_unique_name_fields_not_unique(two_name_class_list: ClassList, input_list: Iterable) -> None: """We should raise a ValueError if an input list contains multiple objects with matching name_field values defined. """ - with pytest.raises(ValueError, match=f"Input list contains objects with the same value of the " - f"{two_name_class_list.name_field} attribute"): + with pytest.raises( + ValueError, + match=f"Input list contains objects with the same value of the " f"{two_name_class_list.name_field} attribute", + ): two_name_class_list._check_unique_name_fields(input_list) -@pytest.mark.parametrize("input_list", [ - ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), -]) +@pytest.mark.parametrize( + "input_list", + [ + ([InputAttributes(name="Alice"), InputAttributes(name="Bob")]), + ], +) def test__check_classes(input_list: Iterable) -> None: """We should not raise an error all objects in the ClassList are of the same type.""" class_list = ClassList([InputAttributes()]) assert class_list._check_classes(input_list) is None -@pytest.mark.parametrize("input_list", [ - ([InputAttributes(name="Alice"), dict(name="Bob")]), -]) +@pytest.mark.parametrize( + "input_list", + [ + ([InputAttributes(name="Alice"), dict(name="Bob")]), + ], +) def test__check_classes_different_classes(input_list: Iterable) -> None: """We should raise a ValueError if an input list contains objects of different types.""" class_list = ClassList([InputAttributes()]) - with pytest.raises(ValueError, match=(f"Input list contains elements of type other " - f"than '{class_list._class_handle.__name__}'")): + with pytest.raises( + ValueError, + match=(f"Input list contains elements of type other " f"than '{class_list._class_handle.__name__}'"), + ): class_list._check_classes(input_list) -@pytest.mark.parametrize(["value", "expected_output"], [ - ("Alice", InputAttributes(name="Alice")), - ("Eve", "Eve"), -]) -def test__get_item_from_name_field(two_name_class_list: ClassList, - value: str, - expected_output: Union[object, str]) -> None: +@pytest.mark.parametrize( + ["value", "expected_output"], + [ + ("Alice", InputAttributes(name="Alice")), + ("Eve", "Eve"), + ], +) +def test__get_item_from_name_field( + two_name_class_list: ClassList, + value: str, + expected_output: Union[object, str], +) -> None: """When we input the name_field value of an object defined in the ClassList, we should return the object. If the value is not the name_field of an object defined in the ClassList, we should return the value. """ assert two_name_class_list._get_item_from_name_field(value) == expected_output -@pytest.mark.parametrize(["input_list", "expected_type"], [ - ([InputAttributes(name="Alice")], InputAttributes), - ([InputAttributes(name="Alice"), SubInputAttributes(name="Bob")], InputAttributes), - ([SubInputAttributes(name="Alice"), InputAttributes(name="Bob")], InputAttributes), - ([SubInputAttributes(name="Alice"), SubInputAttributes(name="Bob")], SubInputAttributes), - ([SubInputAttributes(name="Alice"), SubInputAttributes(name="Bob"), InputAttributes(name="Eve")], InputAttributes), - ([InputAttributes(name="Alice"), dict(name="Bob")], InputAttributes), - ([dict(name="Alice"), InputAttributes(name="Bob")], dict), -]) +@pytest.mark.parametrize( + ["input_list", "expected_type"], + [ + ([InputAttributes(name="Alice")], InputAttributes), + ([InputAttributes(name="Alice"), SubInputAttributes(name="Bob")], InputAttributes), + ([SubInputAttributes(name="Alice"), InputAttributes(name="Bob")], InputAttributes), + ([SubInputAttributes(name="Alice"), SubInputAttributes(name="Bob")], SubInputAttributes), + ( + [SubInputAttributes(name="Alice"), SubInputAttributes(name="Bob"), InputAttributes(name="Eve")], + InputAttributes, + ), + ([InputAttributes(name="Alice"), dict(name="Bob")], InputAttributes), + ([dict(name="Alice"), InputAttributes(name="Bob")], dict), + ], +) def test_determine_class_handle(input_list: ClassList, expected_type: type) -> None: """The _class_handle for the ClassList should be the type that satisfies the condition "isinstance(element, type)" for all elements in the ClassList. diff --git a/tests/test_controls.py b/tests/test_controls.py index 4c2e6ba8..be37401d 100644 --- a/tests/test_controls.py +++ b/tests/test_controls.py @@ -16,24 +16,30 @@ class TestCalculate: def setup_class(self): self.calculate = Calculate() - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Single), - ("calcSldDuringFit", False), - ("resampleParams", [0.9, 50]), - ("display", Display.Iter), - ("procedure", Procedures.Calculate), - ]) + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.Calculate), + ], + ) def test_calculate_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Calculate class.""" assert getattr(self.calculate, control_property) == value - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Points), - ("calcSldDuringFit", True), - ("resampleParams", [0.2, 1]), - ("display", Display.Notify), - ]) - def test_calculate_property_setters(self, control_property: str, value: Any) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ], + ) + def test_calculate_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of Calculate class.""" setattr(self.calculate, control_property, value) assert getattr(self.calculate, control_property) == value @@ -59,20 +65,26 @@ def test_calculate_display_validation(self, value: Any) -> None: self.calculate.display = value assert exp.value.errors()[0]["msg"] == "Input should be 'off', 'iter', 'notify' or 'final'" - @pytest.mark.parametrize("value, msg", [ - ([5.0], "List should have at least 2 items after validation, not 1"), - ([12, 13, 14], "List should have at most 2 items after validation, not 3"), - ]) + @pytest.mark.parametrize( + "value, msg", + [ + ([5.0], "List should have at least 2 items after validation, not 1"), + ([12, 13, 14], "List should have at most 2 items after validation, not 3"), + ], + ) def test_calculate_resampleParams_length_validation(self, value: list, msg: str) -> None: """Tests the resampleParams setter length validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: self.calculate.resampleParams = value assert exp.value.errors()[0]["msg"] == msg - @pytest.mark.parametrize("value, msg", [ - ([1.0, 2], "Value error, resampleParams[0] must be between 0 and 1"), - ([0.5, -0.1], "Value error, resampleParams[1] must be greater than or equal to 0"), - ]) + @pytest.mark.parametrize( + "value, msg", + [ + ([1.0, 2], "Value error, resampleParams[0] must be between 0 and 1"), + ([0.5, -0.1], "Value error, resampleParams[1] must be greater than or equal to 0"), + ], + ) def test_calculate_resampleParams_value_validation(self, value: list, msg: str) -> None: """Tests the resampleParams setter value validation in Calculate class.""" with pytest.raises(pydantic.ValidationError) as exp: @@ -100,16 +112,17 @@ def test_calculate_set_procedure_error(self) -> None: def test_repr(self) -> None: """Tests the Calculate model __repr__.""" table = self.calculate.__repr__() - table_str = ("+------------------+-----------+\n" - "| Property | Value |\n" - "+------------------+-----------+\n" - "| procedure | calculate |\n" - "| parallel | single |\n" - "| calcSldDuringFit | False |\n" - "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" - "+------------------+-----------+" - ) + table_str = ( + "+------------------+-----------+\n" + "| Property | Value |\n" + "+------------------+-----------+\n" + "| procedure | calculate |\n" + "| parallel | single |\n" + "| calcSldDuringFit | False |\n" + "| resampleParams | [0.9, 50] |\n" + "| display | iter |\n" + "+------------------+-----------+" + ) assert table == table_str @@ -121,47 +134,56 @@ class TestSimplex: def setup_class(self): self.simplex = Simplex() - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Single), - ("calcSldDuringFit", False), - ("resampleParams", [0.9, 50]), - ("display", Display.Iter), - ("procedure", Procedures.Simplex), - ("xTolerance", 1e-6), - ("funcTolerance", 1e-6), - ("maxFuncEvals", 10000), - ("maxIterations", 1000), - ("updateFreq", -1), - ("updatePlotFreq", 1), - ]) + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.Simplex), + ("xTolerance", 1e-6), + ("funcTolerance", 1e-6), + ("maxFuncEvals", 10000), + ("maxIterations", 1000), + ("updateFreq", -1), + ("updatePlotFreq", 1), + ], + ) def test_simplex_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Simplex class.""" assert getattr(self.simplex, control_property) == value - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Points), - ("calcSldDuringFit", True), - ("resampleParams", [0.2, 1]), - ("display", Display.Notify), - ("xTolerance", 4e-6), - ("funcTolerance", 3e-4), - ("maxFuncEvals", 100), - ("maxIterations", 50), - ("updateFreq", 4), - ("updatePlotFreq", 3), - ]) - def test_simplex_property_setters(self, control_property: str, value: Any) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("xTolerance", 4e-6), + ("funcTolerance", 3e-4), + ("maxFuncEvals", 100), + ("maxIterations", 50), + ("updateFreq", 4), + ("updatePlotFreq", 3), + ], + ) + def test_simplex_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of Simplex class.""" setattr(self.simplex, control_property, value) assert getattr(self.simplex, control_property) == value - @pytest.mark.parametrize("control_property, value", [ - ("xTolerance", -4e-6), - ("funcTolerance", -3e-4), - ("maxFuncEvals", -100), - ("maxIterations", -50), - ]) - def test_simplex_property_errors(self, control_property: str, value: Union[float, int]) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("xTolerance", -4e-6), + ("funcTolerance", -3e-4), + ("maxFuncEvals", -100), + ("maxIterations", -50), + ], + ) + def test_simplex_property_errors(self, control_property: str, value: Union[float, int]) -> None: """Tests the property errors of Simplex class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.simplex, control_property, value) @@ -188,22 +210,23 @@ def test_simplex_set_procedure_error(self) -> None: def test_repr(self) -> None: """Tests the Simplex model __repr__.""" table = self.simplex.__repr__() - table_str = ("+------------------+-----------+\n" - "| Property | Value |\n" - "+------------------+-----------+\n" - "| procedure | simplex |\n" - "| parallel | single |\n" - "| calcSldDuringFit | False |\n" - "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" - "| xTolerance | 1e-06 |\n" - "| funcTolerance | 1e-06 |\n" - "| maxFuncEvals | 10000 |\n" - "| maxIterations | 1000 |\n" - "| updateFreq | -1 |\n" - "| updatePlotFreq | 1 |\n" - "+------------------+-----------+" - ) + table_str = ( + "+------------------+-----------+\n" + "| Property | Value |\n" + "+------------------+-----------+\n" + "| procedure | simplex |\n" + "| parallel | single |\n" + "| calcSldDuringFit | False |\n" + "| resampleParams | [0.9, 50] |\n" + "| display | iter |\n" + "| xTolerance | 1e-06 |\n" + "| funcTolerance | 1e-06 |\n" + "| maxFuncEvals | 10000 |\n" + "| maxIterations | 1000 |\n" + "| updateFreq | -1 |\n" + "| updatePlotFreq | 1 |\n" + "+------------------+-----------+" + ) assert table == table_str @@ -215,61 +238,75 @@ class TestDE: def setup_class(self): self.de = DE() - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Single), - ("calcSldDuringFit", False), - ("resampleParams", [0.9, 50]), - ("display", Display.Iter), - ("procedure", Procedures.DE), - ("populationSize", 20), - ("fWeight", 0.5), - ("crossoverProbability", 0.8), - ("strategy", Strategies.RandomWithPerVectorDither), - ("targetValue", 1), - ("numGenerations", 500), - ]) + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.DE), + ("populationSize", 20), + ("fWeight", 0.5), + ("crossoverProbability", 0.8), + ("strategy", Strategies.RandomWithPerVectorDither), + ("targetValue", 1), + ("numGenerations", 500), + ], + ) def test_de_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of DE class.""" assert getattr(self.de, control_property) == value - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Points), - ("calcSldDuringFit", True), - ("resampleParams", [0.2, 1]), - ("display", Display.Notify), - ("populationSize", 20), - ("fWeight", 0.3), - ("crossoverProbability", 0.4), - ("strategy", Strategies.BestWithJitter), - ("targetValue", 2.0), - ("numGenerations", 50), - ]) - def test_de_property_setters(self, control_property: str, value: Any) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("populationSize", 20), + ("fWeight", 0.3), + ("crossoverProbability", 0.4), + ("strategy", Strategies.BestWithJitter), + ("targetValue", 2.0), + ("numGenerations", 50), + ], + ) + def test_de_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of DE class.""" setattr(self.de, control_property, value) assert getattr(self.de, control_property) == value - @pytest.mark.parametrize("value, msg", [ - (0, "Input should be greater than 0"), - (2, "Input should be less than 1"), - ]) - def test_de_crossoverProbability_error(self, value: int, msg: str) -> None: + @pytest.mark.parametrize( + "value, msg", + [ + (0, "Input should be greater than 0"), + (2, "Input should be less than 1"), + ], + ) + def test_de_crossoverProbability_error(self, value: int, msg: str) -> None: """Tests the crossoverProbability setter error in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: self.de.crossoverProbability = value assert exp.value.errors()[0]["msg"] == msg - @pytest.mark.parametrize("control_property, value", [ - ("targetValue", 0), - ("targetValue", 0.999), - ("numGenerations", -500), - ("numGenerations", 0), - ("populationSize", 0), - ("populationSize", -1), - ]) - def test_de_targetValue_numGenerations_populationSize_error(self, - control_property: str, - value: Union[int, float]) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("targetValue", 0), + ("targetValue", 0.999), + ("numGenerations", -500), + ("numGenerations", 0), + ("populationSize", 0), + ("populationSize", -1), + ], + ) + def test_de_targetValue_numGenerations_populationSize_error( + self, + control_property: str, + value: Union[int, float], + ) -> None: """Tests the targetValue, numGenerations, populationSize setter error in DE class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.de, control_property, value) @@ -296,22 +333,23 @@ def test_de_set_procedure_error(self) -> None: def test_repr(self) -> None: """Tests the DE model __repr__.""" table = self.de.__repr__() - table_str = ("+----------------------+--------------------------------------+\n" - "| Property | Value |\n" - "+----------------------+--------------------------------------+\n" - "| procedure | de |\n" - "| parallel | single |\n" - "| calcSldDuringFit | False |\n" - "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" - "| populationSize | 20 |\n" - "| fWeight | 0.5 |\n" - "| crossoverProbability | 0.8 |\n" - "| strategy | Strategies.RandomWithPerVectorDither |\n" - "| targetValue | 1.0 |\n" - "| numGenerations | 500 |\n" - "+----------------------+--------------------------------------+" - ) + table_str = ( + "+----------------------+--------------------------------------+\n" + "| Property | Value |\n" + "+----------------------+--------------------------------------+\n" + "| procedure | de |\n" + "| parallel | single |\n" + "| calcSldDuringFit | False |\n" + "| resampleParams | [0.9, 50] |\n" + "| display | iter |\n" + "| populationSize | 20 |\n" + "| fWeight | 0.5 |\n" + "| crossoverProbability | 0.8 |\n" + "| strategy | Strategies.RandomWithPerVectorDither |\n" + "| targetValue | 1.0 |\n" + "| numGenerations | 500 |\n" + "+----------------------+--------------------------------------+" + ) assert table == table_str @@ -323,52 +361,64 @@ class TestNS: def setup_class(self): self.ns = NS() - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Single), - ("calcSldDuringFit", False), - ("resampleParams", [0.9, 50]), - ("display", Display.Iter), - ("procedure", Procedures.NS), - ("nLive", 150), - ("nMCMC", 0), - ("propScale", 0.1), - ("nsTolerance", 0.1), - ]) + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.NS), + ("nLive", 150), + ("nMCMC", 0), + ("propScale", 0.1), + ("nsTolerance", 0.1), + ], + ) def test_ns_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of NS class.""" assert getattr(self.ns, control_property) == value - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Points), - ("calcSldDuringFit", True), - ("resampleParams", [0.2, 1]), - ("display", Display.Notify), - ("nLive", 1500), - ("nMCMC", 1), - ("propScale", 0.5), - ("nsTolerance", 0.8), - ]) - def test_ns_property_setters(self, control_property: str, value: Any) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("nLive", 1500), + ("nMCMC", 1), + ("propScale", 0.5), + ("nsTolerance", 0.8), + ], + ) + def test_ns_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters of NS class.""" setattr(self.ns, control_property, value) assert getattr(self.ns, control_property) == value - @pytest.mark.parametrize("control_property, value, bound", [ - ("nMCMC", -0.6, 0), - ("nsTolerance", -500, 0), - ("nLive", -500, 1), - ]) + @pytest.mark.parametrize( + "control_property, value, bound", + [ + ("nMCMC", -0.6, 0), + ("nsTolerance", -500, 0), + ("nLive", -500, 1), + ], + ) def test_ns_setter_error(self, control_property: str, value: Union[int, float], bound: int) -> None: """Tests the nMCMC, nsTolerance, nLive setter error in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: setattr(self.ns, control_property, value) assert exp.value.errors()[0]["msg"] == f"Input should be greater than or equal to {bound}" - @pytest.mark.parametrize("value, msg", [ - (0, "Input should be greater than 0"), - (2, "Input should be less than 1"), - ]) - def test_ns_propScale_error(self, value: int, msg: str) -> None: + @pytest.mark.parametrize( + "value, msg", + [ + (0, "Input should be greater than 0"), + (2, "Input should be less than 1"), + ], + ) + def test_ns_propScale_error(self, value: int, msg: str) -> None: """Tests the propScale error in NS class.""" with pytest.raises(pydantic.ValidationError) as exp: self.ns.propScale = value @@ -395,20 +445,21 @@ def test_ns_procedure_error(self) -> None: def test_control_class_ns_repr(self) -> None: """Tests the NS model __repr__.""" table = self.ns.__repr__() - table_str = ("+------------------+-----------+\n" - "| Property | Value |\n" - "+------------------+-----------+\n" - "| procedure | ns |\n" - "| parallel | single |\n" - "| calcSldDuringFit | False |\n" - "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" - "| nLive | 150 |\n" - "| nMCMC | 0.0 |\n" - "| propScale | 0.1 |\n" - "| nsTolerance | 0.1 |\n" - "+------------------+-----------+" - ) + table_str = ( + "+------------------+-----------+\n" + "| Property | Value |\n" + "+------------------+-----------+\n" + "| procedure | ns |\n" + "| parallel | single |\n" + "| calcSldDuringFit | False |\n" + "| resampleParams | [0.9, 50] |\n" + "| display | iter |\n" + "| nLive | 150 |\n" + "| nMCMC | 0.0 |\n" + "| propScale | 0.1 |\n" + "| nsTolerance | 0.1 |\n" + "+------------------+-----------+" + ) assert table == table_str @@ -420,44 +471,53 @@ class TestDream: def setup_class(self): self.dream = Dream() - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Single), - ("calcSldDuringFit", False), - ("resampleParams", [0.9, 50]), - ("display", Display.Iter), - ("procedure", Procedures.Dream), - ("nSamples", 50000), - ("nChains", 10), - ("jumpProbability", 0.5), - ("pUnitGamma", 0.2), - ("boundHandling", BoundHandling.Fold), - ]) + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Single), + ("calcSldDuringFit", False), + ("resampleParams", [0.9, 50]), + ("display", Display.Iter), + ("procedure", Procedures.Dream), + ("nSamples", 50000), + ("nChains", 10), + ("jumpProbability", 0.5), + ("pUnitGamma", 0.2), + ("boundHandling", BoundHandling.Fold), + ], + ) def test_dream_property_values(self, control_property: str, value: Any) -> None: """Tests the default values of Dream class.""" assert getattr(self.dream, control_property) == value - @pytest.mark.parametrize("control_property, value", [ - ("parallel", Parallel.Points), - ("calcSldDuringFit", True), - ("resampleParams", [0.2, 1]), - ("display", Display.Notify), - ("nSamples", 500), - ("nChains", 1000), - ("jumpProbability", 0.7), - ("pUnitGamma", 0.3), - ("boundHandling", BoundHandling.Reflect), - ]) - def test_dream_property_setters(self, control_property: str, value: Any) -> None: + @pytest.mark.parametrize( + "control_property, value", + [ + ("parallel", Parallel.Points), + ("calcSldDuringFit", True), + ("resampleParams", [0.2, 1]), + ("display", Display.Notify), + ("nSamples", 500), + ("nChains", 1000), + ("jumpProbability", 0.7), + ("pUnitGamma", 0.3), + ("boundHandling", BoundHandling.Reflect), + ], + ) + def test_dream_property_setters(self, control_property: str, value: Any) -> None: """Tests the setters in Dream class.""" setattr(self.dream, control_property, value) assert getattr(self.dream, control_property) == value - @pytest.mark.parametrize("control_property, value, msg", [ - ("jumpProbability", 0, "Input should be greater than 0"), - ("jumpProbability", 2, "Input should be less than 1"), - ("pUnitGamma", -5, "Input should be greater than 0"), - ("pUnitGamma", 20, "Input should be less than 1"), - ]) + @pytest.mark.parametrize( + "control_property, value, msg", + [ + ("jumpProbability", 0, "Input should be greater than 0"), + ("jumpProbability", 2, "Input should be less than 1"), + ("pUnitGamma", -5, "Input should be greater than 0"), + ("pUnitGamma", 20, "Input should be less than 1"), + ], + ) def test_dream_jumpProbability_pUnitGamma_error(self, control_property: str, value: int, msg: str) -> None: """Tests the jumpProbability and pUnitGamma setter errors in Dream class.""" with pytest.raises(pydantic.ValidationError) as exp: @@ -499,32 +559,37 @@ def test_dream_procedure_error(self) -> None: def test_control_class_dream_repr(self) -> None: """Tests the Dream model __repr__.""" table = self.dream.__repr__() - table_str = ("+------------------+-----------+\n" - "| Property | Value |\n" - "+------------------+-----------+\n" - "| procedure | dream |\n" - "| parallel | single |\n" - "| calcSldDuringFit | False |\n" - "| resampleParams | [0.9, 50] |\n" - "| display | iter |\n" - "| nSamples | 50000 |\n" - "| nChains | 10 |\n" - "| jumpProbability | 0.5 |\n" - "| pUnitGamma | 0.2 |\n" - "| boundHandling | fold |\n" - "| adaptPCR | False |\n" - "+------------------+-----------+" - ) + table_str = ( + "+------------------+-----------+\n" + "| Property | Value |\n" + "+------------------+-----------+\n" + "| procedure | dream |\n" + "| parallel | single |\n" + "| calcSldDuringFit | False |\n" + "| resampleParams | [0.9, 50] |\n" + "| display | iter |\n" + "| nSamples | 50000 |\n" + "| nChains | 10 |\n" + "| jumpProbability | 0.5 |\n" + "| pUnitGamma | 0.2 |\n" + "| boundHandling | fold |\n" + "| adaptPCR | False |\n" + "+------------------+-----------+" + ) assert table == table_str -@pytest.mark.parametrize(["procedure", "expected_model"], [ - ("calculate", Calculate), - ("simplex", Simplex), - ("de", DE), - ("ns", NS), - ("dream", Dream), -]) + +@pytest.mark.parametrize( + ["procedure", "expected_model"], + [ + ("calculate", Calculate), + ("simplex", Simplex), + ("de", DE), + ("ns", NS), + ("dream", Dream), + ], +) def test_set_controls(procedure: Procedures, expected_model: Union[Calculate, Simplex, DE, NS, Dream]) -> None: """We should return the correct model given the value of procedure.""" controls_model = set_controls(procedure) @@ -539,25 +604,35 @@ def test_set_controls_default_procedure() -> None: def test_set_controls_invalid_procedure() -> None: """We should return the default model when we call "set_controls" without specifying a procedure.""" - with pytest.raises(ValueError, match="The controls procedure must be one of: 'calculate', 'simplex', 'de', 'ns' " - "or 'dream'"): + with pytest.raises( + ValueError, + match="The controls procedure must be one of: 'calculate', 'simplex', 'de', 'ns' " "or 'dream'", + ): set_controls("invalid") -@pytest.mark.parametrize(["procedure", "expected_model"], [ - ("calculate", Calculate), - ("simplex", Simplex), - ("de", DE), - ("ns", NS), - ("dream", Dream), -]) -def test_set_controls_extra_fields(procedure: Procedures, expected_model: Union[Calculate, Simplex, DE, NS, Dream])\ - -> None: +@pytest.mark.parametrize( + ["procedure", "expected_model"], + [ + ("calculate", Calculate), + ("simplex", Simplex), + ("de", DE), + ("ns", NS), + ("dream", Dream), + ], +) +def test_set_controls_extra_fields( + procedure: Procedures, + expected_model: Union[Calculate, Simplex, DE, NS, Dream], +) -> None: """If we provide extra fields to a controls model through "set_controls", we should print a formatted ValidationError with a custom error message. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for {expected_model.__name__}\n' - f'extra_field\n Extra inputs are not permitted. The fields for ' - f'the {procedure} controls procedure are:\n ' - f'{", ".join(expected_model.model_fields.keys())}\n'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for {expected_model.__name__}\n' + f'extra_field\n Extra inputs are not permitted. The fields for ' + f'the {procedure} controls procedure are:\n ' + f'{", ".join(expected_model.model_fields.keys())}\n', + ): set_controls(procedure, extra_field="invalid") diff --git a/tests/test_custom_errors.py b/tests/test_custom_errors.py index c08f56ec..57c1f6fc 100644 --- a/tests/test_custom_errors.py +++ b/tests/test_custom_errors.py @@ -1,4 +1,5 @@ """Test the utils.custom_errors module.""" + import re import pytest @@ -14,18 +15,28 @@ def TestModel(): return TestModel -@pytest.mark.parametrize(["custom_errors", "expected_error_message"], [ - ({}, - "2 validation errors for TestModel\nint_field\n Input should be a valid integer, unable to parse string as an " - "integer [type=int_parsing, input_value='string', input_type=str]\nstr_field\n Input should be a valid string " - "[type=string_type, input_value=5, input_type=int]"), - ({"int_parsing": "This is a custom error message", "string_type": "This is another custom error message"}, - "2 validation errors for TestModel\nint_field\n This is a custom error message [type=int_parsing, " - "input_value='string', input_type=str]\nstr_field\n This is another custom error message [type=string_type, " - "input_value=5, input_type=int]"), -]) -def test_custom_pydantic_validation_error(TestModel, custom_errors: dict[str, str], expected_error_message: str, - ) -> None: +@pytest.mark.parametrize( + ["custom_errors", "expected_error_message"], + [ + ( + {}, + "2 validation errors for TestModel\nint_field\n Input should be a valid integer, unable to parse string as an " + "integer [type=int_parsing, input_value='string', input_type=str]\nstr_field\n Input should be a valid string " + "[type=string_type, input_value=5, input_type=int]", + ), + ( + {"int_parsing": "This is a custom error message", "string_type": "This is another custom error message"}, + "2 validation errors for TestModel\nint_field\n This is a custom error message [type=int_parsing, " + "input_value='string', input_type=str]\nstr_field\n This is another custom error message [type=string_type, " + "input_value=5, input_type=int]", + ), + ], +) +def test_custom_pydantic_validation_error( + TestModel, + custom_errors: dict[str, str], + expected_error_message: str, +) -> None: """When we call custom_pydantic_validation_error with custom errors, we should return an error list containing PydanticCustomErrors, otherwise we return the original set of errors. """ diff --git a/tests/test_inputs.py b/tests/test_inputs.py index d003a8ac..f14d6710 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -27,38 +27,67 @@ @pytest.fixture def standard_layers_project(): """Add parameters to the default project for a non polarised calculation.""" - test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]), - ) + test_project = RAT.Project( + data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]), + ) test_project.parameters.append(name="Test Thickness") test_project.parameters.append(name="Test SLD") test_project.parameters.append(name="Test Roughness") - test_project.custom_files.append(name="Test Custom File", filename="python_test.py", function_name="dummy_function", - language="python") - test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness", - ) - test_project.contrasts.append(name="Test Contrast", data="Test Data", background="Background 1", bulk_in="SLD Air", - bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", - model=["Test Layer"]) + test_project.custom_files.append( + name="Test Custom File", + filename="python_test.py", + function_name="dummy_function", + language="python", + ) + test_project.layers.append( + name="Test Layer", + thickness="Test Thickness", + SLD="Test SLD", + roughness="Test Roughness", + ) + test_project.contrasts.append( + name="Test Contrast", + data="Test Data", + background="Background 1", + bulk_in="SLD Air", + bulk_out="SLD D2O", + scalefactor="Scalefactor 1", + resolution="Resolution 1", + model=["Test Layer"], + ) return test_project @pytest.fixture def domains_project(): """Add parameters to the default project for a domains calculation.""" - test_project = RAT.Project(calculation=Calculations.Domains, - data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]), - ) + test_project = RAT.Project( + calculation=Calculations.Domains, + data=RAT.ClassList([RAT.models.Data(name="Test Data", data=np.array([[1.0, 1.0, 1.0]]))]), + ) test_project.parameters.append(name="Test Thickness") test_project.parameters.append(name="Test SLD") test_project.parameters.append(name="Test Roughness") test_project.custom_files.append(name="Test Custom File", filename="matlab_test.m", language="matlab") - test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness", - ) + test_project.layers.append( + name="Test Layer", + thickness="Test Thickness", + SLD="Test SLD", + roughness="Test Roughness", + ) test_project.domain_contrasts.append(name="up", model=["Test Layer"]) test_project.domain_contrasts.append(name="down", model=["Test Layer"]) - test_project.contrasts.append(name="Test Contrast", data="Test Data", background="Background 1", bulk_in="SLD Air", - bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", - domain_ratio="Domain Ratio 1", model=["down", "up"]) + test_project.contrasts.append( + name="Test Contrast", + data="Test Data", + background="Background 1", + bulk_in="SLD Air", + bulk_out="SLD D2O", + scalefactor="Scalefactor 1", + resolution="Resolution 1", + domain_ratio="Domain Ratio 1", + model=["down", "up"], + ) return test_project @@ -70,9 +99,16 @@ def custom_xy_project(): test_project.parameters.append(name="Test SLD") test_project.parameters.append(name="Test Roughness") test_project.custom_files.append(name="Test Custom File", filename="cpp_test.dll", language="cpp") - test_project.contrasts.append(name="Test Contrast", data="Simulation", background="Background 1", bulk_in="SLD Air", - bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", - model=["Test Custom File"]) + test_project.contrasts.append( + name="Test Contrast", + data="Simulation", + background="Background 1", + bulk_in="SLD Air", + bulk_out="SLD D2O", + scalefactor="Scalefactor 1", + resolution="Resolution 1", + model=["Test Custom File"], + ) return test_project @@ -110,8 +146,16 @@ def standard_layers_problem(): problem.fitParams = [3.0] problem.otherParams = [0.0, 0.0, 0.0, 1e-06, 0.23, 0.0, 6.35e-06, 0.03] problem.fitLimits = [[1.0, 5.0]] - problem.otherLimits = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [1e-07, 1e-05], [0.02, 0.25], [0.0, 0.0], - [6.2e-06, 6.35e-06], [0.01, 0.05]] + problem.otherLimits = [ + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [1e-07, 1e-05], + [0.02, 0.25], + [0.0, 0.0], + [6.2e-06, 6.35e-06], + [0.01, 0.05], + ] return problem @@ -150,8 +194,17 @@ def domains_problem(): problem.fitParams = [3.0] problem.otherParams = [0.0, 0.0, 0.0, 1e-06, 0.23, 0.0, 6.35e-06, 0.03, 0.5] problem.fitLimits = [[1.0, 5.0]] - problem.otherLimits = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [1e-07, 1e-05], [0.02, 0.25], [0.0, 0.0], - [6.2e-06, 6.35e-06], [0.01, 0.05], [0.4, 0.6]] + problem.otherLimits = [ + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [1e-07, 1e-05], + [0.02, 0.25], + [0.0, 0.0], + [6.2e-06, 6.35e-06], + [0.01, 0.05], + [0.4, 0.6], + ] return problem @@ -190,8 +243,16 @@ def custom_xy_problem(): problem.fitParams = [3.0] problem.otherParams = [0.0, 0.0, 0.0, 1e-06, 0.23, 0.0, 6.35e-06, 0.03] problem.fitLimits = [[1.0, 5.0]] - problem.otherLimits = [[0.0, 0.0], [0.0, 0.0], [0.0, 0.0], [1e-07, 1e-05], [0.02, 0.25], [0.0, 0.0], - [6.2e-06, 6.35e-06], [0.01, 0.05]] + problem.otherLimits = [ + [0.0, 0.0], + [0.0, 0.0], + [0.0, 0.0], + [1e-07, 1e-05], + [0.02, 0.25], + [0.0, 0.0], + [6.2e-06, 6.35e-06], + [0.01, 0.05], + ] return problem @@ -316,10 +377,12 @@ def domains_limits(): def non_polarised_priors(): """The expected priors object from "standard_layers_project" and "custom_xy_project".""" priors = Priors() - priors.param = [["Substrate Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Thickness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test SLD", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.param = [ + ["Substrate Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Thickness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test SLD", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ] priors.backgroundParam = [["Background Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.qzshift = [] priors.scalefactor = [["Scalefactor 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] @@ -327,10 +390,28 @@ def non_polarised_priors(): priors.bulkOut = [["SLD D2O", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.resolutionParam = [["Resolution Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.domainRatio = [] - priors.priorNames = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness", "Background Param 1", - "Scalefactor 1", "SLD Air", "SLD D2O", "Resolution Param 1"] - priors.priorValues = [[1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], - [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf]] + priors.priorNames = [ + "Substrate Roughness", + "Test Thickness", + "Test SLD", + "Test Roughness", + "Background Param 1", + "Scalefactor 1", + "SLD Air", + "SLD D2O", + "Resolution Param 1", + ] + priors.priorValues = [ + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + ] return priors @@ -339,10 +420,12 @@ def non_polarised_priors(): def domains_priors(): """The expected priors object from "domains_project".""" priors = Priors() - priors.param = [["Substrate Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Thickness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test SLD", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], - ["Test Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] + priors.param = [ + ["Substrate Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Thickness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test SLD", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ["Test Roughness", RAT.utils.enums.Priors.Uniform, 0.0, np.inf], + ] priors.backgroundParam = [["Background Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.qzshift = [] priors.scalefactor = [["Scalefactor 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] @@ -350,10 +433,30 @@ def domains_priors(): priors.bulkOut = [["SLD D2O", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.resolutionParam = [["Resolution Param 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] priors.domainRatio = [["Domain Ratio 1", RAT.utils.enums.Priors.Uniform, 0.0, np.inf]] - priors.priorNames = ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness", "Background Param 1", - "Scalefactor 1", "SLD Air", "SLD D2O", "Resolution Param 1", "Domain Ratio 1"] - priors.priorValues = [[1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], - [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf], [1, 0.0, np.inf]] + priors.priorNames = [ + "Substrate Roughness", + "Test Thickness", + "Test SLD", + "Test Roughness", + "Background Param 1", + "Scalefactor 1", + "SLD Air", + "SLD D2O", + "Resolution Param 1", + "Domain Ratio 1", + ] + priors.priorValues = [ + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + [1, 0.0, np.inf], + ] return priors @@ -404,8 +507,7 @@ def standard_layers_controls(): @pytest.fixture def custom_xy_controls(): - """The expected controls object for input to the compiled RAT code given the default inputs and "custom_xy_project". - """ + """The expected controls object for input to the compiled RAT code given the default inputs and "custom_xy_project".""" controls = Control() controls.procedure = Procedures.Calculate controls.parallel = Parallel.Single @@ -461,15 +563,35 @@ def test_checks(): return checks -@pytest.mark.parametrize(["test_project", "test_problem", "test_cells", "test_limits", "test_priors", "test_controls"], - [ - ("standard_layers_project", "standard_layers_problem", "standard_layers_cells", "non_polarised_limits", - "non_polarised_priors", "standard_layers_controls"), - ("custom_xy_project", "custom_xy_problem", "custom_xy_cells", "non_polarised_limits", "non_polarised_priors", - "custom_xy_controls"), - ("domains_project", "domains_problem", "domains_cells", "domains_limits", "domains_priors", - "standard_layers_controls"), -]) +@pytest.mark.parametrize( + ["test_project", "test_problem", "test_cells", "test_limits", "test_priors", "test_controls"], + [ + ( + "standard_layers_project", + "standard_layers_problem", + "standard_layers_cells", + "non_polarised_limits", + "non_polarised_priors", + "standard_layers_controls", + ), + ( + "custom_xy_project", + "custom_xy_problem", + "custom_xy_cells", + "non_polarised_limits", + "non_polarised_priors", + "custom_xy_controls", + ), + ( + "domains_project", + "domains_problem", + "domains_cells", + "domains_limits", + "domains_priors", + "standard_layers_controls", + ), + ], +) def test_make_input(test_project, test_problem, test_cells, test_limits, test_priors, test_controls, request) -> None: """When converting the "project" and "controls", we should obtain the five input objects required for the compiled RAT code. @@ -481,19 +603,33 @@ def test_make_input(test_project, test_problem, test_cells, test_limits, test_pr test_priors = request.getfixturevalue(test_priors) test_controls = request.getfixturevalue(test_controls) - parameter_fields = ["param", "backgroundParam", "scalefactor", "qzshift", "bulkIn", "bulkOut", "resolutionParam", - "domainRatio"] + parameter_fields = [ + "param", + "backgroundParam", + "scalefactor", + "qzshift", + "bulkIn", + "bulkOut", + "resolutionParam", + "domainRatio", + ] mocked_matlab_module = mock.MagicMock() mocked_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_engine - with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, - "matlab.engine": mocked_matlab_module.engine}), \ - mock.patch.object(RAT.rat_core, "DylibEngine", mock.MagicMock()), \ - mock.patch.object(RAT.inputs, "get_python_handle", mock.MagicMock(return_value=dummy_function)), \ - mock.patch.object(RAT.wrappers.MatlabWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)), \ - mock.patch.object(RAT.wrappers.DylibWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)): + with mock.patch.dict( + "sys.modules", + {"matlab": mocked_matlab_module, "matlab.engine": mocked_matlab_module.engine}, + ), mock.patch.object(RAT.rat_core, "DylibEngine", mock.MagicMock()), mock.patch.object( + RAT.inputs, + "get_python_handle", + mock.MagicMock(return_value=dummy_function), + ), mock.patch.object( + RAT.wrappers.MatlabWrapper, + "getHandle", + mock.MagicMock(return_value=dummy_function), + ), mock.patch.object(RAT.wrappers.DylibWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)): problem, cells, limits, priors, controls = make_input(test_project, RAT.set_controls()) check_problem_equal(problem, test_problem) @@ -511,11 +647,14 @@ def test_make_input(test_project, test_problem, test_cells, test_limits, test_pr check_controls_equal(controls, test_controls) -@pytest.mark.parametrize(["test_project", "test_problem"], [ - ("standard_layers_project", "standard_layers_problem"), - ("custom_xy_project", "custom_xy_problem"), - ("domains_project", "domains_problem"), -]) +@pytest.mark.parametrize( + ["test_project", "test_problem"], + [ + ("standard_layers_project", "standard_layers_problem"), + ("custom_xy_project", "custom_xy_problem"), + ("domains_project", "domains_problem"), + ], +) def test_make_problem(test_project, test_problem, request) -> None: """The problem object should contain the relevant parameters defined in the input project object.""" test_project = request.getfixturevalue(test_project) @@ -525,11 +664,14 @@ def test_make_problem(test_project, test_problem, request) -> None: check_problem_equal(problem, test_problem) -@pytest.mark.parametrize("test_problem", [ - "standard_layers_problem", - "custom_xy_problem", - "domains_problem", -]) +@pytest.mark.parametrize( + "test_problem", + [ + "standard_layers_problem", + "custom_xy_problem", + "domains_problem", + ], +) def test_check_indices(test_problem, request) -> None: """The check_indices routine should not raise errors for a properly defined ProblemDefinition object.""" test_problem = request.getfixturevalue(test_problem) @@ -537,65 +679,75 @@ def test_check_indices(test_problem, request) -> None: check_indices(test_problem) -@pytest.mark.parametrize(["test_problem", "index_list", "bad_value"], [ - ("standard_layers_problem", "contrastBulkIns", [0.0]), - ("standard_layers_problem", "contrastBulkIns", [2.0]), - ("standard_layers_problem", "contrastBulkOuts", [0.0]), - ("standard_layers_problem", "contrastBulkOuts", [2.0]), - ("standard_layers_problem", "contrastScalefactors", [0.0]), - ("standard_layers_problem", "contrastScalefactors", [2.0]), - ("standard_layers_problem", "contrastBackgroundParams", [0.0]), - ("standard_layers_problem", "contrastBackgroundParams", [2.0]), - ("standard_layers_problem", "contrastResolutionParams", [0.0]), - ("standard_layers_problem", "contrastResolutionParams", [2.0]), - ("custom_xy_problem", "contrastBulkIns", [0.0]), - ("custom_xy_problem", "contrastBulkIns", [2.0]), - ("custom_xy_problem", "contrastBulkOuts", [0.0]), - ("custom_xy_problem", "contrastBulkOuts", [2.0]), - ("custom_xy_problem", "contrastScalefactors", [0.0]), - ("custom_xy_problem", "contrastScalefactors", [2.0]), - ("custom_xy_problem", "contrastBackgroundParams", [0.0]), - ("custom_xy_problem", "contrastBackgroundParams", [2.0]), - ("custom_xy_problem", "contrastResolutionParams", [0.0]), - ("custom_xy_problem", "contrastResolutionParams", [2.0]), - ("domains_problem", "contrastBulkIns", [0.0]), - ("domains_problem", "contrastBulkIns", [2.0]), - ("domains_problem", "contrastBulkOuts", [0.0]), - ("domains_problem", "contrastBulkOuts", [2.0]), - ("domains_problem", "contrastScalefactors", [0.0]), - ("domains_problem", "contrastScalefactors", [2.0]), - ("domains_problem", "contrastDomainRatios", [0.0]), - ("domains_problem", "contrastDomainRatios", [2.0]), - ("domains_problem", "contrastBackgroundParams", [0.0]), - ("domains_problem", "contrastBackgroundParams", [2.0]), - ("domains_problem", "contrastResolutionParams", [0.0]), - ("domains_problem", "contrastResolutionParams", [2.0]), -]) +@pytest.mark.parametrize( + ["test_problem", "index_list", "bad_value"], + [ + ("standard_layers_problem", "contrastBulkIns", [0.0]), + ("standard_layers_problem", "contrastBulkIns", [2.0]), + ("standard_layers_problem", "contrastBulkOuts", [0.0]), + ("standard_layers_problem", "contrastBulkOuts", [2.0]), + ("standard_layers_problem", "contrastScalefactors", [0.0]), + ("standard_layers_problem", "contrastScalefactors", [2.0]), + ("standard_layers_problem", "contrastBackgroundParams", [0.0]), + ("standard_layers_problem", "contrastBackgroundParams", [2.0]), + ("standard_layers_problem", "contrastResolutionParams", [0.0]), + ("standard_layers_problem", "contrastResolutionParams", [2.0]), + ("custom_xy_problem", "contrastBulkIns", [0.0]), + ("custom_xy_problem", "contrastBulkIns", [2.0]), + ("custom_xy_problem", "contrastBulkOuts", [0.0]), + ("custom_xy_problem", "contrastBulkOuts", [2.0]), + ("custom_xy_problem", "contrastScalefactors", [0.0]), + ("custom_xy_problem", "contrastScalefactors", [2.0]), + ("custom_xy_problem", "contrastBackgroundParams", [0.0]), + ("custom_xy_problem", "contrastBackgroundParams", [2.0]), + ("custom_xy_problem", "contrastResolutionParams", [0.0]), + ("custom_xy_problem", "contrastResolutionParams", [2.0]), + ("domains_problem", "contrastBulkIns", [0.0]), + ("domains_problem", "contrastBulkIns", [2.0]), + ("domains_problem", "contrastBulkOuts", [0.0]), + ("domains_problem", "contrastBulkOuts", [2.0]), + ("domains_problem", "contrastScalefactors", [0.0]), + ("domains_problem", "contrastScalefactors", [2.0]), + ("domains_problem", "contrastDomainRatios", [0.0]), + ("domains_problem", "contrastDomainRatios", [2.0]), + ("domains_problem", "contrastBackgroundParams", [0.0]), + ("domains_problem", "contrastBackgroundParams", [2.0]), + ("domains_problem", "contrastResolutionParams", [0.0]), + ("domains_problem", "contrastResolutionParams", [2.0]), + ], +) def test_check_indices_error(test_problem, index_list, bad_value, request) -> None: """The check_indices routine should raise an IndexError if a contrast list contains an index that is out of the range of the corresponding parameter list in a ProblemDefinition object. """ - param_list = {"contrastBulkIns": "bulkIn", - "contrastBulkOuts": "bulkOut", - "contrastScalefactors": "scalefactors", - "contrastDomainRatios": "domainRatio", - "contrastBackgroundParams": "backgroundParams", - "contrastResolutionParams": "resolutionParams", - } + param_list = { + "contrastBulkIns": "bulkIn", + "contrastBulkOuts": "bulkOut", + "contrastScalefactors": "scalefactors", + "contrastDomainRatios": "domainRatio", + "contrastBackgroundParams": "backgroundParams", + "contrastResolutionParams": "resolutionParams", + } test_problem = request.getfixturevalue(test_problem) setattr(test_problem, index_list, bad_value) - with pytest.raises(IndexError, match=f'The problem field "{index_list}" contains: {bad_value[0]}, which lie ' - f'outside of the range of "{param_list[index_list]}"'): + with pytest.raises( + IndexError, + match=f'The problem field "{index_list}" contains: {bad_value[0]}, which lie ' + f'outside of the range of "{param_list[index_list]}"', + ): check_indices(test_problem) -@pytest.mark.parametrize(["test_project", "test_cells"], [ - ("standard_layers_project", "standard_layers_cells"), - ("custom_xy_project", "custom_xy_cells"), - ("domains_project", "domains_cells"), -]) +@pytest.mark.parametrize( + ["test_project", "test_cells"], + [ + ("standard_layers_project", "standard_layers_cells"), + ("custom_xy_project", "custom_xy_cells"), + ("domains_project", "domains_cells"), + ], +) def test_make_cells(test_project, test_cells, request) -> None: """The cells object should be populated according to the input project object.""" test_project = request.getfixturevalue(test_project) @@ -605,12 +757,18 @@ def test_make_cells(test_project, test_cells, request) -> None: mocked_matlab_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_matlab_engine - with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, - "matlab.engine": mocked_matlab_module.engine}), \ - mock.patch.object(RAT.rat_core, "DylibEngine", mock.MagicMock()), \ - mock.patch.object(RAT.inputs, "get_python_handle", mock.MagicMock(return_value=dummy_function)), \ - mock.patch.object(RAT.wrappers.MatlabWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)), \ - mock.patch.object(RAT.wrappers.DylibWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)): + with mock.patch.dict( + "sys.modules", + {"matlab": mocked_matlab_module, "matlab.engine": mocked_matlab_module.engine}, + ), mock.patch.object(RAT.rat_core, "DylibEngine", mock.MagicMock()), mock.patch.object( + RAT.inputs, + "get_python_handle", + mock.MagicMock(return_value=dummy_function), + ), mock.patch.object( + RAT.wrappers.MatlabWrapper, + "getHandle", + mock.MagicMock(return_value=dummy_function), + ), mock.patch.object(RAT.wrappers.DylibWrapper, "getHandle", mock.MagicMock(return_value=dummy_function)): cells = make_cells(test_project) check_cells_equal(cells, test_cells) @@ -618,8 +776,7 @@ def test_make_cells(test_project, test_cells, request) -> None: def test_get_python_handle(): path = pathlib.Path(__file__).parent.resolve() - assert (RAT.inputs.get_python_handle("utils.py", "dummy_function", path).__code__ == - dummy_function.__code__) + assert RAT.inputs.get_python_handle("utils.py", "dummy_function", path).__code__ == dummy_function.__code__ def test_make_controls(standard_layers_controls, test_checks) -> None: @@ -632,14 +789,41 @@ def test_make_controls(standard_layers_controls, test_checks) -> None: def check_problem_equal(actual_problem, expected_problem) -> None: """Compare two instances of the "problem" object for equality.""" - scalar_fields = ["TF", "modelType", "geometry", "useImaginary", "numberOfContrasts", "numberOfLayers", - "numberOfDomainContrasts"] - - array_fields = ["params", "backgroundParams", "qzshifts", "scalefactors", "bulkIn", "bulkOut", "resolutionParams", - "domainRatio", "contrastBackgroundParams", "contrastBackgroundActions", "contrastQzshifts", - "contrastScalefactors", "contrastBulkIns", "contrastBulkOuts", "contrastResolutionParams", - "contrastDomainRatios", "resample", "dataPresent", "oilChiDataPresent", "fitParams", "otherParams", - "fitLimits", "otherLimits"] + scalar_fields = [ + "TF", + "modelType", + "geometry", + "useImaginary", + "numberOfContrasts", + "numberOfLayers", + "numberOfDomainContrasts", + ] + + array_fields = [ + "params", + "backgroundParams", + "qzshifts", + "scalefactors", + "bulkIn", + "bulkOut", + "resolutionParams", + "domainRatio", + "contrastBackgroundParams", + "contrastBackgroundActions", + "contrastQzshifts", + "contrastScalefactors", + "contrastBulkIns", + "contrastBulkOuts", + "contrastResolutionParams", + "contrastDomainRatios", + "resample", + "dataPresent", + "oilChiDataPresent", + "fitParams", + "otherParams", + "fitLimits", + "otherLimits", + ] for scalar_field in scalar_fields: assert getattr(actual_problem, scalar_field) == getattr(expected_problem, scalar_field) @@ -647,11 +831,9 @@ def check_problem_equal(actual_problem, expected_problem) -> None: assert (getattr(actual_problem, array_field) == getattr(expected_problem, array_field)).all() # Need to account for "NaN" entries in contrastCustomFiles field - assert ((actual_problem.contrastCustomFiles == expected_problem.contrastCustomFiles).all() or - ["NaN" if np.isnan(el) else el for el in actual_problem.contrastCustomFiles] == - ["NaN" if np.isnan(el) else el for el in expected_problem.contrastCustomFiles] - ) - + assert (actual_problem.contrastCustomFiles == expected_problem.contrastCustomFiles).all() or [ + "NaN" if np.isnan(el) else el for el in actual_problem.contrastCustomFiles + ] == ["NaN" if np.isnan(el) else el for el in expected_problem.contrastCustomFiles] def check_cells_equal(actual_cells, expected_cells) -> None: @@ -659,29 +841,59 @@ def check_cells_equal(actual_cells, expected_cells) -> None: assert actual_cells.f1 == expected_cells.f1 # Need to test equality of the numpy arrays separately - for (a, b) in zip(actual_cells.f2, expected_cells.f2): + for a, b in zip(actual_cells.f2, expected_cells.f2): assert (a == b).all() # f6 may contain "NaN" values, so consider separately - assert (actual_cells.f6 == expected_cells.f6 or - ["NaN" if np.isnan(el) else el for entry in actual_cells.f6 for el in entry] == - ["NaN" if np.isnan(el) else el for entry in expected_cells.f6 for el in entry]) + assert actual_cells.f6 == expected_cells.f6 or [ + "NaN" if np.isnan(el) else el for entry in actual_cells.f6 for el in entry + ] == ["NaN" if np.isnan(el) else el for entry in expected_cells.f6 for el in entry] for index in chain(range(3, 6), range(7, 21)): field = f"f{index}" assert getattr(actual_cells, field) == getattr(expected_cells, field) - def check_controls_equal(actual_controls, expected_controls) -> None: """Compare two instances of the "controls" object used as input for the compiled RAT code for equality.""" - controls_fields = ["procedure", "parallel", "calcSldDuringFit", "display", "xTolerance", - "funcTolerance", "maxFuncEvals", "maxIterations", "updateFreq", "updatePlotFreq", - "populationSize", "fWeight", "crossoverProbability", "strategy", "targetValue", "numGenerations", - "nLive", "nMCMC", "propScale", "nsTolerance", "nSamples", "nChains", "jumpProbability", - "pUnitGamma", "boundHandling", "adaptPCR"] - checks_fields = ["fitParam", "fitBackgroundParam", "fitQzshift", "fitScalefactor", "fitBulkIn", "fitBulkOut", - "fitResolutionParam", "fitDomainRatio"] + controls_fields = [ + "procedure", + "parallel", + "calcSldDuringFit", + "display", + "xTolerance", + "funcTolerance", + "maxFuncEvals", + "maxIterations", + "updateFreq", + "updatePlotFreq", + "populationSize", + "fWeight", + "crossoverProbability", + "strategy", + "targetValue", + "numGenerations", + "nLive", + "nMCMC", + "propScale", + "nsTolerance", + "nSamples", + "nChains", + "jumpProbability", + "pUnitGamma", + "boundHandling", + "adaptPCR", + ] + checks_fields = [ + "fitParam", + "fitBackgroundParam", + "fitQzshift", + "fitScalefactor", + "fitBulkIn", + "fitBulkOut", + "fitResolutionParam", + "fitDomainRatio", + ] # Check "resampleParams" separately as it is an array assert (actual_controls.resampleParams == expected_controls.resampleParams).all() @@ -689,4 +901,3 @@ def check_controls_equal(actual_controls, expected_controls) -> None: assert getattr(actual_controls, field) == getattr(expected_controls, field) for field in checks_fields: assert (getattr(actual_controls.checks, field) == getattr(expected_controls.checks, field)).all() - diff --git a/tests/test_models.py b/tests/test_models.py index 328f36ed..2a13efba 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -10,16 +10,19 @@ import RAT.models -@pytest.mark.parametrize(["model", "model_name", "model_params"], [ - (RAT.models.Background, "Background", {}), - (RAT.models.Contrast, "Contrast", {}), - (RAT.models.CustomFile, "Custom File", {}), - (RAT.models.Data, "Data", {}), - (RAT.models.DomainContrast, "Domain Contrast", {}), - (RAT.models.Layer, "Layer", {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"}), - (RAT.models.Parameter, "Parameter", {}), - (RAT.models.Resolution, "Resolution", {}), -]) +@pytest.mark.parametrize( + ["model", "model_name", "model_params"], + [ + (RAT.models.Background, "Background", {}), + (RAT.models.Contrast, "Contrast", {}), + (RAT.models.CustomFile, "Custom File", {}), + (RAT.models.Data, "Data", {}), + (RAT.models.DomainContrast, "Domain Contrast", {}), + (RAT.models.Layer, "Layer", {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"}), + (RAT.models.Parameter, "Parameter", {}), + (RAT.models.Resolution, "Resolution", {}), + ], +) def test_default_names(model: Callable, model_name: str, model_params: dict) -> None: """When initialising multiple models without specifying a name, they should be given a default name with the format: "New ". @@ -35,43 +38,61 @@ def test_default_names(model: Callable, model_name: str, model_params: dict) -> assert model_4.name == f"New {model_name} 3" -@pytest.mark.parametrize(["model", "model_params"], [ - (RAT.models.Background, {}), - (RAT.models.Contrast, {}), - (RAT.models.ContrastWithRatio, {}), - (RAT.models.CustomFile, {}), - (RAT.models.Data, {}), - (RAT.models.DomainContrast, {}), - (RAT.models.Layer, {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"}), - (RAT.models.AbsorptionLayer, {"thickness": "Test Thickness", "SLD_real": "Test SLD", "SLD_imaginary": "Test SLD", - "roughness": "Test Roughness"}), - (RAT.models.Parameter, {}), - (RAT.models.Resolution, {}), -]) +@pytest.mark.parametrize( + ["model", "model_params"], + [ + (RAT.models.Background, {}), + (RAT.models.Contrast, {}), + (RAT.models.ContrastWithRatio, {}), + (RAT.models.CustomFile, {}), + (RAT.models.Data, {}), + (RAT.models.DomainContrast, {}), + (RAT.models.Layer, {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"}), + ( + RAT.models.AbsorptionLayer, + { + "thickness": "Test Thickness", + "SLD_real": "Test SLD", + "SLD_imaginary": "Test SLD", + "roughness": "Test Roughness", + }, + ), + (RAT.models.Parameter, {}), + (RAT.models.Resolution, {}), + ], +) class TestModels: def test_initialise_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When initialising a model with the wrong type for the "name" field, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nname\n " - f"Input should be a valid string"): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for {model.__name__}\nname\n " f"Input should be a valid string", + ): model(name=1, **model_params) def test_assignment_with_wrong_type(self, model: Callable, model_params: dict) -> None: """When assigning the "name" field of a model with the wrong type, we should raise a ValidationError.""" test_model = model(**model_params) - with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nname\n " - f"Input should be a valid string"): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for {model.__name__}\nname\n " f"Input should be a valid string", + ): test_model.name = 1 def test_initialise_with_zero_length_name(self, model: Callable, model_params: dict) -> None: """When initialising a model with a zero length name, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nname\n " - f"String should have at least 1 character"): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for {model.__name__}\nname\n " f"String should have at least 1 character", + ): model(name="", **model_params) def test_initialise_with_extra_fields(self, model: Callable, model_params: dict) -> None: """When initialising a model with unspecified fields, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f"1 validation error for {model.__name__}\nnew_field\n " - f"Extra inputs are not permitted"): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for {model.__name__}\nnew_field\n " f"Extra inputs are not permitted", + ): model(new_field=1, **model_params) @@ -82,9 +103,12 @@ def test_data_eq() -> None: assert RAT.models.Data().__eq__("data") == NotImplemented -@pytest.mark.parametrize("input_data", [ - (np.array([[1.0, 1.0, 1.0]])), -]) +@pytest.mark.parametrize( + "input_data", + [ + (np.array([[1.0, 1.0, 1.0]])), + ], +) def test_data_dimension(input_data: np.ndarray[float]) -> None: """The "data" field of the "Data" model should be a two-dimensional numpy array with at least three values in the second dimension. @@ -93,81 +117,111 @@ def test_data_dimension(input_data: np.ndarray[float]) -> None: assert (test_data.data == input_data).all() -@pytest.mark.parametrize("input_data", [ - (np.array([])), - (np.array([1.0, 1.0])), -]) +@pytest.mark.parametrize( + "input_data", + [ + (np.array([])), + (np.array([1.0, 1.0])), + ], +) def test_data_too_few_dimensions(input_data: np.ndarray[float]) -> None: - """If the "data" field of the "Data" model is not a two-dimensional numpy array we should raise a ValidationError. - """ - with pytest.raises(pydantic.ValidationError, match='1 validation error for Data\ndata\n Value error, "data" must ' - 'have at least two dimensions'): + """If the "data" field of the "Data" model is not a two-dimensional numpy array we should raise a ValidationError.""" + with pytest.raises( + pydantic.ValidationError, + match='1 validation error for Data\ndata\n Value error, "data" must ' "have at least two dimensions", + ): RAT.models.Data(data=input_data) -@pytest.mark.parametrize("input_data", [ - (np.array([[]])), - (np.array([[1.0]])), - (np.array([[1.0, 1.0]])), -]) +@pytest.mark.parametrize( + "input_data", + [ + (np.array([[]])), + (np.array([[1.0]])), + (np.array([[1.0, 1.0]])), + ], +) def test_data_too_few_values(input_data: np.ndarray[float]) -> None: """If the second dimension of the array in the "data" field of the "Data" model has fewer than three values we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match='1 validation error for Data\ndata\n Value error, "data" must ' - 'have at least three columns'): + with pytest.raises( + pydantic.ValidationError, + match='1 validation error for Data\ndata\n Value error, "data" must ' "have at least three columns", + ): RAT.models.Data(data=input_data) -@pytest.mark.parametrize("input_range", [ - ([1.0, 2.0]), -]) +@pytest.mark.parametrize( + "input_range", + [ + ([1.0, 2.0]), + ], +) def test_data_ranges(input_range: list[float]) -> None: """The "data_range" and "simulation_range" fields of the "Data" model should contain exactly two values.""" assert RAT.models.Data(data_range=input_range).data_range == input_range assert RAT.models.Data(simulation_range=input_range).simulation_range == input_range -@pytest.mark.parametrize("input_range", [ - ([]), - ([1.0]), - ([1.0, 2.0, 3.0]), -]) +@pytest.mark.parametrize( + "input_range", + [ + ([]), + ([1.0]), + ([1.0, 2.0, 3.0]), + ], +) def test_two_values_in_data_range(input_range: list[float]) -> None: """If the "data_range" field of the "Data" model contains more or less than two values, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Data\ndata_range\n List should have ' - f'at {"least" if len(input_range) < 2 else "most"} 2 items ' - f'after validation, not {len(input_range)}'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Data\ndata_range\n List should have ' + f'at {"least" if len(input_range) < 2 else "most"} 2 items ' + f'after validation, not {len(input_range)}', + ): RAT.models.Data(data_range=input_range) -@pytest.mark.parametrize("input_range", [ - ([]), - ([1.0]), - ([1.0, 2.0, 3.0]), -]) +@pytest.mark.parametrize( + "input_range", + [ + ([]), + ([1.0]), + ([1.0, 2.0, 3.0]), + ], +) def test_two_values_in_simulation_range(input_range: list[float]) -> None: """If the "simulation_range" field of the "Data" model contains more or less than two values, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Data\nsimulation_range\n List should ' - f'have at {"least" if len(input_range) < 2 else "most"} 2 items ' - f'after validation, not {len(input_range)}'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Data\nsimulation_range\n List should ' + f'have at {"least" if len(input_range) < 2 else "most"} 2 items ' + f'after validation, not {len(input_range)}', + ): RAT.models.Data(simulation_range=input_range) -@pytest.mark.parametrize("field", [ - "data_range", - "simulation_range", -]) +@pytest.mark.parametrize( + "field", + [ + "data_range", + "simulation_range", + ], +) def test_min_max_in_range(field: str) -> None: """If the maximum value of the "data_range" or "simulation_range" fields of the "Data" model is greater than the minimum value, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Data\n{field}\n Value error, {field} ' - f'"min" value is greater than the "max" value'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Data\n{field}\n Value error, {field} " + f'"min" value is greater than the "max" value', + ): RAT.models.Data(**{field: [1.0, 0.0]}) @@ -180,47 +234,69 @@ def test_default_ranges() -> None: assert test_data.simulation_range == [1.0, 3.0] -@pytest.mark.parametrize("test_range", [ - [0.0, 2.0], - [2.0, 4.0], - [0.0, 4.0], -]) +@pytest.mark.parametrize( + "test_range", + [ + [0.0, 2.0], + [2.0, 4.0], + [0.0, 4.0], + ], +) def test_data_range(test_range) -> None: """If "data" is specified but the "data_range" lies outside of the limits of the data we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=re.escape(f"1 validation error for Data\n Value error, The " - f"data_range value of: {test_range} must lie within " - f"the min/max values of the data: [1.0, 3.0]")): + with pytest.raises( + pydantic.ValidationError, + match=re.escape( + f"1 validation error for Data\n Value error, The " + f"data_range value of: {test_range} must lie within " + f"the min/max values of the data: [1.0, 3.0]", + ), + ): RAT.models.Data(data=np.array([[1.0, 0.0, 0.0], [3.0, 0.0, 0.0]]), data_range=test_range) -@pytest.mark.parametrize("test_range", [ - [0.0, 2.0], - [2.0, 4.0], - [1.5, 2.5], -]) +@pytest.mark.parametrize( + "test_range", + [ + [0.0, 2.0], + [2.0, 4.0], + [1.5, 2.5], + ], +) def test_simulation_range(test_range) -> None: """If "data" is specified but the "simulation_range" lies within the limits of the data we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=re.escape(f"1 validation error for Data\n Value error, The " - f"simulation_range value of: {test_range} must lie " - f"outside of the min/max values of the data: " - f"[1.0, 3.0]")): + with pytest.raises( + pydantic.ValidationError, + match=re.escape( + f"1 validation error for Data\n Value error, The " + f"simulation_range value of: {test_range} must lie " + f"outside of the min/max values of the data: " + f"[1.0, 3.0]", + ), + ): RAT.models.Data(data=np.array([[1.0, 0.0, 0.0], [3.0, 0.0, 0.0]]), simulation_range=test_range) -@pytest.mark.parametrize(["minimum", "value", "maximum"], [ - (0.0, 2.0, 1.0), - (0, 1, 0), - (1, -1, 1), -]) +@pytest.mark.parametrize( + ["minimum", "value", "maximum"], + [ + (0.0, 2.0, 1.0), + (0, 1, 0), + (1, -1, 1), + ], +) def test_parameter_range(minimum: float, value: float, maximum: float) -> None: """For the "Parameter" model, if the value of the "value" field does not lie with the values given in the "min" and "max" fields, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f"1 validation error for Parameter\n Value error, value " - f"{float(value)} is not within the defined range: " - f"{float(minimum)} <= value <= {float(maximum)}"): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Parameter\n Value error, value " + f"{float(value)} is not within the defined range: " + f"{float(minimum)} <= value <= {float(maximum)}", + ): RAT.models.Parameter(min=minimum, value=value, max=maximum) diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 27b6cf21..ef08f302 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -12,10 +12,13 @@ from tests.utils import check_results_equal -@pytest.mark.parametrize(["test_procedure", "test_output_results", "test_bayes", "test_results"], [ - (Procedures.Calculate, "reflectivity_calculation_output_results", None, "reflectivity_calculation_results"), - (Procedures.Dream, "dream_output_results", "dream_bayes", "dream_results"), -]) +@pytest.mark.parametrize( + ["test_procedure", "test_output_results", "test_bayes", "test_results"], + [ + (Procedures.Calculate, "reflectivity_calculation_output_results", None, "reflectivity_calculation_results"), + (Procedures.Dream, "dream_output_results", "dream_bayes", "dream_results"), + ], +) def test_make_results(test_procedure, test_output_results, test_bayes, test_results, request) -> None: """The python results object should contain the relevant parameters defined in the C++ results and bayes objects.""" test_output_results = request.getfixturevalue(test_output_results) diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 029a297a..7b765e8e 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -9,13 +9,11 @@ from RAT.rat_core import PlotEventData from RAT.utils.plotting import Figure, plot_ref_sld_helper -TEST_DIR_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), - "test_data") +TEST_DIR_PATH = os.path.join(os.path.dirname(os.path.realpath(__file__)), "test_data") def data() -> PlotEventData: - """Creates the fixture for the tests. - """ + """Creates the fixture for the tests.""" data_path = os.path.join(TEST_DIR_PATH, "plotting_data.pickle") with open(data_path, "rb") as f: loaded_data = pickle.load(f) @@ -34,8 +32,7 @@ def data() -> PlotEventData: @pytest.fixture def fig() -> Figure: - """Creates the fixture for the tests. - """ + """Creates the fixture for the tests.""" plt.close("all") figure = Figure(1, 3) fig = plot_ref_sld_helper(fig=figure, data=data()) @@ -43,8 +40,7 @@ def fig() -> Figure: def test_figure_axis_formating(fig: Figure) -> None: - """Tests the axis formating of the figure. - """ + """Tests the axis formating of the figure.""" ref_plot = fig._ax[0] sld_plot = fig._ax[1] @@ -65,8 +61,7 @@ def test_figure_axis_formating(fig: Figure) -> None: def test_figure_color_formating(fig: Figure) -> None: - """Tests the color formating of the figure. - """ + """Tests the color formating of the figure.""" ref_plot = fig._ax[0] sld_plot = fig._ax[1] @@ -74,17 +69,18 @@ def test_figure_color_formating(fig: Figure) -> None: assert len(sld_plot.get_lines()) == 6 for axis_ix in range(len(ref_plot.get_lines())): - ax1 = axis_ix*2 + ax1 = axis_ix * 2 ax2 = ax1 + 1 # Tests whether the color of the line and the errorbars match on the ref_plot - assert (ref_plot.containers[ax1][2][0]._original_edgecolor == - ref_plot.containers[ax2][2][0]._original_edgecolor == - ref_plot.get_lines()[axis_ix].get_color()) + assert ( + ref_plot.containers[ax1][2][0]._original_edgecolor + == ref_plot.containers[ax2][2][0]._original_edgecolor + == ref_plot.get_lines()[axis_ix].get_color() + ) # Tests whether the color of the sld and resampled_sld match on the sld_plot - assert (sld_plot.get_lines()[ax1].get_color() == - sld_plot.get_lines()[ax2].get_color()) + assert sld_plot.get_lines()[ax1].get_color() == sld_plot.get_lines()[ax2].get_color() def test_eventhandlers_linked_to_figure(fig: Figure) -> None: @@ -98,8 +94,7 @@ class methods. if str(type(val)) == "": break canvas_close_event_callback = fig._fig.canvas.callbacks.callbacks["close_event"][ix]._func_ref.__repr__() - close_event_callback = re.findall(pattern, - canvas_close_event_callback)[0] + close_event_callback = re.findall(pattern, canvas_close_event_callback)[0] assert close_event_callback == "_close" assert hasattr(Figure, "_close") @@ -107,8 +102,7 @@ class methods. if str(type(val)) == "": break canvas_key_press_event_callback = fig._fig.canvas.callbacks.callbacks["key_press_event"][ix]._func_ref.__repr__() - key_press_event_callback = re.findall(pattern, - canvas_key_press_event_callback)[0] + key_press_event_callback = re.findall(pattern, canvas_key_press_event_callback)[0] assert key_press_event_callback == "_process_button_press" assert hasattr(Figure, "_process_button_press") @@ -133,6 +127,7 @@ def test_wait_for_close(mock: MagicMock, fig: Figure) -> None: """Tests the _wait_for_close method stops the while loop when _esc_pressed is True. """ + def mock_wait_for_button_press(timeout): fig._esc_pressed = True diff --git a/tests/test_project.py b/tests/test_project.py index 7fe4a5cd..e0177788 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -14,28 +14,47 @@ from RAT.utils.enums import Calculations, LayerModels layer_params = {"thickness": "Test Thickness", "SLD": "Test SLD", "roughness": "Test Roughness"} -absorption_layer_params = {"thickness": "Test Thickness", "SLD_real": "Test SLD", "SLD_imaginary": "Test SLD", - "roughness": "Test Roughness"} +absorption_layer_params = { + "thickness": "Test Thickness", + "SLD_real": "Test SLD", + "SLD_imaginary": "Test SLD", + "roughness": "Test Roughness", +} + @pytest.fixture def test_project(): """Add parameters to the default project, so each ClassList can be tested properly.""" - test_project = RAT.Project(data=RAT.ClassList([RAT.models.Data(name="Simulation", data=np.array([[1.0, 1.0, 1.0]]))])) + test_project = RAT.Project( + data=RAT.ClassList([RAT.models.Data(name="Simulation", data=np.array([[1.0, 1.0, 1.0]]))]), + ) test_project.parameters.append(name="Test Thickness") test_project.parameters.append(name="Test SLD") test_project.parameters.append(name="Test Roughness") test_project.custom_files.append(name="Test Custom File") - test_project.layers.append(name="Test Layer", thickness="Test Thickness", SLD="Test SLD", roughness="Test Roughness") - test_project.contrasts.append(name="Test Contrast", data="Simulation", background="Background 1", bulk_in="SLD Air", - bulk_out="SLD D2O", scalefactor="Scalefactor 1", resolution="Resolution 1", - model=["Test Layer"]) + test_project.layers.append( + name="Test Layer", + thickness="Test Thickness", + SLD="Test SLD", + roughness="Test Roughness", + ) + test_project.contrasts.append( + name="Test Contrast", + data="Simulation", + background="Background 1", + bulk_in="SLD Air", + bulk_out="SLD D2O", + scalefactor="Scalefactor 1", + resolution="Resolution 1", + model=["Test Layer"], + ) return test_project @pytest.fixture def default_project_repr(): """A string of the output of repr() for a Project model with no parameters specified.""" - return( + return ( "Calculation: ---------------------------------------------------------------------------------------\n\n" "non polarised\n\n" "Model: ---------------------------------------------------------------------------------------------\n\n" @@ -95,7 +114,8 @@ def default_project_repr(): "| index | name | data | data range | simulation range |\n" "+-------+------------+------+------------+------------------+\n" "| 0 | Simulation | [] | [] | [0.005, 0.7] |\n" - "+-------+------------+------+------------+------------------+\n\n") + "+-------+------------+------+------------+------------------+\n\n" + ) @pytest.fixture @@ -148,59 +168,83 @@ def test_classlists_specific_cases() -> None: assert project.contrasts._class_handle.__name__ == "ContrastWithRatio" -@pytest.mark.parametrize(["input_model", "model_params"], [ - (RAT.models.Background, {}), - (RAT.models.Contrast, {}), - (RAT.models.ContrastWithRatio, {}), - (RAT.models.CustomFile, {}), - (RAT.models.Data, {}), - (RAT.models.DomainContrast, {}), - (RAT.models.Layer, layer_params), - (RAT.models.AbsorptionLayer, absorption_layer_params), - (RAT.models.Resolution, {}), -]) +@pytest.mark.parametrize( + ["input_model", "model_params"], + [ + (RAT.models.Background, {}), + (RAT.models.Contrast, {}), + (RAT.models.ContrastWithRatio, {}), + (RAT.models.CustomFile, {}), + (RAT.models.Data, {}), + (RAT.models.DomainContrast, {}), + (RAT.models.Layer, layer_params), + (RAT.models.AbsorptionLayer, absorption_layer_params), + (RAT.models.Resolution, {}), + ], +) def test_initialise_wrong_classes(input_model: Callable, model_params: dict) -> None: """If the "Project" model is initialised with incorrect classes, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match='1 validation error for Project\nparameters\n Value error, ' - '"parameters" ClassList contains objects other than ' - '"Parameter"'): + with pytest.raises( + pydantic.ValidationError, + match="1 validation error for Project\nparameters\n Value error, " + '"parameters" ClassList contains objects other than ' + '"Parameter"', + ): RAT.Project(parameters=RAT.ClassList(input_model(**model_params))) -@pytest.mark.parametrize(["input_model", "model_params", "absorption", "actual_model_name"], [ - (RAT.models.Layer, layer_params, True, "AbsorptionLayer"), - (RAT.models.AbsorptionLayer, absorption_layer_params, False, "Layer"), -]) -def test_initialise_wrong_layers(input_model: Callable, model_params: dict, absorption: bool, - actual_model_name: str) -> None: +@pytest.mark.parametrize( + ["input_model", "model_params", "absorption", "actual_model_name"], + [ + (RAT.models.Layer, layer_params, True, "AbsorptionLayer"), + (RAT.models.AbsorptionLayer, absorption_layer_params, False, "Layer"), + ], +) +def test_initialise_wrong_layers( + input_model: Callable, + model_params: dict, + absorption: bool, + actual_model_name: str, +) -> None: """If the "Project" model is initialised with the incorrect layer model given the value of absorption, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\nlayers\n Value error, ' - f'"layers" ClassList contains objects other than ' - f'"{actual_model_name}"'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\nlayers\n Value error, " + f'"layers" ClassList contains objects other than ' + f'"{actual_model_name}"', + ): RAT.Project(absorption=absorption, layers=RAT.ClassList(input_model(**model_params))) -@pytest.mark.parametrize(["input_model", "calculation", "actual_model_name"], [ - (RAT.models.Contrast, Calculations.Domains, "ContrastWithRatio"), - (RAT.models.ContrastWithRatio, Calculations.NonPolarised, "Contrast"), -]) -def test_initialise_wrong_contrasts(input_model: Callable, calculation: Calculations, - actual_model_name: str) -> None: +@pytest.mark.parametrize( + ["input_model", "calculation", "actual_model_name"], + [ + (RAT.models.Contrast, Calculations.Domains, "ContrastWithRatio"), + (RAT.models.ContrastWithRatio, Calculations.NonPolarised, "Contrast"), + ], +) +def test_initialise_wrong_contrasts(input_model: Callable, calculation: Calculations, actual_model_name: str) -> None: """If the "Project" model is initialised with the incorrect contrast model given the value of calculation, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\ncontrasts\n Value error, ' - f'"contrasts" ClassList contains objects other than ' - f'"{actual_model_name}"'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\ncontrasts\n Value error, " + f'"contrasts" ClassList contains objects other than ' + f'"{actual_model_name}"', + ): RAT.Project(calculation=calculation, contrasts=RAT.ClassList(input_model())) -@pytest.mark.parametrize("input_parameter", [ - (RAT.models.Parameter(name="Test Parameter")), - (RAT.models.Parameter(name="Substrate Roughness")), -]) +@pytest.mark.parametrize( + "input_parameter", + [ + (RAT.models.Parameter(name="Test Parameter")), + (RAT.models.Parameter(name="Substrate Roughness")), + ], +) def test_initialise_without_substrate_roughness(input_parameter: Callable) -> None: """If the "Project" model is initialised without "Substrate Roughness as a protected parameter, add it to the front of the "parameters" ClassList. @@ -209,72 +253,109 @@ def test_initialise_without_substrate_roughness(input_parameter: Callable) -> No assert project.parameters[0] == RAT.models.ProtectedParameter(name="Substrate Roughness") -@pytest.mark.parametrize(["field", "wrong_input_model", "model_params"], [ - ("backgrounds", RAT.models.Resolution, {}), - ("contrasts", RAT.models.Layer, layer_params), - ("domain_contrasts", RAT.models.Parameter, {}), - ("custom_files", RAT.models.Data, {}), - ("data", RAT.models.Contrast, {}), - ("layers", RAT.models.DomainContrast, {}), - ("parameters", RAT.models.CustomFile, {}), - ("resolutions", RAT.models.Background, {}), -]) +@pytest.mark.parametrize( + ["field", "wrong_input_model", "model_params"], + [ + ("backgrounds", RAT.models.Resolution, {}), + ("contrasts", RAT.models.Layer, layer_params), + ("domain_contrasts", RAT.models.Parameter, {}), + ("custom_files", RAT.models.Data, {}), + ("data", RAT.models.Contrast, {}), + ("layers", RAT.models.DomainContrast, {}), + ("parameters", RAT.models.CustomFile, {}), + ("resolutions", RAT.models.Background, {}), + ], +) def test_assign_wrong_classes(test_project, field: str, wrong_input_model: Callable, model_params: dict) -> None: """If we assign incorrect classes to the "Project" model, we should raise a ValidationError.""" - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n{field}\n Value error, ' - f'"{field}" ClassList contains objects other than ' - f'"{RAT.project.model_in_classlist[field]}"'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n{field}\n Value error, " + f'"{field}" ClassList contains objects other than ' + f'"{RAT.project.model_in_classlist[field]}"', + ): setattr(test_project, field, RAT.ClassList(wrong_input_model(**model_params))) -@pytest.mark.parametrize(["wrong_input_model", "model_params", "absorption", "actual_model_name"], [ - (RAT.models.Layer, layer_params, True, "AbsorptionLayer"), - (RAT.models.AbsorptionLayer, absorption_layer_params, False, "Layer"), -]) -def test_assign_wrong_layers(wrong_input_model: Callable, model_params: dict, absorption: bool, - actual_model_name: str) -> None: +@pytest.mark.parametrize( + ["wrong_input_model", "model_params", "absorption", "actual_model_name"], + [ + (RAT.models.Layer, layer_params, True, "AbsorptionLayer"), + (RAT.models.AbsorptionLayer, absorption_layer_params, False, "Layer"), + ], +) +def test_assign_wrong_layers( + wrong_input_model: Callable, + model_params: dict, + absorption: bool, + actual_model_name: str, +) -> None: """If we assign incorrect classes to the "Project" model, we should raise a ValidationError.""" project = RAT.Project(absorption=absorption) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\nlayers\n Value error, ' - f'"layers" ClassList contains objects other than ' - f'"{actual_model_name}"'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\nlayers\n Value error, " + f'"layers" ClassList contains objects other than ' + f'"{actual_model_name}"', + ): project.layers = RAT.ClassList(wrong_input_model(**model_params)) -@pytest.mark.parametrize(["wrong_input_model", "calculation", "actual_model_name"], [ - (RAT.models.Contrast, Calculations.Domains, "ContrastWithRatio"), - (RAT.models.ContrastWithRatio, Calculations.NonPolarised, "Contrast"), -]) +@pytest.mark.parametrize( + ["wrong_input_model", "calculation", "actual_model_name"], + [ + (RAT.models.Contrast, Calculations.Domains, "ContrastWithRatio"), + (RAT.models.ContrastWithRatio, Calculations.NonPolarised, "Contrast"), + ], +) def test_assign_wrong_contrasts(wrong_input_model: Callable, calculation: Calculations, actual_model_name: str) -> None: """If we assign incorrect classes to the "Project" model, we should raise a ValidationError.""" project = RAT.Project(calculation=calculation) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\ncontrasts\n Value error, ' - f'"contrasts" ClassList contains objects other than ' - f'"{actual_model_name}"'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\ncontrasts\n Value error, " + f'"contrasts" ClassList contains objects other than ' + f'"{actual_model_name}"', + ): project.contrasts = RAT.ClassList(wrong_input_model()) -@pytest.mark.parametrize(["field", "model_params"], [ - ("backgrounds", {}), - ("contrasts", {}), - ("custom_files", {}), - ("data", {}), - ("layers", layer_params), - ("parameters", {}), - ("resolutions", {}), -]) +@pytest.mark.parametrize( + ["field", "model_params"], + [ + ("backgrounds", {}), + ("contrasts", {}), + ("custom_files", {}), + ("data", {}), + ("layers", layer_params), + ("parameters", {}), + ("resolutions", {}), + ], +) def test_assign_models(test_project, field: str, model_params: dict) -> None: """If the "Project" model is initialised with models rather than ClassLists, we should raise a ValidationError.""" input_model = getattr(RAT.models, RAT.project.model_in_classlist[field]) - with pytest.raises(pydantic.ValidationError, match=f"1 validation error for Project\n{field}\n Input should be " - f"an instance of ClassList"): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n{field}\n Input should be " f"an instance of ClassList", + ): setattr(test_project, field, input_model(**model_params)) def test_wrapped_routines(test_project) -> None: """When initialising a project, several ClassList routines should be wrapped.""" - wrapped_methods = ["_setitem", "_delitem", "_iadd", "append", "insert", "pop", "remove", "clear", "extend", - "set_fields"] + wrapped_methods = [ + "_setitem", + "_delitem", + "_iadd", + "append", + "insert", + "pop", + "remove", + "clear", + "extend", + "set_fields", + ] for class_list in RAT.project.class_lists: attribute = getattr(test_project, class_list) for methodName in wrapped_methods: @@ -288,13 +369,16 @@ def test_set_domain_ratios(test_project) -> None: assert test_project.domain_ratios == [] -@pytest.mark.parametrize("project_parameters", [ - ({"calculation": Calculations.NonPolarised, "model": LayerModels.StandardLayers}), - ({"calculation": Calculations.NonPolarised, "model": LayerModels.CustomLayers}), - ({"calculation": Calculations.NonPolarised, "model": LayerModels.CustomXY}), - ({"calculation": Calculations.Domains, "model": LayerModels.CustomLayers}), - ({"calculation": Calculations.Domains, "model": LayerModels.CustomXY}), -]) +@pytest.mark.parametrize( + "project_parameters", + [ + ({"calculation": Calculations.NonPolarised, "model": LayerModels.StandardLayers}), + ({"calculation": Calculations.NonPolarised, "model": LayerModels.CustomLayers}), + ({"calculation": Calculations.NonPolarised, "model": LayerModels.CustomXY}), + ({"calculation": Calculations.Domains, "model": LayerModels.CustomLayers}), + ({"calculation": Calculations.Domains, "model": LayerModels.CustomXY}), + ], +) def test_set_domain_contrasts(project_parameters: dict) -> None: """If we are not running a domains calculation with standard layers, the "domain_contrasts" field of the model should always be empty. @@ -305,10 +389,13 @@ def test_set_domain_contrasts(project_parameters: dict) -> None: assert project.domain_contrasts == [] -@pytest.mark.parametrize("project_parameters", [ - ({"model": LayerModels.CustomLayers}), - ({"model": LayerModels.CustomXY}), -]) +@pytest.mark.parametrize( + "project_parameters", + [ + ({"model": LayerModels.CustomLayers}), + ({"model": LayerModels.CustomXY}), + ], +) def test_set_layers(project_parameters: dict) -> None: """If we are not using a standard layers model, the "layers" field of the model should always be empty.""" project = RAT.Project(**project_parameters) @@ -317,13 +404,20 @@ def test_set_layers(project_parameters: dict) -> None: assert project.layers == [] -@pytest.mark.parametrize(["input_calculation", "input_contrast", "new_calculation", "new_contrast_model", - "num_domain_ratios"], [ - (Calculations.NonPolarised, RAT.models.Contrast, Calculations.Domains, "ContrastWithRatio", 1), - (Calculations.Domains, RAT.models.ContrastWithRatio, Calculations.NonPolarised, "Contrast", 0), -]) -def test_set_calculation(input_calculation: Calculations, input_contrast: Callable, new_calculation: Calculations, - new_contrast_model: str, num_domain_ratios: int) -> None: +@pytest.mark.parametrize( + ["input_calculation", "input_contrast", "new_calculation", "new_contrast_model", "num_domain_ratios"], + [ + (Calculations.NonPolarised, RAT.models.Contrast, Calculations.Domains, "ContrastWithRatio", 1), + (Calculations.Domains, RAT.models.ContrastWithRatio, Calculations.NonPolarised, "Contrast", 0), + ], +) +def test_set_calculation( + input_calculation: Calculations, + input_contrast: Callable, + new_calculation: Calculations, + new_contrast_model: str, + num_domain_ratios: int, +) -> None: """When changing the value of the calculation option, the "contrasts" ClassList should switch to using the appropriate Contrast model. """ @@ -336,16 +430,23 @@ def test_set_calculation(input_calculation: Calculations, input_contrast: Callab assert len(project.domain_ratios) == num_domain_ratios -@pytest.mark.parametrize(["new_calc", "new_model", "expected_contrast_model"], [ - (Calculations.NonPolarised, LayerModels.StandardLayers, ["Test Layer"]), - (Calculations.NonPolarised, LayerModels.CustomLayers, []), - (Calculations.NonPolarised, LayerModels.CustomXY, []), - (Calculations.Domains, LayerModels.StandardLayers, []), - (Calculations.Domains, LayerModels.CustomLayers, []), - (Calculations.Domains, LayerModels.CustomXY, []), -]) -def test_set_contrast_model_field(test_project, new_calc: Calculations, new_model: LayerModels, - expected_contrast_model: list[str]) -> None: +@pytest.mark.parametrize( + ["new_calc", "new_model", "expected_contrast_model"], + [ + (Calculations.NonPolarised, LayerModels.StandardLayers, ["Test Layer"]), + (Calculations.NonPolarised, LayerModels.CustomLayers, []), + (Calculations.NonPolarised, LayerModels.CustomXY, []), + (Calculations.Domains, LayerModels.StandardLayers, []), + (Calculations.Domains, LayerModels.CustomLayers, []), + (Calculations.Domains, LayerModels.CustomXY, []), + ], +) +def test_set_contrast_model_field( + test_project, + new_calc: Calculations, + new_model: LayerModels, + expected_contrast_model: list[str], +) -> None: """If we change the calculation and model such that the values of the "model" field of the "contrasts" model come from a different field of the project, we should clear the contrast "model" field. """ @@ -354,40 +455,76 @@ def test_set_contrast_model_field(test_project, new_calc: Calculations, new_mode assert test_project.contrasts[0].model == expected_contrast_model -@pytest.mark.parametrize(["input_model", "test_contrast_model", "error_message"], [ - (LayerModels.StandardLayers, ["Test Domain Ratio"], - 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.'), - (LayerModels.StandardLayers, ["Test Domain Ratio", "Test Domain Ratio", "Test Domain Ratio"], - 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.'), - (LayerModels.CustomLayers, ["Test Custom File", "Test Custom File"], - 'For a custom model calculation the "model" field of "contrasts" cannot contain more than one value.'), -]) -def test_check_contrast_model_length(test_project, input_model: LayerModels, test_contrast_model: list[str], - error_message: str) -> None: +@pytest.mark.parametrize( + ["input_model", "test_contrast_model", "error_message"], + [ + ( + LayerModels.StandardLayers, + ["Test Domain Ratio"], + 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.', + ), + ( + LayerModels.StandardLayers, + ["Test Domain Ratio", "Test Domain Ratio", "Test Domain Ratio"], + 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.', + ), + ( + LayerModels.CustomLayers, + ["Test Custom File", "Test Custom File"], + 'For a custom model calculation the "model" field of "contrasts" cannot contain more than one value.', + ), + ], +) +def test_check_contrast_model_length( + test_project, + input_model: LayerModels, + test_contrast_model: list[str], + error_message: str, +) -> None: """If we are not running a domains calculation with standard layers, the "domain_contrasts" field of the model should always be empty. """ test_domain_ratios = RAT.ClassList(RAT.models.Parameter(name="Test Domain Ratio")) test_contrasts = RAT.ClassList(RAT.models.ContrastWithRatio(model=test_contrast_model)) - with pytest.raises(pydantic.ValidationError, - match=f"1 validation error for Project\n Value error, {error_message}"): - RAT.Project(calculation=Calculations.Domains, model=input_model, domain_ratios=test_domain_ratios, - contrasts=test_contrasts) - - -@pytest.mark.parametrize(["input_layer", "model_params", "input_absorption", "new_layer_model"], [ - (RAT.models.Layer, layer_params, False, "AbsorptionLayer"), - (RAT.models.AbsorptionLayer, absorption_layer_params, True, "Layer"), -]) -def test_set_absorption(input_layer: Callable, model_params: dict, input_absorption: bool, new_layer_model: str) -> None: + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, {error_message}", + ): + RAT.Project( + calculation=Calculations.Domains, + model=input_model, + domain_ratios=test_domain_ratios, + contrasts=test_contrasts, + ) + + +@pytest.mark.parametrize( + ["input_layer", "model_params", "input_absorption", "new_layer_model"], + [ + (RAT.models.Layer, layer_params, False, "AbsorptionLayer"), + (RAT.models.AbsorptionLayer, absorption_layer_params, True, "Layer"), + ], +) +def test_set_absorption( + input_layer: Callable, + model_params: dict, + input_absorption: bool, + new_layer_model: str, +) -> None: """When changing the value of the absorption option, the "layers" ClassList should switch to using the appropriate Layer model. """ - project = RAT.Project(absorption=input_absorption, - parameters=RAT.ClassList([RAT.models.Parameter(name="Test Thickness"), - RAT.models.Parameter(name="Test SLD"), - RAT.models.Parameter(name="Test Roughness")]), - layers=RAT.ClassList(input_layer(**model_params))) + project = RAT.Project( + absorption=input_absorption, + parameters=RAT.ClassList( + [ + RAT.models.Parameter(name="Test Thickness"), + RAT.models.Parameter(name="Test SLD"), + RAT.models.Parameter(name="Test Roughness"), + ], + ), + layers=RAT.ClassList(input_layer(**model_params)), + ) project.absorption = not input_absorption assert project.absorption is not input_absorption @@ -395,35 +532,44 @@ def test_set_absorption(input_layer: Callable, model_params: dict, input_absorpt assert project.layers._class_handle.__name__ == new_layer_model -@pytest.mark.parametrize("delete_operation", [ - "project.parameters.__delitem__(0)", - "project.parameters.pop()", - 'project.parameters.remove("Substrate Roughness")', - "project.parameters.clear()", -]) +@pytest.mark.parametrize( + "delete_operation", + [ + "project.parameters.__delitem__(0)", + "project.parameters.pop()", + 'project.parameters.remove("Substrate Roughness")', + "project.parameters.clear()", + ], +) def test_check_protected_parameters(delete_operation) -> None: """If we try to remove a protected parameter, we should raise an error.""" project = RAT.Project() - with pytest.raises(pydantic.ValidationError, match="1 validation error for Project\n Value error, Can't delete" - " the protected parameters: Substrate Roughness"): + with pytest.raises( + pydantic.ValidationError, + match="1 validation error for Project\n Value error, Can't delete" + " the protected parameters: Substrate Roughness", + ): eval(delete_operation) # Ensure model was not deleted assert project.parameters[0].name == "Substrate Roughness" -@pytest.mark.parametrize(["model", "field"], [ - ("background_parameters", "value_1"), - ("resolution_parameters", "value_1"), - ("parameters", "roughness"), - ("data", "data"), - ("backgrounds", "background"), - ("bulk_in", "bulk_in"), - ("bulk_out", "bulk_out"), - ("scalefactors", "scalefactor"), - ("resolutions", "resolution"), -]) +@pytest.mark.parametrize( + ["model", "field"], + [ + ("background_parameters", "value_1"), + ("resolution_parameters", "value_1"), + ("parameters", "roughness"), + ("data", "data"), + ("backgrounds", "background"), + ("bulk_in", "bulk_in"), + ("bulk_out", "bulk_out"), + ("scalefactors", "scalefactor"), + ("resolutions", "resolution"), + ], +) def test_rename_models(test_project, model: str, field: str) -> None: """When renaming a model in the project, the new name should be recorded when that model is referred to elsewhere in the project. @@ -433,144 +579,228 @@ def test_rename_models(test_project, model: str, field: str) -> None: assert getattr(getattr(test_project, attribute)[-1], field) == "New Name" -@pytest.mark.parametrize("field", [ - "value_1", - "value_2", - "value_3", - "value_4", - "value_5", -]) +@pytest.mark.parametrize( + "field", + [ + "value_1", + "value_2", + "value_3", + "value_4", + "value_5", + ], +) def test_allowed_backgrounds(field: str) -> None: """If the "value" fields of the Background model are set to values that are not specified in the background parameters, we should raise a ValidationError. """ test_background = RAT.models.Background(**{field: "undefined"}) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "backgrounds" must be ' - f'defined in "background_parameters".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"undefined" in the "{field}" field of "backgrounds" must be ' + f'defined in "background_parameters".', + ): RAT.Project(backgrounds=RAT.ClassList(test_background)) -@pytest.mark.parametrize("field", [ - "thickness", - "SLD", - "roughness", -]) +@pytest.mark.parametrize( + "field", + [ + "thickness", + "SLD", + "roughness", + ], +) def test_allowed_layers(field: str) -> None: """If the "thickness", "SLD", or "roughness" fields of the Layer model are set to values that are not specified in the parameters, we should raise a ValidationError. """ test_layer = RAT.models.Layer(**{**layer_params, field: "undefined"}) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "layers" must be ' - f'defined in "parameters".'): - RAT.Project(absorption=False, parameters=RAT.ClassList([RAT.models.Parameter(name="Test Thickness"), - RAT.models.Parameter(name="Test SLD"), - RAT.models.Parameter(name="Test Roughness")]), - layers=RAT.ClassList(test_layer)) - - -@pytest.mark.parametrize("field", [ - "thickness", - "SLD_real", - "SLD_imaginary", - "roughness", -]) + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"undefined" in the "{field}" field of "layers" must be ' + f'defined in "parameters".', + ): + RAT.Project( + absorption=False, + parameters=RAT.ClassList( + [ + RAT.models.Parameter(name="Test Thickness"), + RAT.models.Parameter(name="Test SLD"), + RAT.models.Parameter(name="Test Roughness"), + ], + ), + layers=RAT.ClassList(test_layer), + ) + + +@pytest.mark.parametrize( + "field", + [ + "thickness", + "SLD_real", + "SLD_imaginary", + "roughness", + ], +) def test_allowed_absorption_layers(field: str) -> None: """If the "thickness", "SLD_real", "SLD_imaginary", or "roughness" fields of the AbsorptionLayer model are set to values that are not specified in the parameters, we should raise a ValidationError. """ test_layer = RAT.models.AbsorptionLayer(**{**absorption_layer_params, field: "undefined"}) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "layers" must be ' - f'defined in "parameters".'): - RAT.Project(absorption=True, parameters=RAT.ClassList([RAT.models.Parameter(name="Test Thickness"), - RAT.models.Parameter(name="Test SLD"), - RAT.models.Parameter(name="Test Roughness")]), - layers=RAT.ClassList(test_layer)) - - -@pytest.mark.parametrize("field", [ - "value_1", - "value_2", - "value_3", - "value_4", - "value_5", -]) + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"undefined" in the "{field}" field of "layers" must be ' + f'defined in "parameters".', + ): + RAT.Project( + absorption=True, + parameters=RAT.ClassList( + [ + RAT.models.Parameter(name="Test Thickness"), + RAT.models.Parameter(name="Test SLD"), + RAT.models.Parameter(name="Test Roughness"), + ], + ), + layers=RAT.ClassList(test_layer), + ) + + +@pytest.mark.parametrize( + "field", + [ + "value_1", + "value_2", + "value_3", + "value_4", + "value_5", + ], +) def test_allowed_resolutions(field: str) -> None: """If the "value" fields of the Resolution model are set to values that are not specified in the background parameters, we should raise a ValidationError. """ test_resolution = RAT.models.Resolution(**{field: "undefined"}) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "resolutions" must be ' - f'defined in "resolution_parameters".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"undefined" in the "{field}" field of "resolutions" must be ' + f'defined in "resolution_parameters".', + ): RAT.Project(resolutions=RAT.ClassList(test_resolution)) -@pytest.mark.parametrize(["field", "model_name"], [ - ("data", "data"), - ("background", "backgrounds"), - ("bulk_in", "bulk_in"), - ("bulk_out", "bulk_out"), - ("scalefactor", "scalefactors"), - ("resolution", "resolutions"), -]) +@pytest.mark.parametrize( + ["field", "model_name"], + [ + ("data", "data"), + ("background", "backgrounds"), + ("bulk_in", "bulk_in"), + ("bulk_out", "bulk_out"), + ("scalefactor", "scalefactors"), + ("resolution", "resolutions"), + ], +) def test_allowed_contrasts(field: str, model_name: str) -> None: """If the fields of the Contrast model are set to values not specified in the other respective models of the project, we should raise a ValidationError. """ test_contrast = RAT.models.Contrast(**{field: "undefined"}) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "contrasts" must be ' - f'defined in "{model_name}".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"undefined" in the "{field}" field of "contrasts" must be ' + f'defined in "{model_name}".', + ): RAT.Project(calculation=Calculations.NonPolarised, contrasts=RAT.ClassList(test_contrast)) -@pytest.mark.parametrize(["field", "model_name"], [ - ("data", "data"), - ("background", "backgrounds"), - ("bulk_in", "bulk_in"), - ("bulk_out", "bulk_out"), - ("scalefactor", "scalefactors"), - ("resolution", "resolutions"), - ("domain_ratio", "domain_ratios"), -]) +@pytest.mark.parametrize( + ["field", "model_name"], + [ + ("data", "data"), + ("background", "backgrounds"), + ("bulk_in", "bulk_in"), + ("bulk_out", "bulk_out"), + ("scalefactor", "scalefactors"), + ("resolution", "resolutions"), + ("domain_ratio", "domain_ratios"), + ], +) def test_allowed_contrasts_with_ratio(field: str, model_name: str) -> None: """If the fields of the ContrastWithRatio model are set to values not specified in the other respective models of the project, we should raise a ValidationError. """ test_contrast = RAT.models.ContrastWithRatio(**{field: "undefined"}) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "contrasts" must be ' - f'defined in "{model_name}".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"undefined" in the "{field}" field of "contrasts" must be ' + f'defined in "{model_name}".', + ): RAT.Project(calculation=Calculations.Domains, contrasts=RAT.ClassList(test_contrast)) -@pytest.mark.parametrize(["input_calc", "input_model", "test_contrast", "field_name"], [ - (Calculations.Domains, LayerModels.StandardLayers, - RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined", "undefined"]), "domain_contrasts"), - (Calculations.Domains, LayerModels.CustomLayers, - RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined"]), "custom_files"), - (Calculations.Domains, LayerModels.CustomXY, - RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined"]), "custom_files"), - (Calculations.NonPolarised, LayerModels.StandardLayers, - RAT.models.Contrast(name="Test Contrast", model=["undefined", "undefined", "undefined"]), "layers"), - (Calculations.NonPolarised, LayerModels.CustomLayers, - RAT.models.Contrast(name="Test Contrast", model=["undefined"]), "custom_files"), - (Calculations.NonPolarised, LayerModels.CustomXY, - RAT.models.Contrast(name="Test Contrast", model=["undefined"]), "custom_files"), -]) -def test_allowed_contrast_models(input_calc: Calculations, input_model: LayerModels, test_contrast: "RAT.models", - field_name: str) -> None: +@pytest.mark.parametrize( + ["input_calc", "input_model", "test_contrast", "field_name"], + [ + ( + Calculations.Domains, + LayerModels.StandardLayers, + RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined", "undefined"]), + "domain_contrasts", + ), + ( + Calculations.Domains, + LayerModels.CustomLayers, + RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined"]), + "custom_files", + ), + ( + Calculations.Domains, + LayerModels.CustomXY, + RAT.models.ContrastWithRatio(name="Test Contrast", model=["undefined"]), + "custom_files", + ), + ( + Calculations.NonPolarised, + LayerModels.StandardLayers, + RAT.models.Contrast(name="Test Contrast", model=["undefined", "undefined", "undefined"]), + "layers", + ), + ( + Calculations.NonPolarised, + LayerModels.CustomLayers, + RAT.models.Contrast(name="Test Contrast", model=["undefined"]), + "custom_files", + ), + ( + Calculations.NonPolarised, + LayerModels.CustomXY, + RAT.models.Contrast(name="Test Contrast", model=["undefined"]), + "custom_files", + ), + ], +) +def test_allowed_contrast_models( + input_calc: Calculations, + input_model: LayerModels, + test_contrast: "RAT.models", + field_name: str, +) -> None: """If any value in the model field of the contrasts is set to a value not specified in the appropriate part of the project, we should raise a ValidationError. """ - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The values: ' - f'"{", ".join(test_contrast.model)}" in the "model" field of ' - f'"contrasts" must be defined in "{field_name}".'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Project\n Value error, The values: ' + f'"{", ".join(test_contrast.model)}" in the "model" field of ' + f'"contrasts" must be defined in "{field_name}".', + ): RAT.Project(calculation=input_calc, model=input_model, contrasts=RAT.ClassList(test_contrast)) @@ -579,9 +809,12 @@ def test_allowed_domain_contrast_models() -> None: the project, we should raise a ValidationError. """ test_contrast = RAT.models.DomainContrast(name="Test Domain Contrast", model=["undefined"]) - with pytest.raises(pydantic.ValidationError, match='1 validation error for Project\n Value error, The values: ' - '"undefined" in the "model" field of "domain_contrasts" must be ' - 'defined in "layers".'): + with pytest.raises( + pydantic.ValidationError, + match="1 validation error for Project\n Value error, The values: " + '"undefined" in the "model" field of "domain_contrasts" must be ' + 'defined in "layers".', + ): RAT.Project(calculation=Calculations.Domains, domain_contrasts=RAT.ClassList(test_contrast)) @@ -592,92 +825,115 @@ def test_repr(default_project_repr: str) -> None: def test_get_all_names(test_project) -> None: """We should be able to get the names of all the models defined in the project.""" - assert test_project.get_all_names() == {"parameters": ["Substrate Roughness", "Test Thickness", "Test SLD", - "Test Roughness"], - "bulk_in": ["SLD Air"], - "bulk_out": ["SLD D2O"], - "scalefactors": ["Scalefactor 1"], - "domain_ratios": [], - "background_parameters": ["Background Param 1"], - "backgrounds": ["Background 1"], - "resolution_parameters": ["Resolution Param 1"], - "resolutions": ["Resolution 1"], - "custom_files": ["Test Custom File"], - "data": ["Simulation"], - "layers": ["Test Layer"], - "domain_contrasts": [], - "contrasts": ["Test Contrast"], - } + assert test_project.get_all_names() == { + "parameters": ["Substrate Roughness", "Test Thickness", "Test SLD", "Test Roughness"], + "bulk_in": ["SLD Air"], + "bulk_out": ["SLD D2O"], + "scalefactors": ["Scalefactor 1"], + "domain_ratios": [], + "background_parameters": ["Background Param 1"], + "backgrounds": ["Background 1"], + "resolution_parameters": ["Resolution Param 1"], + "resolutions": ["Resolution 1"], + "custom_files": ["Test Custom File"], + "data": ["Simulation"], + "layers": ["Test Layer"], + "domain_contrasts": [], + "contrasts": ["Test Contrast"], + } def test_get_all_protected_parameters(test_project) -> None: """We should be able to get the names of all the protected parameters defined in the project.""" - assert test_project.get_all_protected_parameters() == {"parameters": ["Substrate Roughness"], - "bulk_in": [], - "bulk_out": [], - "scalefactors": [], - "domain_ratios": [], - "background_parameters": [], - "resolution_parameters": [], - } - - -@pytest.mark.parametrize("test_value", [ - "", - "Background Param 1", -]) + assert test_project.get_all_protected_parameters() == { + "parameters": ["Substrate Roughness"], + "bulk_in": [], + "bulk_out": [], + "scalefactors": [], + "domain_ratios": [], + "background_parameters": [], + "resolution_parameters": [], + } + + +@pytest.mark.parametrize( + "test_value", + [ + "", + "Background Param 1", + ], +) def test_check_allowed_values(test_value: str) -> None: """We should not raise an error if string values are defined and on the list of allowed values.""" project = RAT.Project.model_construct(backgrounds=RAT.ClassList(RAT.models.Background(value_1=test_value))) assert project.check_allowed_values("backgrounds", ["value_1"], ["Background Param 1"]) is None -@pytest.mark.parametrize("test_value", [ - "Undefined Param", -]) +@pytest.mark.parametrize( + "test_value", + [ + "Undefined Param", + ], +) def test_check_allowed_values_not_on_list(test_value: str) -> None: """If string values are defined and are not included on the list of allowed values we should raise a ValueError.""" project = RAT.Project.model_construct(backgrounds=RAT.ClassList(RAT.models.Background(value_1=test_value))) - with pytest.raises(ValueError, match=f'The value "{test_value}" in the "value_1" field of "backgrounds" must be ' - f'defined in "background_parameters".'): + with pytest.raises( + ValueError, + match=f'The value "{test_value}" in the "value_1" field of "backgrounds" must be ' + f'defined in "background_parameters".', + ): project.check_allowed_values("backgrounds", ["value_1"], ["Background Param 1"]) -@pytest.mark.parametrize("test_values", [ - [], - ["Test Layer"], -]) +@pytest.mark.parametrize( + "test_values", + [ + [], + ["Test Layer"], + ], +) def test_check_contrast_model_allowed_values(test_values: list[str]) -> None: - """We should not raise an error if values are defined in a non-empty list and all are on the list of allowed values. - """ - project = RAT.Project.model_construct(contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", - model=test_values))) + """We should not raise an error if values are defined in a non-empty list and all are on the list of allowed values.""" + project = RAT.Project.model_construct( + contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", model=test_values)), + ) assert project.check_contrast_model_allowed_values("contrasts", ["Test Layer"], "layers") is None -@pytest.mark.parametrize("test_values", [ - ["Undefined Param"], - ["Test Layer", "Undefined Param"], -]) +@pytest.mark.parametrize( + "test_values", + [ + ["Undefined Param"], + ["Test Layer", "Undefined Param"], + ], +) def test_check_allowed_contrast_model_not_on_list(test_values: list[str]) -> None: """If string values are defined in a non-empty list and any of them are not included on the list of allowed values we should raise a ValueError. """ - project = RAT.Project.model_construct(contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", - model=test_values))) - with pytest.raises(ValueError, match=f'The values: "{", ".join(str(i) for i in test_values)}" in the "model" field ' - f'of "contrasts" must be defined in "layers".'): + project = RAT.Project.model_construct( + contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", model=test_values)), + ) + with pytest.raises( + ValueError, + match=f'The values: "{", ".join(str(i) for i in test_values)}" in the "model" field ' + f'of "contrasts" must be defined in "layers".', + ): project.check_contrast_model_allowed_values("contrasts", ["Test Layer"], "layers") -@pytest.mark.parametrize(["input_calc", "input_model", "expected_field_name"], [ - (Calculations.Domains, LayerModels.StandardLayers, "domain_contrasts"), - (Calculations.NonPolarised, LayerModels.StandardLayers, "layers"), - (Calculations.Domains, LayerModels.CustomLayers, "custom_files"), - (Calculations.NonPolarised, LayerModels.CustomLayers, "custom_files"), - (Calculations.Domains, LayerModels.CustomXY, "custom_files"), - (Calculations.NonPolarised, LayerModels.CustomXY, "custom_files"), -]) +@pytest.mark.parametrize( + ["input_calc", "input_model", "expected_field_name"], + [ + (Calculations.Domains, LayerModels.StandardLayers, "domain_contrasts"), + (Calculations.NonPolarised, LayerModels.StandardLayers, "layers"), + (Calculations.Domains, LayerModels.CustomLayers, "custom_files"), + (Calculations.NonPolarised, LayerModels.CustomLayers, "custom_files"), + (Calculations.Domains, LayerModels.CustomXY, "custom_files"), + (Calculations.NonPolarised, LayerModels.CustomXY, "custom_files"), + ], +) def test_get_contrast_model_field(input_calc: Calculations, input_model: LayerModels, expected_field_name: str) -> None: """Each combination of calculation and model determines the field where the values of "model" field of "contrasts" are defined. @@ -686,10 +942,13 @@ def test_get_contrast_model_field(input_calc: Calculations, input_model: LayerMo assert project.get_contrast_model_field() == expected_field_name -@pytest.mark.parametrize("input_filename", [ - "test_script.py", - "test_script", -]) +@pytest.mark.parametrize( + "input_filename", + [ + "test_script.py", + "test_script", + ], +) def test_write_script(test_project, temp_dir, test_project_script, input_filename: str) -> None: """Test the script we write to regenerate the project is created and runs as expected.""" test_project.write_script("problem", os.path.join(temp_dir, input_filename)) @@ -712,210 +971,246 @@ def test_write_script(test_project, temp_dir, test_project_script, input_filenam assert getattr(new_project, class_list) == getattr(test_project, class_list) -@pytest.mark.parametrize("extension", [ - ".txt" - ".f90" - ".m" - ".pyc", -]) +@pytest.mark.parametrize( + "extension", + [ + ".txt", + ".f90", + ".m", + ".pyc", + ], +) def test_write_script_wrong_extension(test_project, extension: str) -> None: """If we try to write the script to anything other than a ".py" file, we raise a ValueError.""" with pytest.raises(ValueError, match='The script name provided to "write_script" must use the ".py" format'): test_project.write_script("problem", "test" + extension) -@pytest.mark.parametrize(["class_list", "field"], [ - ("backgrounds", "value_1"), - ("backgrounds", "value_2"), - ("backgrounds", "value_3"), - ("backgrounds", "value_4"), - ("backgrounds", "value_5"), - ("resolutions", "value_1"), - ("resolutions", "value_2"), - ("resolutions", "value_3"), - ("resolutions", "value_4"), - ("resolutions", "value_5"), - ("layers", "thickness"), - ("layers", "SLD"), - ("layers", "roughness"), - ("contrasts", "data"), - ("contrasts", "background"), - ("contrasts", "bulk_in"), - ("contrasts", "bulk_out"), - ("contrasts", "scalefactor"), - ("contrasts", "resolution"), -]) +@pytest.mark.parametrize( + ["class_list", "field"], + [ + ("backgrounds", "value_1"), + ("backgrounds", "value_2"), + ("backgrounds", "value_3"), + ("backgrounds", "value_4"), + ("backgrounds", "value_5"), + ("resolutions", "value_1"), + ("resolutions", "value_2"), + ("resolutions", "value_3"), + ("resolutions", "value_4"), + ("resolutions", "value_5"), + ("layers", "thickness"), + ("layers", "SLD"), + ("layers", "roughness"), + ("contrasts", "data"), + ("contrasts", "background"), + ("contrasts", "bulk_in"), + ("contrasts", "bulk_out"), + ("contrasts", "scalefactor"), + ("contrasts", "resolution"), + ], +) def test_wrap_set(test_project, class_list: str, field: str) -> None: """If we set the field values of a model in a ClassList as undefined values, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "{class_list}" must be ' - f'defined in ' - f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Project\n Value error, The value ' + f'"undefined" in the "{field}" field of "{class_list}" must be ' + f'defined in ' + f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".', + ): test_attribute.set_fields(0, **{field: "undefined"}) # Ensure invalid model was not changed assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ("background_parameters", "Background Param 1", "value_1"), - ("resolution_parameters", "Resolution Param 1", "value_1"), - ("parameters", "Test SLD", "SLD"), - ("data", "Simulation", "data"), - ("backgrounds", "Background 1", "background"), - ("bulk_in", "SLD Air", "bulk_in"), - ("bulk_out", "SLD D2O", "bulk_out"), - ("scalefactors", "Scalefactor 1", "scalefactor"), - ("resolutions", "Resolution 1", "resolution"), -]) +@pytest.mark.parametrize( + ["class_list", "parameter", "field"], + [ + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test SLD", "SLD"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), + ], +) def test_wrap_del(test_project, class_list: str, parameter: str, field: str) -> None: """If we delete a model in a ClassList containing values defined elsewhere, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) index = test_attribute.index(parameter) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"{parameter}" in the "{field}" field of ' - f'"{RAT.project.model_names_used_in[class_list].attribute}" ' - f'must be defined in "{class_list}".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"{parameter}" in the "{field}" field of ' + f'"{RAT.project.model_names_used_in[class_list].attribute}" ' + f'must be defined in "{class_list}".', + ): del test_attribute[index] # Ensure model was not deleted assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ("backgrounds", "value_1", {}), - ("backgrounds", "value_2", {}), - ("backgrounds", "value_3", {}), - ("backgrounds", "value_4", {}), - ("backgrounds", "value_5", {}), - ("resolutions", "value_1", {}), - ("resolutions", "value_2", {}), - ("resolutions", "value_3", {}), - ("resolutions", "value_4", {}), - ("resolutions", "value_5", {}), - ("layers", "thickness", layer_params), - ("layers", "SLD", layer_params), - ("layers", "roughness", layer_params), - ("contrasts", "data", {}), - ("contrasts", "background", {}), - ("contrasts", "bulk_in", {}), - ("contrasts", "bulk_out", {}), - ("contrasts", "scalefactor", {}), - ("contrasts", "resolution", {}), -]) +@pytest.mark.parametrize( + ["class_list", "field", "model_params"], + [ + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), + ], +) def test_wrap_iadd(test_project, class_list: str, field: str, model_params: dict) -> None: """If we add a model containing undefined values to a ClassList, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) input_model = getattr(RAT.models, RAT.project.model_in_classlist[class_list]) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "{class_list}" must be ' - f'defined in ' - f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Project\n Value error, The value ' + f'"undefined" in the "{field}" field of "{class_list}" must be ' + f'defined in ' + f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".', + ): test_attribute += [input_model(**{**model_params, field: "undefined"})] # Ensure invalid model was not added assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ("backgrounds", "value_1", {}), - ("backgrounds", "value_2", {}), - ("backgrounds", "value_3", {}), - ("backgrounds", "value_4", {}), - ("backgrounds", "value_5", {}), - ("resolutions", "value_1", {}), - ("resolutions", "value_2", {}), - ("resolutions", "value_3", {}), - ("resolutions", "value_4", {}), - ("resolutions", "value_5", {}), - ("layers", "thickness", layer_params), - ("layers", "SLD", layer_params), - ("layers", "roughness", layer_params), - ("contrasts", "data", {}), - ("contrasts", "background", {}), - ("contrasts", "bulk_in", {}), - ("contrasts", "bulk_out", {}), - ("contrasts", "scalefactor", {}), - ("contrasts", "resolution", {}), -]) +@pytest.mark.parametrize( + ["class_list", "field", "model_params"], + [ + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), + ], +) def test_wrap_append(test_project, class_list: str, field: str, model_params: dict) -> None: """If we append a model containing undefined values to a ClassList, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) input_model = getattr(RAT.models, RAT.project.model_in_classlist[class_list]) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "{class_list}" must be ' - f'defined in ' - f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Project\n Value error, The value ' + f'"undefined" in the "{field}" field of "{class_list}" must be ' + f'defined in ' + f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".', + ): test_attribute.append(input_model(**{**model_params, field: "undefined"})) # Ensure invalid model was not appended assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ("backgrounds", "value_1", {}), - ("backgrounds", "value_2", {}), - ("backgrounds", "value_3", {}), - ("backgrounds", "value_4", {}), - ("backgrounds", "value_5", {}), - ("resolutions", "value_1", {}), - ("resolutions", "value_2", {}), - ("resolutions", "value_3", {}), - ("resolutions", "value_4", {}), - ("resolutions", "value_5", {}), - ("layers", "thickness", layer_params), - ("layers", "SLD", layer_params), - ("layers", "roughness", layer_params), - ("contrasts", "data", {}), - ("contrasts", "background", {}), - ("contrasts", "bulk_in", {}), - ("contrasts", "bulk_out", {}), - ("contrasts", "scalefactor", {}), - ("contrasts", "resolution", {}), -]) +@pytest.mark.parametrize( + ["class_list", "field", "model_params"], + [ + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), + ], +) def test_wrap_insert(test_project, class_list: str, field: str, model_params: dict) -> None: """If we insert a model containing undefined values into a ClassList, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) input_model = getattr(RAT.models, RAT.project.model_in_classlist[class_list]) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "{class_list}" must be ' - f'defined in ' - f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Project\n Value error, The value ' + f'"undefined" in the "{field}" field of "{class_list}" must be ' + f'defined in ' + f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".', + ): test_attribute.insert(0, input_model(**{**model_params, field: "undefined"})) # Ensure invalid model was not inserted assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "field"], [ - ("backgrounds", "value_1"), - ("backgrounds", "value_2"), - ("backgrounds", "value_3"), - ("backgrounds", "value_4"), - ("backgrounds", "value_5"), - ("resolutions", "value_1"), - ("resolutions", "value_2"), - ("resolutions", "value_3"), - ("resolutions", "value_4"), - ("resolutions", "value_5"), - ("contrasts", "data"), - ("contrasts", "background"), - ("contrasts", "bulk_in"), - ("contrasts", "bulk_out"), - ("contrasts", "scalefactor"), - ("contrasts", "resolution"), -]) +@pytest.mark.parametrize( + ["class_list", "field"], + [ + ("backgrounds", "value_1"), + ("backgrounds", "value_2"), + ("backgrounds", "value_3"), + ("backgrounds", "value_4"), + ("backgrounds", "value_5"), + ("resolutions", "value_1"), + ("resolutions", "value_2"), + ("resolutions", "value_3"), + ("resolutions", "value_4"), + ("resolutions", "value_5"), + ("contrasts", "data"), + ("contrasts", "background"), + ("contrasts", "bulk_in"), + ("contrasts", "bulk_out"), + ("contrasts", "scalefactor"), + ("contrasts", "resolution"), + ], +) def test_wrap_insert_type_error(test_project, class_list: str, field: str) -> None: """If we raise a TypeError using the wrapped insert routine, we should re-raise the error.""" test_attribute = getattr(test_project, class_list) @@ -929,116 +1224,140 @@ def test_wrap_insert_type_error(test_project, class_list: str, field: str) -> No assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ("background_parameters", "Background Param 1", "value_1"), - ("resolution_parameters", "Resolution Param 1", "value_1"), - ("parameters", "Test SLD", "SLD"), - ("data", "Simulation", "data"), - ("backgrounds", "Background 1", "background"), - ("bulk_in", "SLD Air", "bulk_in"), - ("bulk_out", "SLD D2O", "bulk_out"), - ("scalefactors", "Scalefactor 1", "scalefactor"), - ("resolutions", "Resolution 1", "resolution"), -]) +@pytest.mark.parametrize( + ["class_list", "parameter", "field"], + [ + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test SLD", "SLD"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), + ], +) def test_wrap_pop(test_project, class_list: str, parameter: str, field: str) -> None: """If we pop a model in a ClassList containing values defined elsewhere, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) index = test_attribute.index(parameter) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"{parameter}" in the "{field}" field of ' - f'"{RAT.project.model_names_used_in[class_list].attribute}" ' - f'must be defined in "{class_list}".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"{parameter}" in the "{field}" field of ' + f'"{RAT.project.model_names_used_in[class_list].attribute}" ' + f'must be defined in "{class_list}".', + ): test_attribute.pop(index) # Ensure model was not popped assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ("background_parameters", "Background Param 1", "value_1"), - ("resolution_parameters", "Resolution Param 1", "value_1"), - ("parameters", "Test SLD", "SLD"), - ("data", "Simulation", "data"), - ("backgrounds", "Background 1", "background"), - ("bulk_in", "SLD Air", "bulk_in"), - ("bulk_out", "SLD D2O", "bulk_out"), - ("scalefactors", "Scalefactor 1", "scalefactor"), - ("resolutions", "Resolution 1", "resolution"), -]) +@pytest.mark.parametrize( + ["class_list", "parameter", "field"], + [ + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test SLD", "SLD"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), + ], +) def test_wrap_remove(test_project, class_list: str, parameter: str, field: str) -> None: """If we remove a model in a ClassList containing values defined elsewhere, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"{parameter}" in the "{field}" field of ' - f'"{RAT.project.model_names_used_in[class_list].attribute}" ' - f'must be defined in "{class_list}".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"{parameter}" in the "{field}" field of ' + f'"{RAT.project.model_names_used_in[class_list].attribute}" ' + f'must be defined in "{class_list}".', + ): test_attribute.remove(parameter) # Ensure model was not removed assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "parameter", "field"], [ - ("background_parameters", "Background Param 1", "value_1"), - ("resolution_parameters", "Resolution Param 1", "value_1"), - ("parameters", "Test Thickness", "thickness"), - ("data", "Simulation", "data"), - ("backgrounds", "Background 1", "background"), - ("bulk_in", "SLD Air", "bulk_in"), - ("bulk_out", "SLD D2O", "bulk_out"), - ("scalefactors", "Scalefactor 1", "scalefactor"), - ("resolutions", "Resolution 1", "resolution"), -]) +@pytest.mark.parametrize( + ["class_list", "parameter", "field"], + [ + ("background_parameters", "Background Param 1", "value_1"), + ("resolution_parameters", "Resolution Param 1", "value_1"), + ("parameters", "Test Thickness", "thickness"), + ("data", "Simulation", "data"), + ("backgrounds", "Background 1", "background"), + ("bulk_in", "SLD Air", "bulk_in"), + ("bulk_out", "SLD D2O", "bulk_out"), + ("scalefactors", "Scalefactor 1", "scalefactor"), + ("resolutions", "Resolution 1", "resolution"), + ], +) def test_wrap_clear(test_project, class_list: str, parameter: str, field: str) -> None: """If we clear a ClassList containing models with values defined elsewhere, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"{parameter}" in the "{field}" field of ' - f'"{RAT.project.model_names_used_in[class_list].attribute}" ' - f'must be defined in "{class_list}".'): + with pytest.raises( + pydantic.ValidationError, + match=f"1 validation error for Project\n Value error, The value " + f'"{parameter}" in the "{field}" field of ' + f'"{RAT.project.model_names_used_in[class_list].attribute}" ' + f'must be defined in "{class_list}".', + ): test_attribute.clear() # Ensure list was not cleared assert test_attribute == orig_class_list -@pytest.mark.parametrize(["class_list", "field", "model_params"], [ - ("backgrounds", "value_1", {}), - ("backgrounds", "value_2", {}), - ("backgrounds", "value_3", {}), - ("backgrounds", "value_4", {}), - ("backgrounds", "value_5", {}), - ("resolutions", "value_1", {}), - ("resolutions", "value_2", {}), - ("resolutions", "value_3", {}), - ("resolutions", "value_4", {}), - ("resolutions", "value_5", {}), - ("layers", "thickness", layer_params), - ("layers", "SLD", layer_params), - ("layers", "roughness", layer_params), - ("contrasts", "data", {}), - ("contrasts", "background", {}), - ("contrasts", "bulk_in", {}), - ("contrasts", "bulk_out", {}), - ("contrasts", "scalefactor", {}), - ("contrasts", "resolution", {}), -]) +@pytest.mark.parametrize( + ["class_list", "field", "model_params"], + [ + ("backgrounds", "value_1", {}), + ("backgrounds", "value_2", {}), + ("backgrounds", "value_3", {}), + ("backgrounds", "value_4", {}), + ("backgrounds", "value_5", {}), + ("resolutions", "value_1", {}), + ("resolutions", "value_2", {}), + ("resolutions", "value_3", {}), + ("resolutions", "value_4", {}), + ("resolutions", "value_5", {}), + ("layers", "thickness", layer_params), + ("layers", "SLD", layer_params), + ("layers", "roughness", layer_params), + ("contrasts", "data", {}), + ("contrasts", "background", {}), + ("contrasts", "bulk_in", {}), + ("contrasts", "bulk_out", {}), + ("contrasts", "scalefactor", {}), + ("contrasts", "resolution", {}), + ], +) def test_wrap_extend(test_project, class_list: str, field: str, model_params: dict) -> None: """If we extend a ClassList with model containing undefined values, we should raise a ValidationError.""" test_attribute = getattr(test_project, class_list) orig_class_list = copy.deepcopy(test_attribute) input_model = getattr(RAT.models, RAT.project.model_in_classlist[class_list]) - with pytest.raises(pydantic.ValidationError, match=f'1 validation error for Project\n Value error, The value ' - f'"undefined" in the "{field}" field of "{class_list}" must be ' - f'defined in ' - f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".'): + with pytest.raises( + pydantic.ValidationError, + match=f'1 validation error for Project\n Value error, The value ' + f'"undefined" in the "{field}" field of "{class_list}" must be ' + f'defined in ' + f'"{RAT.project.values_defined_in[f"{class_list}.{field}"]}".', + ): test_attribute.extend([input_model(**{**model_params, field: "undefined"})]) # Ensure invalid model was not appended diff --git a/tests/test_run.py b/tests/test_run.py index 1009b696..115fc971 100644 --- a/tests/test_run.py +++ b/tests/test_run.py @@ -20,50 +20,215 @@ def input_project(): """A cut-down version of the input Project object for a reflectivity calculation set out in "DSPC_standard_layers.py". """ - project = RAT.Project(name="original_dspc_bilayer", calculation="non polarised", model="standard layers", - geometry="substrate/liquid", absorption=False) + project = RAT.Project( + name="original_dspc_bilayer", + calculation="non polarised", + model="standard layers", + geometry="substrate/liquid", + absorption=False, + ) # Set up the relevant parameters - project.parameters.append(name="Oxide Thickness", min=5.0, value=19.54, max=60.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) - project.parameters.append(name="Oxide SLD", min=3.39e-06, value=3.39e-06, max=3.41e-06, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="SAM Tails Thickness", min=15.0, value=22.66, max=40.0, fit=True, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="SAM Tails SLD", min=-5e-07, value=-4.01e-07, max=-3e-07, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="SAM Tails Hydration", min=1.0, value=5.252, max=50.0, fit=True, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="SAM Roughness", min=1.0, value=5.64, max=15.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) - project.parameters.append(name="CW Thickness", min=10.0, value=17.12, max=28.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) - project.parameters.append(name="CW SLD", min=0.0, value=0.0, max=1e-09, fit=False, prior_type="uniform", mu=0.0, - sigma=np.inf) - project.parameters.append(name="SAM Heads Thickness", min=5.0, value=8.56, max=17.0, fit=True, - prior_type="gaussian", mu=10.0, sigma=2.0) - project.parameters.append(name="SAM Heads SLD", min=1.0e-07, value=1.75e-06, max=2.0e-06, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="SAM Heads Hydration", min=10.0, value=45.45, max=50.0, fit=True, - prior_type="uniform", mu=30.0, sigma=3.0) - project.parameters.append(name="Bilayer Heads Thickness", min=7.0, value=10.7, max=17.0, fit=True, - prior_type="gaussian", mu=10.0, sigma=2.0) - project.parameters.append(name="Bilayer Heads SLD", min=5.0e-07, value=1.47e-06, max=1.5e-06, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="Bilayer Roughness", min=2.0, value=6.014, max=15.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) - project.parameters.append(name="Bilayer Tails Thickness", min=14.0, value=17.82, max=22.0, fit=True, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="Bilayer Tails SLD", min=-5.0e-07, value=-4.61e-07, max=0.0, fit=False, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="Bilayer Tails Hydration", min=10.0, value=17.64, max=50.0, fit=True, - prior_type="uniform", mu=0.0, sigma=np.inf) - project.parameters.append(name="Bilayer Heads Hydration", min=10.0, value=36.15, max=50.0, fit=True, - prior_type="gaussian", mu=30.0, sigma=3.0) - project.parameters.append(name="CW Hydration", min=99.9, value=100.0, max=100.0, fit=False, prior_type="uniform", - mu=0.0, sigma=np.inf) - project.parameters.append(name="Oxide Hydration", min=0.0, value=23.61, max=60.0, fit=True, prior_type="uniform", - mu=0.0, sigma=np.inf) + project.parameters.append( + name="Oxide Thickness", + min=5.0, + value=19.54, + max=60.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Oxide SLD", + min=3.39e-06, + value=3.39e-06, + max=3.41e-06, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="SAM Tails Thickness", + min=15.0, + value=22.66, + max=40.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="SAM Tails SLD", + min=-5e-07, + value=-4.01e-07, + max=-3e-07, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="SAM Tails Hydration", + min=1.0, + value=5.252, + max=50.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="SAM Roughness", + min=1.0, + value=5.64, + max=15.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="CW Thickness", + min=10.0, + value=17.12, + max=28.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="CW SLD", + min=0.0, + value=0.0, + max=1e-09, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="SAM Heads Thickness", + min=5.0, + value=8.56, + max=17.0, + fit=True, + prior_type="gaussian", + mu=10.0, + sigma=2.0, + ) + project.parameters.append( + name="SAM Heads SLD", + min=1.0e-07, + value=1.75e-06, + max=2.0e-06, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="SAM Heads Hydration", + min=10.0, + value=45.45, + max=50.0, + fit=True, + prior_type="uniform", + mu=30.0, + sigma=3.0, + ) + project.parameters.append( + name="Bilayer Heads Thickness", + min=7.0, + value=10.7, + max=17.0, + fit=True, + prior_type="gaussian", + mu=10.0, + sigma=2.0, + ) + project.parameters.append( + name="Bilayer Heads SLD", + min=5.0e-07, + value=1.47e-06, + max=1.5e-06, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Bilayer Roughness", + min=2.0, + value=6.014, + max=15.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Bilayer Tails Thickness", + min=14.0, + value=17.82, + max=22.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Bilayer Tails SLD", + min=-5.0e-07, + value=-4.61e-07, + max=0.0, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Bilayer Tails Hydration", + min=10.0, + value=17.64, + max=50.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Bilayer Heads Hydration", + min=10.0, + value=36.15, + max=50.0, + fit=True, + prior_type="gaussian", + mu=30.0, + sigma=3.0, + ) + project.parameters.append( + name="CW Hydration", + min=99.9, + value=100.0, + max=100.0, + fit=False, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) + project.parameters.append( + name="Oxide Hydration", + min=0.0, + value=23.61, + max=60.0, + fit=True, + prior_type="uniform", + mu=0.0, + sigma=np.inf, + ) project.parameters.set_fields(0, max=10) @@ -80,10 +245,20 @@ def input_project(): del project.backgrounds[0] del project.background_parameters[0] - project.background_parameters.append(name="Background parameter D2O", min=5.0e-10, value=2.23e-06, max=7.0e-06, - fit=True) - project.background_parameters.append(name="Background parameter SMW", min=1.0e-10, value=3.38e-06, max=4.99e-06, - fit=True) + project.background_parameters.append( + name="Background parameter D2O", + min=5.0e-10, + value=2.23e-06, + max=7.0e-06, + fit=True, + ) + project.background_parameters.append( + name="Background parameter SMW", + min=1.0e-10, + value=3.38e-06, + max=4.99e-06, + fit=True, + ) return project @@ -98,10 +273,31 @@ def reflectivity_calculation_problem(): problem.modelType = LayerModels.StandardLayers problem.geometry = Geometries.SubstrateLiquid problem.useImaginary = False - problem.params = np.array([3.000e+00, 1.954e+01, 3.390e-06, 2.266e+01, -4.010e-07, 5.252e+00, - 5.640e+00, 1.712e+01, 0.000e+00, 8.560e+00, 1.750e-06, 4.545e+01, - 1.070e+01, 1.470e-06, 6.014e+00, 1.782e+01, -4.610e-07, 1.764e+01, - 3.615e+01, 1.000e+02, 2.361e+01]) + problem.params = np.array( + [ + 3.000e00, + 1.954e01, + 3.390e-06, + 2.266e01, + -4.010e-07, + 5.252e00, + 5.640e00, + 1.712e01, + 0.000e00, + 8.560e00, + 1.750e-06, + 4.545e01, + 1.070e01, + 1.470e-06, + 6.014e00, + 1.782e01, + -4.610e-07, + 1.764e01, + 3.615e01, + 1.000e02, + 2.361e01, + ], + ) problem.bulkIn = np.array([2.073e-06]) problem.bulkOut = np.array([5.98e-06, 2.21e-06]) problem.qzshifts = np.array([0.0]) @@ -124,40 +320,80 @@ def reflectivity_calculation_problem(): problem.numberOfContrasts = 2.0 problem.numberOfLayers = 6.0 problem.numberOfDomainContrasts = 0.0 - problem.fitParams = np.array([3.000e+00, 1.954e+01, 2.266e+01, 5.252e+00, 5.640e+00, 1.712e+01, 8.560e+00, - 4.545e+01, 1.070e+01, 6.014e+00, 1.782e+01, 1.764e+01, 3.615e+01, 2.361e+01, - 2.230e-06, 3.380e-06, 5.980e-06, 2.210e-06]) - problem.otherParams = np.array([3.390e-06, -4.010e-07, 0.000e+00, 1.750e-06, 1.470e-06, -4.610e-07, - 1.000e+02, 1.000e-01, 1.500e-01, 2.073e-06, 3.000e-02]) - problem.fitLimits = np.array([[1.00e+00, 1.00e+01], - [5.00e+00, 6.00e+01], - [1.50e+01, 3.50e+01], - [1.00e+00, 5.00e+01], - [1.00e+00, 1.50e+01], - [1.00e+01, 2.80e+01], - [5.00e+00, 1.70e+01], - [1.00e+01, 5.00e+01], - [7.00e+00, 1.70e+01], - [2.00e+00, 1.50e+01], - [1.40e+01, 2.20e+01], - [1.00e+01, 5.00e+01], - [1.00e+01, 5.00e+01], - [0.00e+00, 6.00e+01], - [5.00e-10, 7.00e-06], - [1.00e-10, 4.99e-06], - [5.50e-06, 6.40e-06], - [1.00e-06, 4.99e-06]]) - problem.otherLimits = np.array([[3.39e-06, 3.41e-06], - [-5.00e-07, -3.00e-07], - [0.00e+00, 1.00e-09], - [1.00e-07, 2.00e-06], - [5.00e-07, 1.50e-06], - [-5.00e-07, 0.00e+00], - [9.99e+01, 1.00e+02], - [5.00e-02, 2.00e-01], - [5.00e-02, 2.00e-01], - [2.00e-06, 2.10e-06], - [1.00e-02, 5.00e-02]]) + problem.fitParams = np.array( + [ + 3.000e00, + 1.954e01, + 2.266e01, + 5.252e00, + 5.640e00, + 1.712e01, + 8.560e00, + 4.545e01, + 1.070e01, + 6.014e00, + 1.782e01, + 1.764e01, + 3.615e01, + 2.361e01, + 2.230e-06, + 3.380e-06, + 5.980e-06, + 2.210e-06, + ], + ) + problem.otherParams = np.array( + [ + 3.390e-06, + -4.010e-07, + 0.000e00, + 1.750e-06, + 1.470e-06, + -4.610e-07, + 1.000e02, + 1.000e-01, + 1.500e-01, + 2.073e-06, + 3.000e-02, + ], + ) + problem.fitLimits = np.array( + [ + [1.00e00, 1.00e01], + [5.00e00, 6.00e01], + [1.50e01, 3.50e01], + [1.00e00, 5.00e01], + [1.00e00, 1.50e01], + [1.00e01, 2.80e01], + [5.00e00, 1.70e01], + [1.00e01, 5.00e01], + [7.00e00, 1.70e01], + [2.00e00, 1.50e01], + [1.40e01, 2.20e01], + [1.00e01, 5.00e01], + [1.00e01, 5.00e01], + [0.00e00, 6.00e01], + [5.00e-10, 7.00e-06], + [1.00e-10, 4.99e-06], + [5.50e-06, 6.40e-06], + [1.00e-06, 4.99e-06], + ], + ) + problem.otherLimits = np.array( + [ + [3.39e-06, 3.41e-06], + [-5.00e-07, -3.00e-07], + [0.00e00, 1.00e-09], + [1.00e-07, 2.00e-06], + [5.00e-07, 1.50e-06], + [-5.00e-07, 0.00e00], + [9.99e01, 1.00e02], + [5.00e-02, 2.00e-01], + [5.00e-02, 2.00e-01], + [2.00e-06, 2.10e-06], + [1.00e-02, 5.00e-02], + ], + ) return problem @@ -174,12 +410,31 @@ def dream_problem(): problem.modelType = LayerModels.StandardLayers problem.geometry = Geometries.SubstrateLiquid problem.useImaginary = False - problem.params = np.array([6.19503045e+00, 1.84420960e+01, 3.39000000e-06, 2.11039621e+01, - -4.01000000e-07, 8.75538121e+00, 3.72292994e+00, 1.84624551e+01, - 0.00000000e+00, 1.02316734e+01, 1.75000000e-06, 2.31156093e+01, - 1.09906265e+01, 1.47000000e-06, 5.71005361e+00, 1.67933822e+01, - -4.61000000e-07, 1.72009856e+01, 3.00260126e+01, 1.00000000e+02, - 2.94448999e+01]) + problem.params = np.array( + [ + 6.19503045e00, + 1.84420960e01, + 3.39000000e-06, + 2.11039621e01, + -4.01000000e-07, + 8.75538121e00, + 3.72292994e00, + 1.84624551e01, + 0.00000000e00, + 1.02316734e01, + 1.75000000e-06, + 2.31156093e01, + 1.09906265e01, + 1.47000000e-06, + 5.71005361e00, + 1.67933822e01, + -4.61000000e-07, + 1.72009856e01, + 3.00260126e01, + 1.00000000e02, + 2.94448999e01, + ], + ) problem.bulkIn = np.array([2.073e-06]) problem.bulkOut = np.array([6.01489149e-06, 1.59371685e-06]) problem.qzshifts = np.array([0.0]) @@ -202,53 +457,99 @@ def dream_problem(): problem.numberOfContrasts = 2.0 problem.numberOfLayers = 6.0 problem.numberOfDomainContrasts = 0.0 - problem.fitParams = np.array([6.19503045e+00, 1.84420960e+01, 2.11039621e+01, 8.75538121e+00, - 3.72292994e+00, 1.84624551e+01, 1.02316734e+01, 2.31156093e+01, - 1.09906265e+01, 5.71005361e+00, 1.67933822e+01, 1.72009856e+01, - 3.00260126e+01, 2.94448999e+01, 2.37113128e-06, 1.99006694e-06, - 6.01489149e-06, 1.59371685e-06]) - problem.otherParams = np.array([3.390e-06, -4.010e-07, 0.000e+00, 1.750e-06, 1.470e-06, -4.610e-07, - 1.000e+02, 1.000e-01, 1.500e-01, 2.073e-06, 3.000e-02, 0.000e+00]) - problem.fitLimits = np.array([[1.00e+00, 1.00e+01], - [5.00e+00, 6.00e+01], - [1.50e+01, 3.50e+01], - [1.00e+00, 5.00e+01], - [1.00e+00, 1.50e+01], - [1.00e+01, 2.80e+01], - [5.00e+00, 1.70e+01], - [1.00e+01, 5.00e+01], - [7.00e+00, 1.70e+01], - [2.00e+00, 1.50e+01], - [1.40e+01, 2.20e+01], - [1.00e+01, 5.00e+01], - [1.00e+01, 5.00e+01], - [0.00e+00, 6.00e+01], - [5.00e-10, 7.00e-06], - [1.00e-10, 4.99e-06], - [5.50e-06, 6.40e-06], - [1.00e-06, 4.99e-06]]) - problem.otherLimits = np.array([[3.39e-06, 3.41e-06], - [-5.00e-07, -3.00e-07], - [0.00e+00, 1.00e-09], - [1.00e-07, 2.00e-06], - [5.00e-07, 1.50e-06], - [-5.00e-07, 0.00e+00], - [9.99e+01, 1.00e+02], - [5.00e-02, 2.00e-01], - [5.00e-02, 2.00e-01], - [2.00e-06, 2.10e-06], - [1.00e-02, 5.00e-02], - [0.00e+00, 0.00e+00]]) + problem.fitParams = np.array( + [ + 6.19503045e00, + 1.84420960e01, + 2.11039621e01, + 8.75538121e00, + 3.72292994e00, + 1.84624551e01, + 1.02316734e01, + 2.31156093e01, + 1.09906265e01, + 5.71005361e00, + 1.67933822e01, + 1.72009856e01, + 3.00260126e01, + 2.94448999e01, + 2.37113128e-06, + 1.99006694e-06, + 6.01489149e-06, + 1.59371685e-06, + ], + ) + problem.otherParams = np.array( + [ + 3.390e-06, + -4.010e-07, + 0.000e00, + 1.750e-06, + 1.470e-06, + -4.610e-07, + 1.000e02, + 1.000e-01, + 1.500e-01, + 2.073e-06, + 3.000e-02, + 0.000e00, + ], + ) + problem.fitLimits = np.array( + [ + [1.00e00, 1.00e01], + [5.00e00, 6.00e01], + [1.50e01, 3.50e01], + [1.00e00, 5.00e01], + [1.00e00, 1.50e01], + [1.00e01, 2.80e01], + [5.00e00, 1.70e01], + [1.00e01, 5.00e01], + [7.00e00, 1.70e01], + [2.00e00, 1.50e01], + [1.40e01, 2.20e01], + [1.00e01, 5.00e01], + [1.00e01, 5.00e01], + [0.00e00, 6.00e01], + [5.00e-10, 7.00e-06], + [1.00e-10, 4.99e-06], + [5.50e-06, 6.40e-06], + [1.00e-06, 4.99e-06], + ], + ) + problem.otherLimits = np.array( + [ + [3.39e-06, 3.41e-06], + [-5.00e-07, -3.00e-07], + [0.00e00, 1.00e-09], + [1.00e-07, 2.00e-06], + [5.00e-07, 1.50e-06], + [-5.00e-07, 0.00e00], + [9.99e01, 1.00e02], + [5.00e-02, 2.00e-01], + [5.00e-02, 2.00e-01], + [2.00e-06, 2.10e-06], + [1.00e-02, 5.00e-02], + [0.00e00, 0.00e00], + ], + ) return problem -@pytest.mark.parametrize(["test_procedure", "test_output_problem", "test_output_results", "test_bayes", "test_results"], - [ - (Procedures.Calculate, "reflectivity_calculation_problem", "reflectivity_calculation_output_results", None, - "reflectivity_calculation_results"), - (Procedures.Dream, "dream_problem", "dream_output_results", "dream_bayes", "dream_results"), -]) +@pytest.mark.parametrize( + ["test_procedure", "test_output_problem", "test_output_results", "test_bayes", "test_results"], + [ + ( + Procedures.Calculate, + "reflectivity_calculation_problem", + "reflectivity_calculation_output_results", + None, + "reflectivity_calculation_results", + ), + (Procedures.Dream, "dream_problem", "dream_output_results", "dream_bayes", "dream_results"), + ], +) def test_run(test_procedure, test_output_problem, test_output_results, test_bayes, test_results, request) -> None: input_project = request.getfixturevalue("input_project") test_output_problem = request.getfixturevalue(test_output_problem) @@ -258,9 +559,11 @@ def test_run(test_procedure, test_output_problem, test_output_results, test_baye test_results = request.getfixturevalue(test_results) - with mock.patch.object(RAT.rat_core, "RATMain", mock.MagicMock(return_value=(test_output_problem, - test_output_results, - test_bayes))): + with mock.patch.object( + RAT.rat_core, + "RATMain", + mock.MagicMock(return_value=(test_output_problem, test_output_results, test_bayes)), + ): # Use default project as we patch RATMain to give the desired outputs project, results = RAT.run(input_project, RAT.set_controls(procedure=test_procedure)) diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index 32502bb0..5d75efda 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -7,8 +7,9 @@ def test_matlab_wrapper() -> None: - with mock.patch.dict("sys.modules", {"matlab": mock.MagicMock(side_effect=ImportError)}), \ - pytest.raises(ImportError): + with mock.patch.dict("sys.modules", {"matlab": mock.MagicMock(side_effect=ImportError)}), pytest.raises( + ImportError, + ): RAT.wrappers.MatlabWrapper("demo.m") mocked_matlab_module = mock.MagicMock() mocked_engine = mock.MagicMock() @@ -16,8 +17,7 @@ def test_matlab_wrapper() -> None: # mocked_matlab_module.engine = mock.MagicMock() - with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, - "matlab.engine": mocked_matlab_module.engine}): + with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, "matlab.engine": mocked_matlab_module.engine}): wrapper = RAT.wrappers.MatlabWrapper("demo.m") assert wrapper.function_name == "demo" mocked_engine.cd.assert_called_once() diff --git a/tests/utils.py b/tests/utils.py index 294951fe..03833ee3 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -1,4 +1,3 @@ - import numpy as np import RAT.outputs @@ -21,7 +20,6 @@ class SubInputAttributes(InputAttributes): """Trivial subclass of InputAttributes.""" - def dummy_function() -> None: """Trivial function for function handle tests.""" @@ -34,18 +32,24 @@ def check_results_equal(actual_results, expected_results) -> None: """ list_fields = ["reflectivity", "simulation", "shiftedData"] double_list_fields = ["layerSlds", "sldProfiles", "resampledLayers"] - contrast_param_fields = ["backgroundParams", "scalefactors", "bulkIn", "bulkOut", "resolutionParams", "subRoughs", - "resample"] - - assert ((isinstance(actual_results, RAT.outputs.Results) and - isinstance(expected_results, RAT.outputs.Results)) or - (isinstance(actual_results, RAT.outputs.BayesResults) and - isinstance(expected_results, RAT.outputs.BayesResults))) + contrast_param_fields = [ + "backgroundParams", + "scalefactors", + "bulkIn", + "bulkOut", + "resolutionParams", + "subRoughs", + "resample", + ] + + assert (isinstance(actual_results, RAT.outputs.Results) and isinstance(expected_results, RAT.outputs.Results)) or ( + isinstance(actual_results, RAT.outputs.BayesResults) and isinstance(expected_results, RAT.outputs.BayesResults) + ) # The first set of fields are either 1D or 2D python lists containing numpy arrays. # Hence, we need to compare them element-wise. for list_field in list_fields: - for (a, b) in zip(getattr(actual_results, list_field), getattr(expected_results, list_field)): + for a, b in zip(getattr(actual_results, list_field), getattr(expected_results, list_field)): assert (a == b).all() for list_field in double_list_fields: @@ -53,7 +57,7 @@ def check_results_equal(actual_results, expected_results) -> None: expected_list = getattr(expected_results, list_field) assert len(actual_list) == len(expected_list) for i in range(len(actual_list)): - for (a, b) in zip(actual_list[i], expected_list[i]): + for a, b in zip(actual_list[i], expected_list[i]): assert (a == b).all() # Compare the final fields @@ -71,7 +75,6 @@ def check_results_equal(actual_results, expected_results) -> None: check_bayes_fields_equal(actual_results, expected_results) - def check_bayes_fields_equal(actual_results, expected_results) -> None: """Compare two instances of the "BayesResults" object for equality. @@ -80,35 +83,56 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: # The BayesResults object consists of a number of subclasses, each containing fields of differing formats. subclasses = ["predictionIntervals", "confidenceIntervals", "dreamParams", "dreamOutput", "nestedSamplerOutput"] - param_fields = {"predictionIntervals": [], - "confidenceIntervals": [], - "dreamParams": ["nParams", "nChains", "nGenerations", "parallel", "CPU", "jumpProbability", - "pUnitGamma", "nCR", "delta", "steps", "zeta", "outlier", "adaptPCR", "thinning", - "epsilon", "ABC", "IO", "storeOutput"], - "dreamOutput": ["runtime", "iteration", "modelOutput"], - "nestedSamplerOutput": ["logZ"], - } - - list_fields = {"predictionIntervals": ["reflectivity", "reflectivityXData"], - "confidenceIntervals": [], - "dreamParams": [], - "dreamOutput": [], - "nestedSamplerOutput": [], - } - - double_list_fields = {"predictionIntervals": ["sld", "sldXData"], - "confidenceIntervals": [], - "dreamParams": [], - "dreamOutput": [], - "nestedSamplerOutput": [], - } - - array_fields = {"predictionIntervals": ["sampleChi"], - "confidenceIntervals": ["percentile65", "percentile95", "mean"], - "dreamParams": ["R"], - "dreamOutput": ["allChains", "outlierChains", "AR", "R_stat", "CR"], - "nestedSamplerOutput": ["nestSamples", "postSamples"], - } + param_fields = { + "predictionIntervals": [], + "confidenceIntervals": [], + "dreamParams": [ + "nParams", + "nChains", + "nGenerations", + "parallel", + "CPU", + "jumpProbability", + "pUnitGamma", + "nCR", + "delta", + "steps", + "zeta", + "outlier", + "adaptPCR", + "thinning", + "epsilon", + "ABC", + "IO", + "storeOutput", + ], + "dreamOutput": ["runtime", "iteration", "modelOutput"], + "nestedSamplerOutput": ["logZ"], + } + + list_fields = { + "predictionIntervals": ["reflectivity", "reflectivityXData"], + "confidenceIntervals": [], + "dreamParams": [], + "dreamOutput": [], + "nestedSamplerOutput": [], + } + + double_list_fields = { + "predictionIntervals": ["sld", "sldXData"], + "confidenceIntervals": [], + "dreamParams": [], + "dreamOutput": [], + "nestedSamplerOutput": [], + } + + array_fields = { + "predictionIntervals": ["sampleChi"], + "confidenceIntervals": ["percentile65", "percentile95", "mean"], + "dreamParams": ["R"], + "dreamOutput": ["allChains", "outlierChains", "AR", "R_stat", "CR"], + "nestedSamplerOutput": ["nestSamples", "postSamples"], + } for subclass in subclasses: actual_subclass = getattr(actual_results, subclass) @@ -118,7 +142,7 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: assert getattr(actual_subclass, field) == getattr(expected_subclass, field) for field in list_fields[subclass]: - for (a, b) in zip(getattr(actual_subclass, field), getattr(expected_subclass, field)): + for a, b in zip(getattr(actual_subclass, field), getattr(expected_subclass, field)): assert (a == b).all() for field in double_list_fields[subclass]: @@ -126,7 +150,7 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: expected_list = getattr(expected_subclass, field) assert len(actual_list) == len(expected_list) for i in range(len(actual_list)): - for (a, b) in zip(actual_list[i], expected_list[i]): + for a, b in zip(actual_list[i], expected_list[i]): assert (a == b).all() # Need to account for the arrays that are initialised as "NaN" in the compiled code @@ -134,9 +158,9 @@ def check_bayes_fields_equal(actual_results, expected_results) -> None: actual_array = getattr(actual_subclass, array) expected_array = getattr(expected_subclass, array) for i in range(len(actual_array)): - assert ((actual_array == expected_array).all() or - (["NaN" if np.isnan(el) else el for el in actual_array[i]] == - ["NaN" if np.isnan(el) else el for el in expected_array[i]])) + assert (actual_array == expected_array).all() or ( + ["NaN" if np.isnan(el) else el for el in actual_array[i]] + == ["NaN" if np.isnan(el) else el for el in expected_array[i]] + ) assert (actual_results.chain == expected_results.chain).all() - From 16c17e14947b97aba18e11bd4534bcc980eb6507 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 26 Jun 2024 12:10:57 +0100 Subject: [PATCH 07/11] Finalises rule selection and tidies up code --- .gitignore | 3 ++ RAT/classlist.py | 4 ++- RAT/models.py | 4 ++- RAT/utils/custom_errors.py | 4 ++- pyproject.toml | 4 +-- setup.py | 2 +- tests/test_custom_errors.py | 10 +++---- tests/test_inputs.py | 4 ++- tests/test_models.py | 4 ++- tests/test_plotting.py | 7 +++-- tests/test_project.py | 59 ++++++++++++++++++++++++++----------- tests/test_wrappers.py | 2 -- 12 files changed, 72 insertions(+), 35 deletions(-) diff --git a/.gitignore b/.gitignore index 1f0386ae..5cab62d3 100644 --- a/.gitignore +++ b/.gitignore @@ -28,3 +28,6 @@ docs/*.inv build/* dist/* *.whl + +# Local pre-commit hooks +.pre-commit-config.yaml diff --git a/RAT/classlist.py b/RAT/classlist.py index 325cf250..7ec821dc 100644 --- a/RAT/classlist.py +++ b/RAT/classlist.py @@ -1,4 +1,6 @@ -"""The classlist module. Contains the ClassList class, which defines a list containing instances of a particular class.""" +"""The classlist module. Contains the ClassList class, which defines a list containing instances of a particular +class. +""" import collections import contextlib diff --git a/RAT/models.py b/RAT/models.py index 4caed0ea..2ec9aa21 100644 --- a/RAT/models.py +++ b/RAT/models.py @@ -105,7 +105,9 @@ def model_post_init(self, __context: Any) -> None: @model_validator(mode="after") def set_matlab_function_name(self): - """If we have a matlab custom function, the "function_name" should be set to the filename without the extension.""" + """If we have a matlab custom function, the "function_name" should be set to the filename without the + extension. + """ if self.language == Languages.Matlab and self.function_name != pathlib.Path(self.filename).stem: self.function_name = pathlib.Path(self.filename).stem diff --git a/RAT/utils/custom_errors.py b/RAT/utils/custom_errors.py index 2dabb011..1778841a 100644 --- a/RAT/utils/custom_errors.py +++ b/RAT/utils/custom_errors.py @@ -1,11 +1,13 @@ """Defines routines for custom error handling in RAT.""" +from typing import Optional + import pydantic_core def custom_pydantic_validation_error( error_list: list[pydantic_core.ErrorDetails], - custom_error_msgs: dict[str, str] = None, + custom_error_msgs: Optional[dict[str, str]] = None, ) -> list[pydantic_core.ErrorDetails]: """Run through the list of errors generated from a pydantic ValidationError, substituting the standard error for a PydanticCustomError for a given set of error types. diff --git a/pyproject.toml b/pyproject.toml index a00ed151..4216b6e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -10,8 +10,8 @@ build-backend = 'setuptools.build_meta' line-length = 120 [tool.ruff.lint] -select = ["E", "F", "UP", "B", "SIM", "I", "COM"] -ignore = ["SIM108", "S101", "COM812"] +select = ["E", "F", "UP", "B", "SIM", "I"] +ignore = ["SIM108"] [tool.ruff.lint.flake8-pytest-style] fixture-parentheses = false diff --git a/setup.py b/setup.py index 48f7f55e..fe5a222b 100644 --- a/setup.py +++ b/setup.py @@ -153,7 +153,7 @@ def build_libraries(self, libraries): author_email="", url="https://github.com/RascalSoftware/python-RAT", description="Python extension for the Reflectivity Analysis Toolbox (RAT)", - long_description=open("README.md").read(), + long_description=open("README.md").read(), # noqa: SIM115 long_description_content_type="text/markdown", packages=find_packages(), include_package_data=True, diff --git a/tests/test_custom_errors.py b/tests/test_custom_errors.py index 57c1f6fc..a96cbf37 100644 --- a/tests/test_custom_errors.py +++ b/tests/test_custom_errors.py @@ -20,15 +20,15 @@ def TestModel(): [ ( {}, - "2 validation errors for TestModel\nint_field\n Input should be a valid integer, unable to parse string as an " - "integer [type=int_parsing, input_value='string', input_type=str]\nstr_field\n Input should be a valid string " - "[type=string_type, input_value=5, input_type=int]", + "2 validation errors for TestModel\nint_field\n Input should be a valid integer, unable to parse string " + "as an integer [type=int_parsing, input_value='string', input_type=str]\nstr_field\n Input should be a " + "valid string [type=string_type, input_value=5, input_type=int]", ), ( {"int_parsing": "This is a custom error message", "string_type": "This is another custom error message"}, "2 validation errors for TestModel\nint_field\n This is a custom error message [type=int_parsing, " - "input_value='string', input_type=str]\nstr_field\n This is another custom error message [type=string_type, " - "input_value=5, input_type=int]", + "input_value='string', input_type=str]\nstr_field\n This is another custom error message " + "[type=string_type, input_value=5, input_type=int]", ), ], ) diff --git a/tests/test_inputs.py b/tests/test_inputs.py index f14d6710..286a84f3 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -507,7 +507,9 @@ def standard_layers_controls(): @pytest.fixture def custom_xy_controls(): - """The expected controls object for input to the compiled RAT code given the default inputs and "custom_xy_project".""" + """The expected controls object for input to the compiled RAT code given the default inputs and + "custom_xy_project". + """ controls = Control() controls.procedure = Procedures.Calculate controls.parallel = Parallel.Single diff --git a/tests/test_models.py b/tests/test_models.py index 2a13efba..657868f8 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -125,7 +125,9 @@ def test_data_dimension(input_data: np.ndarray[float]) -> None: ], ) def test_data_too_few_dimensions(input_data: np.ndarray[float]) -> None: - """If the "data" field of the "Data" model is not a two-dimensional numpy array we should raise a ValidationError.""" + """If the "data" field of the "Data" model is not a two-dimensional numpy array we should raise a + ValidationError. + """ with pytest.raises( pydantic.ValidationError, match='1 validation error for Data\ndata\n Value error, "data" must ' "have at least two dimensions", diff --git a/tests/test_plotting.py b/tests/test_plotting.py index 7b765e8e..95e03ffd 100644 --- a/tests/test_plotting.py +++ b/tests/test_plotting.py @@ -89,19 +89,22 @@ def test_eventhandlers_linked_to_figure(fig: Figure) -> None: class methods. """ pattern = r"\(([^\]]+)\)" + index = 0 for ix, val in fig._fig.canvas.callbacks.callbacks["close_event"].items(): if str(type(val)) == "": + index = ix break - canvas_close_event_callback = fig._fig.canvas.callbacks.callbacks["close_event"][ix]._func_ref.__repr__() + canvas_close_event_callback = fig._fig.canvas.callbacks.callbacks["close_event"][index]._func_ref.__repr__() close_event_callback = re.findall(pattern, canvas_close_event_callback)[0] assert close_event_callback == "_close" assert hasattr(Figure, "_close") for ix, val in fig._fig.canvas.callbacks.callbacks["key_press_event"].items(): if str(type(val)) == "": + index = ix break - canvas_key_press_event_callback = fig._fig.canvas.callbacks.callbacks["key_press_event"][ix]._func_ref.__repr__() + canvas_key_press_event_callback = fig._fig.canvas.callbacks.callbacks["key_press_event"][index]._func_ref.__repr__() key_press_event_callback = re.findall(pattern, canvas_key_press_event_callback)[0] assert key_press_event_callback == "_process_button_press" assert hasattr(Figure, "_process_button_press") diff --git a/tests/test_project.py b/tests/test_project.py index e0177788..1afaba82 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -123,22 +123,41 @@ def test_project_script(): return ( '# THIS FILE IS GENERATED FROM RAT VIA THE "WRITE_SCRIPT" ROUTINE. IT IS NOT PART OF THE RAT CODE.\n\n' "import RAT\nfrom RAT.models import *\nfrom numpy import array, inf\n\n" - "problem = RAT.Project(\n name='', calculation='non polarised', model='standard layers', geometry='air/substrate', absorption=False,\n" - " parameters=RAT.ClassList([ProtectedParameter(name='Substrate Roughness', min=1.0, value=3.0, max=5.0, fit=True, prior_type='uniform', mu=0.0, sigma=inf)," - " Parameter(name='Test Thickness', min=0.0, value=0.0, max=0.0, fit=False, prior_type='uniform', mu=0.0, sigma=inf)," + "problem = RAT.Project(\n" + " name='', calculation='non polarised', model='standard layers', geometry='air/substrate'," + " absorption=False,\n" + " parameters=RAT.ClassList(" + "[ProtectedParameter(name='Substrate Roughness', min=1.0, value=3.0, max=5.0, fit=True, prior_type='uniform'," + " mu=0.0, sigma=inf)," + " Parameter(name='Test Thickness', min=0.0, value=0.0, max=0.0, fit=False, prior_type='uniform', mu=0.0," + " sigma=inf)," " Parameter(name='Test SLD', min=0.0, value=0.0, max=0.0, fit=False, prior_type='uniform', mu=0.0, sigma=inf)," - " Parameter(name='Test Roughness', min=0.0, value=0.0, max=0.0, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" - " background_parameters=RAT.ClassList([Parameter(name='Background Param 1', min=1e-07, value=1e-06, max=1e-05, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" - " scalefactors=RAT.ClassList([Parameter(name='Scalefactor 1', min=0.02, value=0.23, max=0.25, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" - " bulk_in=RAT.ClassList([Parameter(name='SLD Air', min=0.0, value=0.0, max=0.0, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" - " bulk_out=RAT.ClassList([Parameter(name='SLD D2O', min=6.2e-06, value=6.35e-06, max=6.35e-06, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" - " resolution_parameters=RAT.ClassList([Parameter(name='Resolution Param 1', min=0.01, value=0.03, max=0.05, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" - " backgrounds=RAT.ClassList([Background(name='Background 1', type='constant', value_1='Background Param 1', value_2='', value_3='', value_4='', value_5='')]),\n" - " resolutions=RAT.ClassList([Resolution(name='Resolution 1', type='constant', value_1='Resolution Param 1', value_2='', value_3='', value_4='', value_5='')]),\n" - " custom_files=RAT.ClassList([CustomFile(name='Test Custom File', filename='', function_name='', language='python', path='')]),\n" - " data=RAT.ClassList([Data(name='Simulation', data=array([[1., 1., 1.]]), data_range=[1.0, 1.0], simulation_range=[1.0, 1.0])]),\n" - " layers=RAT.ClassList([Layer(name='Test Layer', thickness='Test Thickness', SLD='Test SLD', roughness='Test Roughness', hydration='', hydrate_with='bulk out')]),\n" - " contrasts=RAT.ClassList([Contrast(name='Test Contrast', data='Simulation', background='Background 1', background_action='add', bulk_in='SLD Air', bulk_out='SLD D2O', scalefactor='Scalefactor 1', resolution='Resolution 1', resample=False, model=['Test Layer'])]),\n" + " Parameter(name='Test Roughness', min=0.0, value=0.0, max=0.0, fit=False, prior_type='uniform', mu=0.0," + " sigma=inf)" + "]),\n" + " background_parameters=RAT.ClassList([Parameter(name='Background Param 1', min=1e-07, value=1e-06," + " max=1e-05, fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" + " scalefactors=RAT.ClassList([Parameter(name='Scalefactor 1', min=0.02, value=0.23, max=0.25, fit=False," + " prior_type='uniform', mu=0.0, sigma=inf)]),\n" + " bulk_in=RAT.ClassList([Parameter(name='SLD Air', min=0.0, value=0.0, max=0.0, fit=False," + " prior_type='uniform', mu=0.0, sigma=inf)]),\n" + " bulk_out=RAT.ClassList([Parameter(name='SLD D2O', min=6.2e-06, value=6.35e-06, max=6.35e-06, fit=False," + " prior_type='uniform', mu=0.0, sigma=inf)]),\n" + " resolution_parameters=RAT.ClassList([Parameter(name='Resolution Param 1', min=0.01, value=0.03, max=0.05," + " fit=False, prior_type='uniform', mu=0.0, sigma=inf)]),\n" + " backgrounds=RAT.ClassList([Background(name='Background 1', type='constant', value_1='Background Param 1'," + " value_2='', value_3='', value_4='', value_5='')]),\n" + " resolutions=RAT.ClassList([Resolution(name='Resolution 1', type='constant', value_1='Resolution Param 1'," + " value_2='', value_3='', value_4='', value_5='')]),\n" + " custom_files=RAT.ClassList([CustomFile(name='Test Custom File', filename='', function_name=''," + " language='python', path='')]),\n" + " data=RAT.ClassList([Data(name='Simulation', data=array([[1., 1., 1.]]), data_range=[1.0, 1.0]," + " simulation_range=[1.0, 1.0])]),\n" + " layers=RAT.ClassList([Layer(name='Test Layer', thickness='Test Thickness', SLD='Test SLD'," + " roughness='Test Roughness', hydration='', hydrate_with='bulk out')]),\n" + " contrasts=RAT.ClassList([Contrast(name='Test Contrast', data='Simulation', background='Background 1'," + " background_action='add', bulk_in='SLD Air', bulk_out='SLD D2O', scalefactor='Scalefactor 1'," + " resolution='Resolution 1', resample=False, model=['Test Layer'])]),\n" " )\n" ) @@ -461,12 +480,14 @@ def test_set_contrast_model_field( ( LayerModels.StandardLayers, ["Test Domain Ratio"], - 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.', + 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two ' + "values.", ), ( LayerModels.StandardLayers, ["Test Domain Ratio", "Test Domain Ratio", "Test Domain Ratio"], - 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two values.', + 'For a standard layers domains calculation the "model" field of "contrasts" must contain exactly two ' + "values.", ), ( LayerModels.CustomLayers, @@ -894,7 +915,9 @@ def test_check_allowed_values_not_on_list(test_value: str) -> None: ], ) def test_check_contrast_model_allowed_values(test_values: list[str]) -> None: - """We should not raise an error if values are defined in a non-empty list and all are on the list of allowed values.""" + """We should not raise an error if values are defined in a non-empty list and all are on the list of allowed + values. + """ project = RAT.Project.model_construct( contrasts=RAT.ClassList(RAT.models.Contrast(name="Test Contrast", model=test_values)), ) diff --git a/tests/test_wrappers.py b/tests/test_wrappers.py index 5d75efda..aa135c34 100644 --- a/tests/test_wrappers.py +++ b/tests/test_wrappers.py @@ -15,8 +15,6 @@ def test_matlab_wrapper() -> None: mocked_engine = mock.MagicMock() mocked_matlab_module.engine.start_matlab.return_value = mocked_engine - # mocked_matlab_module.engine = mock.MagicMock() - with mock.patch.dict("sys.modules", {"matlab": mocked_matlab_module, "matlab.engine": mocked_matlab_module.engine}): wrapper = RAT.wrappers.MatlabWrapper("demo.m") assert wrapper.function_name == "demo" From b6c5f18867ecaa586bd03cf09c5c257920d34fd0 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:07:53 +0100 Subject: [PATCH 08/11] Adds "requirements-dev.txt" --- requirements-dev.txt | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 requirements-dev.txt diff --git a/requirements-dev.txt b/requirements-dev.txt new file mode 100644 index 00000000..e69de29b From bf30c3cd1bbae187f2d198558f24b9d73696398c Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 26 Jun 2024 14:08:25 +0100 Subject: [PATCH 09/11] Adds "requirements-dev.txt" --- requirements-dev.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/requirements-dev.txt b/requirements-dev.txt index e69de29b..4899358b 100644 --- a/requirements-dev.txt +++ b/requirements-dev.txt @@ -0,0 +1 @@ +ruff >= 0.4.10 From f2d69daeda9462492456150e6c15623e9bb26540 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 26 Jun 2024 15:18:00 +0100 Subject: [PATCH 10/11] Adds new github action for linter and formatter --- .github/workflows/check_linting_and_format.yml | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 .github/workflows/check_linting_and_format.yml diff --git a/.github/workflows/check_linting_and_format.yml b/.github/workflows/check_linting_and_format.yml new file mode 100644 index 00000000..41b9e1ac --- /dev/null +++ b/.github/workflows/check_linting_and_format.yml @@ -0,0 +1,18 @@ +name: Ruff + +on: + push: + branches: [ main ] + pull_request: + branches: [ main ] + +jobs: + ruff: + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - uses: chartboost/ruff-action@v1 + - uses: chartboost/ruff-action@v1 + with: + args: 'format --check' + \ No newline at end of file From ff3f1fcf2caeca91b024c6ccd407eff9c1f2e330 Mon Sep 17 00:00:00 2001 From: Paul Sharp <44529197+DrPaulSharp@users.noreply.github.com> Date: Wed, 26 Jun 2024 17:57:19 +0100 Subject: [PATCH 11/11] Addresses review comments --- .../workflows/{check_linting_and_format.yml => run_ruff.yml} | 0 setup.py | 4 ++-- 2 files changed, 2 insertions(+), 2 deletions(-) rename .github/workflows/{check_linting_and_format.yml => run_ruff.yml} (100%) diff --git a/.github/workflows/check_linting_and_format.yml b/.github/workflows/run_ruff.yml similarity index 100% rename from .github/workflows/check_linting_and_format.yml rename to .github/workflows/run_ruff.yml diff --git a/setup.py b/setup.py index fe5a222b..3eedf681 100644 --- a/setup.py +++ b/setup.py @@ -165,11 +165,11 @@ def build_libraries(self, libraries): install_requires=["numpy >= 1.20", "prettytable >= 3.9.0", "pydantic >= 2.7.2", "matplotlib >= 3.8.3"], extras_require={ ':python_version < "3.11"': ["StrEnum >= 0.4.15"], - "Dev": ["pytest>=7.4.0", "pytest-cov>=4.1.0"], + "Dev": ["pytest>=7.4.0", "pytest-cov>=4.1.0", "ruff>=0.4.10"], "Matlab_latest": ["matlabengine"], "Matlab_2023b": ["matlabengine == 23.2.1"], "Matlab_2023a": ["matlabengine == 9.14.3"], - "Matlab-2022b": ["matlabengine == 9.13.9"], + "Matlab_2022b": ["matlabengine == 9.13.9"], "Matlab_2022a": ["matlabengine == 9.12.19"], "Matlab_2021b": ["matlabengine == 9.11.21"], "Matlab_2021a": ["matlabengine == 9.10.3"],