From eb2fee9aa36a19435f7c728166e139d43da06261 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Mon, 9 Dec 2024 09:40:03 +0000 Subject: [PATCH 01/15] refactored backgrounds and resolutions to use source parameter --- RATapi/examples/absorption/absorption.ipynb | 8 +- RATapi/examples/absorption/absorption.py | 8 +- RATapi/examples/languages/setup_problem.py | 6 +- .../examples/non_polarised/DSPC_custom_XY.py | 6 +- .../non_polarised/DSPC_custom_layers.ipynb | 6 +- .../non_polarised/DSPC_custom_layers.py | 6 +- .../non_polarised/DSPC_custom_xy.ipynb | 6 +- .../non_polarised/DSPC_standard_layers.ipynb | 4 +- .../non_polarised/DSPC_standard_layers.py | 4 +- RATapi/inputs.py | 4 +- RATapi/models.py | 286 ++++++++++++++---- RATapi/project.py | 95 +++--- RATapi/utils/convert.py | 20 +- tests/conftest.py | 52 +--- tests/test_convert.py | 8 +- tests/test_project.py | 233 +++++++------- 16 files changed, 439 insertions(+), 313 deletions(-) diff --git a/RATapi/examples/absorption/absorption.ipynb b/RATapi/examples/absorption/absorption.ipynb index 9041fd0b..10f3cda0 100644 --- a/RATapi/examples/absorption/absorption.ipynb +++ b/RATapi/examples/absorption/absorption.ipynb @@ -140,10 +140,10 @@ "problem.background_parameters.append(name=\"Background parameter 3\", min=1.0e-06, value=9.01e-06, max=9.0e-05, fit=True)\n", "problem.background_parameters.append(name=\"Background parameter 4\", min=1.0e-06, value=5.61e-06, max=9.0e-05, fit=True)\n", "\n", - "problem.backgrounds.append(name=\"Background 1\", type=\"constant\", value_1=\"Background parameter 1\")\n", - "problem.backgrounds.append(name=\"Background 2\", type=\"constant\", value_1=\"Background parameter 2\")\n", - "problem.backgrounds.append(name=\"Background 3\", type=\"constant\", value_1=\"Background parameter 3\")\n", - "problem.backgrounds.append(name=\"Background 4\", type=\"constant\", value_1=\"Background parameter 4\")\n", + "problem.backgrounds.append(name=\"Background 1\", type=\"constant\", source=\"Background parameter 1\")\n", + "problem.backgrounds.append(name=\"Background 2\", type=\"constant\", source=\"Background parameter 2\")\n", + "problem.backgrounds.append(name=\"Background 3\", type=\"constant\", source=\"Background parameter 3\")\n", + "problem.backgrounds.append(name=\"Background 4\", type=\"constant\", source=\"Background parameter 4\")\n", "\n", "# Make the resolution fittable\n", "problem.resolution_parameters.set_fields(0, fit=True)\n" diff --git a/RATapi/examples/absorption/absorption.py b/RATapi/examples/absorption/absorption.py index 4b59ff88..ce7407d4 100644 --- a/RATapi/examples/absorption/absorption.py +++ b/RATapi/examples/absorption/absorption.py @@ -68,10 +68,10 @@ def absorption(): name="Background parameter 4", min=1.0e-06, value=5.61e-06, max=9.0e-05, fit=True ) - problem.backgrounds.append(name="Background 1", type="constant", value_1="Background parameter 1") - problem.backgrounds.append(name="Background 2", type="constant", value_1="Background parameter 2") - problem.backgrounds.append(name="Background 3", type="constant", value_1="Background parameter 3") - problem.backgrounds.append(name="Background 4", type="constant", value_1="Background parameter 4") + problem.backgrounds.append(name="Background 1", type="constant", source="Background parameter 1") + problem.backgrounds.append(name="Background 2", type="constant", source="Background parameter 2") + problem.backgrounds.append(name="Background 3", type="constant", source="Background parameter 3") + problem.backgrounds.append(name="Background 4", type="constant", source="Background parameter 4") # Make the resolution fittable problem.resolution_parameters.set_fields(0, fit=True) diff --git a/RATapi/examples/languages/setup_problem.py b/RATapi/examples/languages/setup_problem.py index c6808119..979c5ed1 100644 --- a/RATapi/examples/languages/setup_problem.py +++ b/RATapi/examples/languages/setup_problem.py @@ -82,11 +82,11 @@ def make_example_problem(): ) # And add the two new constant backgrounds - problem.backgrounds.append(name="Background SMW", type="constant", value_1="Background parameter SMW") - problem.backgrounds.append(name="Background H2O", type="constant", value_1="Background parameter H2O") + problem.backgrounds.append(name="Background SMW", type="constant", source="Background parameter SMW") + problem.backgrounds.append(name="Background H2O", type="constant", source="Background parameter H2O") # And edit the other one - problem.backgrounds.set_fields(0, name="Background D2O", value_1="Background parameter D2O") + problem.backgrounds.set_fields(0, name="Background D2O", source="Background parameter D2O") # Finally modify some of the other parameters to be more suitable values for a solid / liquid experiment problem.scalefactors.set_fields(0, value=1.0, min=0.5, max=2.0, fit=True) diff --git a/RATapi/examples/non_polarised/DSPC_custom_XY.py b/RATapi/examples/non_polarised/DSPC_custom_XY.py index 567c765b..647f302b 100644 --- a/RATapi/examples/non_polarised/DSPC_custom_XY.py +++ b/RATapi/examples/non_polarised/DSPC_custom_XY.py @@ -90,11 +90,11 @@ def DSPC_custom_XY(): problem.background_parameters.append(name="Background parameter H2O", min=0.0, 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") - problem.backgrounds.append(name="Background H2O", type="constant", value_1="Background parameter H2O") + problem.backgrounds.append(name="Background SMW", type="constant", source="Background parameter SMW") + problem.backgrounds.append(name="Background H2O", type="constant", source="Background parameter H2O") # And edit the other one - problem.backgrounds.set_fields(0, name="Background D2O", value_1="Background parameter D2O") + problem.backgrounds.set_fields(0, name="Background D2O", source="Background parameter D2O") # Finally modify some of the other parameters to be more suitable values for a solid / liquid experiment problem.scalefactors.set_fields(0, value=1.0, min=0.5, max=2.0, fit=True) diff --git a/RATapi/examples/non_polarised/DSPC_custom_layers.ipynb b/RATapi/examples/non_polarised/DSPC_custom_layers.ipynb index e4ac99ad..0adac7eb 100644 --- a/RATapi/examples/non_polarised/DSPC_custom_layers.ipynb +++ b/RATapi/examples/non_polarised/DSPC_custom_layers.ipynb @@ -534,11 +534,11 @@ "problem.background_parameters.append(name=\"Background parameter H2O\", min=1.0e-10, value=1.0e-7, max=1.0e-5, fit=True)\n", "\n", "# And add the two new constant backgrounds\n", - "problem.backgrounds.append(name=\"Background SMW\", type=\"constant\", value_1=\"Background parameter SMW\")\n", - "problem.backgrounds.append(name=\"Background H2O\", type=\"constant\", value_1=\"Background parameter H2O\")\n", + "problem.backgrounds.append(name=\"Background SMW\", type=\"constant\", source=\"Background parameter SMW\")\n", + "problem.backgrounds.append(name=\"Background H2O\", type=\"constant\", source=\"Background parameter H2O\")\n", "\n", "# And edit the other one\n", - "problem.backgrounds.set_fields(0, name=\"Background D2O\", value_1=\"Background parameter D2O\")\n", + "problem.backgrounds.set_fields(0, name=\"Background D2O\", source=\"Background parameter D2O\")\n", "\n", "# Finally modify some of the other parameters to be more suitable values for a solid / liquid experiment\n", "problem.scalefactors.set_fields(0, value=1.0, min=0.5, max=2.0, fit=True)" diff --git a/RATapi/examples/non_polarised/DSPC_custom_layers.py b/RATapi/examples/non_polarised/DSPC_custom_layers.py index ddbac6fb..508653fb 100644 --- a/RATapi/examples/non_polarised/DSPC_custom_layers.py +++ b/RATapi/examples/non_polarised/DSPC_custom_layers.py @@ -74,11 +74,11 @@ def DSPC_custom_layers(): ) # And add the two new constant backgrounds - problem.backgrounds.append(name="Background SMW", type="constant", value_1="Background parameter SMW") - problem.backgrounds.append(name="Background H2O", type="constant", value_1="Background parameter H2O") + problem.backgrounds.append(name="Background SMW", type="constant", source="Background parameter SMW") + problem.backgrounds.append(name="Background H2O", type="constant", source="Background parameter H2O") # And edit the other one - problem.backgrounds.set_fields(0, name="Background D2O", value_1="Background parameter D2O") + problem.backgrounds.set_fields(0, name="Background D2O", source="Background parameter D2O") # Finally modify some of the other parameters to be more suitable values for a solid / liquid experiment problem.scalefactors.set_fields(0, value=1.0, min=0.5, max=2.0, fit=True) diff --git a/RATapi/examples/non_polarised/DSPC_custom_xy.ipynb b/RATapi/examples/non_polarised/DSPC_custom_xy.ipynb index ae57d6e2..8925865c 100644 --- a/RATapi/examples/non_polarised/DSPC_custom_xy.ipynb +++ b/RATapi/examples/non_polarised/DSPC_custom_xy.ipynb @@ -175,11 +175,11 @@ "problem.background_parameters.append(name=\"Background parameter H2O\", min=0.0, value=1.0e-7, max=1.0e-5, fit=True)\n", "\n", "# And add the two new constant backgrounds\n", - "problem.backgrounds.append(name=\"Background SMW\", type=\"constant\", value_1=\"Background parameter SMW\")\n", - "problem.backgrounds.append(name=\"Background H2O\", type=\"constant\", value_1=\"Background parameter H2O\")\n", + "problem.backgrounds.append(name=\"Background SMW\", type=\"constant\", source=\"Background parameter SMW\")\n", + "problem.backgrounds.append(name=\"Background H2O\", type=\"constant\", source=\"Background parameter H2O\")\n", "\n", "# And edit the other one\n", - "problem.backgrounds.set_fields(0, name=\"Background D2O\", value_1=\"Background parameter D2O\")\n", + "problem.backgrounds.set_fields(0, name=\"Background D2O\", source=\"Background parameter D2O\")\n", "\n", "# Finally modify some of the other parameters to be more suitable values for a solid / liquid experiment\n", "problem.scalefactors.set_fields(0, value=1.0, min=0.5, max=2.0, fit=True)\n", diff --git a/RATapi/examples/non_polarised/DSPC_standard_layers.ipynb b/RATapi/examples/non_polarised/DSPC_standard_layers.ipynb index 3269efe1..050b7497 100644 --- a/RATapi/examples/non_polarised/DSPC_standard_layers.ipynb +++ b/RATapi/examples/non_polarised/DSPC_standard_layers.ipynb @@ -184,8 +184,8 @@ "problem.background_parameters.append(name=\"Background parameter D2O\", min=5.0e-10, value=2.23e-06, max=7.0e-06, fit=True)\n", "problem.background_parameters.append(name=\"Background parameter SMW\", min=1.0e-10, value=3.38e-06, max=4.99e-06, fit=True)\n", "\n", - "problem.backgrounds.append(name=\"D2O Background\", type=\"constant\", value_1=\"Background parameter D2O\")\n", - "problem.backgrounds.append(name=\"SMW Background\", type=\"constant\", value_1=\"Background parameter SMW\")" + "problem.backgrounds.append(name=\"D2O Background\", type=\"constant\", source=\"Background parameter D2O\")\n", + "problem.backgrounds.append(name=\"SMW Background\", type=\"constant\", source=\"Background parameter SMW\")" ] }, { diff --git a/RATapi/examples/non_polarised/DSPC_standard_layers.py b/RATapi/examples/non_polarised/DSPC_standard_layers.py index 8ae59fdd..0d0d1181 100644 --- a/RATapi/examples/non_polarised/DSPC_standard_layers.py +++ b/RATapi/examples/non_polarised/DSPC_standard_layers.py @@ -147,8 +147,8 @@ def DSPC_standard_layers(): 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") + problem.backgrounds.append(name="D2O Background", type="constant", source="Background parameter D2O") + problem.backgrounds.append(name="SMW Background", type="constant", source="Background parameter SMW") # Now add the data data_path = pathlib.Path(__file__).parents[1] / "data" diff --git a/RATapi/inputs.py b/RATapi/inputs.py index 304f5fde..670d5f92 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -235,7 +235,7 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: if background.type == TypeOptions.Data: contrast_background_params.append([-1]) else: - contrast_background_params.append([project.background_parameters.index(background.value_1, True)]) + contrast_background_params.append([project.background_parameters.index(background.source, True)]) # Set resolution parameters, with -1 used to indicate a data resolution all_data = [] @@ -248,7 +248,7 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: if resolution.type == TypeOptions.Data: contrast_resolution_params.append(-1) else: - contrast_resolution_params.append(project.resolution_parameters.index(resolution.value_1, True)) + contrast_resolution_params.append(project.resolution_parameters.index(resolution.source, True)) data_index = project.data.index(contrast.data) data = project.data[data_index].data diff --git a/RATapi/models.py b/RATapi/models.py index 3c76840b..c055f536 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -1,6 +1,7 @@ """The models module. Contains the pydantic models used by RAT to store project parameters.""" import pathlib +from itertools import count from typing import Any import numpy as np @@ -15,23 +16,15 @@ from strenum import StrEnum -def int_sequence(): - """Iterate through integers for use as model counters.""" - num = 1 - while True: - yield str(num) - num += 1 - - # Create a counter for each model -background_number = int_sequence() -contrast_number = int_sequence() -custom_file_number = int_sequence() -data_number = int_sequence() -domain_contrast_number = int_sequence() -layer_number = int_sequence() -parameter_number = int_sequence() -resolution_number = int_sequence() +background_number = count(1) +contrast_number = count(1) +custom_file_number = count(1) +data_number = count(1) +domain_contrast_number = count(1) +layer_number = count(1) +parameter_number = count(1) +resolution_number = count(1) class RATModel(BaseModel, validate_assignment=True, extra="forbid"): @@ -52,35 +45,70 @@ def __str__(self): class Background(RATModel): - """Defines the Backgrounds in RAT.""" - - name: str = Field(default_factory=lambda: "New Background " + next(background_number), min_length=1) + """A background signal. + + Parameters + ---------- + name : str + The name of the background. + type : TypeOptions + The type of background (constant, function or data) + source : str + The source data for the background; + - if type is 'constant', this should be the name of a background parameter. + - if type is 'data', this should be the name of a dataset defined in `Project.data`. + - if type is 'function', this should be the name of a custom function defined in `Project.custom_files`. + value_1, value_2, ..., value_5 : str + Values required by the background. + - if type is 'constant', this should be blank. + - if type is 'data', value_1 may be the parameter name for an optional offset. Other values are ignored. + - if type is 'function', these values may be the names of up to 5 parameters which are passed to the function. + + """ + + name: str = Field(default_factory=lambda: f"New Background {next(background_number)}", min_length=1) type: TypeOptions = TypeOptions.Constant + source: str = "" value_1: str = "" value_2: str = "" value_3: str = "" value_4: str = "" value_5: str = "" - @field_validator("type") - @classmethod - def validate_unimplemented_backgrounds(cls, type: TypeOptions): - """Raise an error if currently unsupported Data or Function backgrounds are used.""" - # FIXME: once data/function backgrounds have been implemented, - # please remember to remove the @pytest.mark.skip decorators used to skip the tests: - # - tests/test_project.py::test_check_allowed_background_resolution_values_data - # - tests/test_project.py::test_check_allowed_background_resolution_values_on_data_list - if type == TypeOptions.Data: - raise NotImplementedError("Data backgrounds are not yet supported.") - if type == TypeOptions.Function: - raise NotImplementedError("Function backgrounds are not yet supported.") - return type - 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) + """A group of all of the components of a model. + + Parameters + ---------- + name : str + The name of the contrast. + data : str + The name of the dataset used by the contrast. + background : str + The name of the background for the contrast. + background_action : BackgroundActions + Whether the background should be added ('add') or subtracted ('subtract') from the data. + bulk_in : str + The name of the bulk-in parameter which defines the SLD of the interface between the + first layer and the environment. + bulk_out : str + The name of the bulk-out parameter which defines the SLD of the interface between the last + layer and the environment. + scalefactor : str + resolution : str + The name of the instrument resolution for this contrast. + resample : bool + Whether adaptive resampling should be used for interface microslicing. + model : list[str] + If this is a standard layers model, this should be a list of layer names + that make up the slab model for this contrast. + For custom models, this should be a list of custom file names of the custom + model functions. + + """ + + name: str = Field(default_factory=lambda: f"New Contrast {next(contrast_number)}", min_length=1) data: str = "" background: str = "" background_action: BackgroundActions = BackgroundActions.Add @@ -125,9 +153,39 @@ def __str__(self): 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) + """A group of all of the components of a model, including domain terms. + + Parameters + ---------- + name : str + The name of the contrast. + data : str + The name of the dataset used by the contrast. + background : str + The name of the background for the contrast. + background_action : BackgroundActions + Whether the background should be added ('add') or subtracted ('subtract') from the data. + bulk_in : str + The name of the bulk-in parameter which defines the SLD of the interface between the + first layer and the environment. + bulk_out : str + The name of the bulk-out parameter which defines the SLD of the interface between the last + layer and the environment. + scalefactor : str + resolution : str + The name of the instrument resolution for this contrast. + resample : bool + Whether adaptive resampling should be used for interface microslicing. + domain_ratio : str + model : list[str] + If this is a standard layers model, this should be a list of layer names + that make up the slab model for this contrast. + For custom models, this should be a list of custom file names of the custom + model functions. + + """ + + name: str = Field(default_factory=lambda: f"New Contrast {next(contrast_number)}", min_length=1) data: str = "" background: str = "" background_action: BackgroundActions = BackgroundActions.Add @@ -161,9 +219,24 @@ def __str__(self): 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) + """A file containing functions to use for a custom model, function background or function resolution. + + Parameters + ---------- + name : str + The name of this custom file object. + filename : str + The name of the file containing the custom function. + function_name : str + The name of the custom function within the file. + language : Languages + What language the custom function is written in: 'matlab', 'python', or 'cpp' (C++) + path : pathlib.Path + The path to the custom file. + + """ + + name: str = Field(default_factory=lambda: f"New Custom File {next(custom_file_number)}", min_length=1) filename: str = "" function_name: str = "" language: Languages = Languages.Python @@ -188,9 +261,18 @@ def set_matlab_function_name(self): class Data(RATModel, arbitrary_types_allowed=True): - """Defines the dataset required for each contrast.""" + """A dataset required for a contrast. - name: str = Field(default_factory=lambda: "New Data " + next(data_number), min_length=1) + name : str + The name of this dataset. + data : np.ndarray[np.float64] + The (x,y,z) data for this dataset, given as a Numpy array of three columns. + data_range : list[float] + simulation_range : list[float] + + """ + + name: str = Field(default_factory=lambda: f"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) @@ -280,9 +362,21 @@ def __str__(self): class DomainContrast(RATModel): - """Groups together the layers required for each domain.""" + """A group of layers required for a domain. + + Parameters + ---------- + name : str + The name of this domain contrast. + model : list[str] + If this is a standard layers model, this should be a list of layer names + that make up the slab model for this contrast. + For custom models, this should be a list of custom file names of the custom + model functions. + + """ - name: str = Field(default_factory=lambda: "New Domain Contrast " + next(domain_contrast_number), min_length=1) + name: str = Field(default_factory=lambda: f"New Domain Contrast {next(domain_contrast_number)}", min_length=1) model: list[str] = [] def __str__(self): @@ -294,9 +388,25 @@ def __str__(self): 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) + """A slab model layer with given physical properties. + + Parameters + ---------- + name : str + The name of this layer. + thickness : str + The name of the parameter describing the thickness of this layer. + SLD : str + The name of the parameter describing the scattering length density + of this layer. + roughness : str + The name of the parameter describing the roughness of this layer. + hydration : str + hydrate_with : str + + """ + + name: str = Field(default_factory=lambda: f"New Layer {next(layer_number)}", min_length=1) thickness: str SLD: str = Field(validation_alias="SLD_real") roughness: str @@ -315,9 +425,28 @@ def sld_imaginary_error(cls, data: Any): 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) + """A slab model layer with a non-negligible absorption term. + + Parameters + ---------- + name : str + The name of this layer. + thickness : str + The name of the parameter describing the thickness of this layer. + SLD_real : str + The name of the parameter describing the real (scattering) term + for the scattering length density of this layer. + SLD_imaginary : str + The name of the parameter describing the imaginary (absorption) term + for the scattering length density of this layer. + roughness : str + The name of the parameter describing the roughness of this layer. + hydration : str + hydrate_with : str + + """ + + name: str = Field(default_factory=lambda: f"New Layer {next(layer_number)}", min_length=1) thickness: str SLD_real: str = Field(validation_alias="SLD") SLD_imaginary: str = "" @@ -327,9 +456,30 @@ 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) + """A parameter needed to specify the model. + + Parameters + ---------- + name : str + The name of this parameter. + min : float + The minimum value that this parameter could take when fitted. + value : float + The value of this parameter. + max : float + The maximum value that this parameter could take when fitted. + fit : bool + Whether this parameter should be fitted in a calculation. + prior_type : Priors + For Bayesian calculations, whether the prior likelihood + is assumed to be 'uniform' or 'gaussian'. + mu, sigma : float + If the prior type is Gaussian, the mu and sigma values describing + the Gaussian function for the prior likelihood. + + """ + + name: str = Field(default_factory=lambda: f"New Parameter {next(parameter_number)}", min_length=1) min: float = 0.0 value: float = 0.0 max: float = 0.0 @@ -360,10 +510,30 @@ class ProtectedParameter(Parameter): class Resolution(RATModel): - """Defines Resolutions in RAT.""" - - name: str = Field(default_factory=lambda: "New Resolution " + next(resolution_number), min_length=1) + """An instrument resolution. + + Parameters + ---------- + name : str + The name of the background. + type : TypeOptions + The type of background (constant, function or data) + source : str + The source data for the background; + - if type is 'constant', this should be the name of a background parameter. + - if type is 'data', this should be the name of a dataset defined in `Project.data`. + - if type is 'function', this should be the name of a custom function defined in `Project.custom_files`. + value_1, value_2, ..., value_5 : str + Values required by the background. + - if type is 'constant', this should be blank. + - if type is 'data', value_1 may be the parameter name for an optional offset. Other values are ignored. + - if type is 'function', these values may be the names of up to 5 parameters which are passed to the function. + + """ + + name: str = Field(default_factory=lambda: f"New Resolution {next(resolution_number)}", min_length=1) type: TypeOptions = TypeOptions.Constant + source: str = "" value_1: str = "" value_2: str = "" value_3: str = "" diff --git a/RATapi/project.py b/RATapi/project.py index 95626436..67090142 100644 --- a/RATapi/project.py +++ b/RATapi/project.py @@ -48,26 +48,22 @@ def discriminate_contrasts(contrast_input): values_defined_in = { - "backgrounds.constant.value_1": "background_parameters", - "backgrounds.constant.value_2": "background_parameters", - "backgrounds.constant.value_3": "background_parameters", - "backgrounds.constant.value_4": "background_parameters", - "backgrounds.constant.value_5": "background_parameters", - "backgrounds.data.value_1": "data", - "backgrounds.data.value_2": "data", - "backgrounds.data.value_3": "data", - "backgrounds.data.value_4": "data", - "backgrounds.data.value_5": "data", - "resolutions.constant.value_1": "resolution_parameters", - "resolutions.constant.value_2": "resolution_parameters", - "resolutions.constant.value_3": "resolution_parameters", - "resolutions.constant.value_4": "resolution_parameters", - "resolutions.constant.value_5": "resolution_parameters", - "resolutions.data.value_1": "data", - "resolutions.data.value_2": "data", - "resolutions.data.value_3": "data", - "resolutions.data.value_4": "data", - "resolutions.data.value_5": "data", + "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", + "backgrounds.constant.source": "background_parameters", + "backgrounds.data.source": "data", + "backgrounds.function.source": "custom_files", + "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", + "resolutions.constant.source": "resolution_parameters", + "resolutions.data.source": "data", + "resolutions.function.source": "custom_files", "layers.thickness": "parameters", "layers.SLD": "parameters", "layers.SLD_real": "parameters", @@ -84,8 +80,12 @@ def discriminate_contrasts(contrast_input): 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"]), + "background_parameters": AllFields( + "backgrounds", ["source", "value_1", "value_2", "value_3", "value_4", "value_5"] + ), + "resolution_parameters": AllFields( + "resolutions", ["source", "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"]), @@ -199,7 +199,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): ) backgrounds: ClassList[RATapi.models.Background] = ClassList( - RATapi.models.Background(name="Background 1", type=TypeOptions.Constant, value_1="Background Param 1"), + RATapi.models.Background(name="Background 1", type=TypeOptions.Constant, source="Background Param 1"), ) resolution_parameters: ClassList[RATapi.models.Parameter] = ClassList( @@ -216,7 +216,7 @@ class Project(BaseModel, validate_assignment=True, extra="forbid"): ) resolutions: ClassList[RATapi.models.Resolution] = ClassList( - RATapi.models.Resolution(name="Resolution 1", type=TypeOptions.Constant, value_1="Resolution Param 1"), + RATapi.models.Resolution(name="Resolution 1", type=TypeOptions.Constant, source="Resolution Param 1"), ) custom_files: ClassList[RATapi.models.CustomFile] = ClassList() @@ -526,13 +526,10 @@ def update_renamed_models(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_background_resolution_values( - "backgrounds", value_fields, self.background_parameters.get_names(), self.data.get_names() - ) - self.check_allowed_background_resolution_values( - "resolutions", value_fields, self.resolution_parameters.get_names(), self.data.get_names() - ) + values = ["value_1", "value_2", "value_3", "value_4", "value_5"] + for field in ["backgrounds", "resolutions"]: + self.check_allowed_source(field) + self.check_allowed_values(field, values, getattr(self, f"{field[:-1]}_parameters").get_names()) self.check_allowed_values( "layers", @@ -624,24 +621,18 @@ def check_allowed_values(self, attribute: str, field_list: list[str], allowed_va f'"{values_defined_in[f"{attribute}.{field}"]}".', ) - def check_allowed_background_resolution_values( - self, attribute: str, field_list: list[str], allowed_constants: list[str], allowed_data: list[str] - ) -> None: - """Check the values of the given fields in the given model are in the supplied list of allowed values. + def check_allowed_source(self, attribute: str) -> None: + """Check that the source of a background or resolution is defined in the relevant field for its type. - For backgrounds and resolutions, the list of allowed values depends on whether the type of the - background/resolution is "constant" or "data". + - A constant background or resolution source should be defined in + `background_parameters` or `resolution_parameters` respectively; + - A data background source should be defined in `data` + - A function background source should be defined in `custom_files` Parameters ---------- attribute : str The attribute of Project being validated. - field_list : list [str] - The fields of the attribute to be checked for valid values. - allowed_constants : list [str] - The list of allowed values for the fields given in field_list if the type is "constant". - allowed_data : list [str] - The list of allowed values for the fields given in field_list if the type is "data". Raises ------ @@ -653,19 +644,17 @@ def check_allowed_background_resolution_values( class_list = getattr(self, attribute) for model in class_list: if model.type == TypeOptions.Constant: - allowed_values = allowed_constants + allowed_values = getattr(self, f"{attribute[:-1]}_parameters").get_names() elif model.type == TypeOptions.Data: - allowed_values = allowed_data + allowed_values = self.data.get_names() else: - raise ValueError('"Function" type backgrounds and resolutions are not yet supported.') + allowed_values = self.custom_files.get_names() - 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}.{model.type}.{field}"]}".', - ) + if (value := model.source) != "" and value not in allowed_values: + raise ValueError( + f'The value "{value}" in the "source" field of "{attribute}" must be defined in ' + f'"{values_defined_in[f"{attribute}.{model.type}.source"]}".', + ) def check_contrast_model_allowed_values( self, diff --git a/RATapi/utils/convert.py b/RATapi/utils/convert.py index 2aeba655..1934dc63 100644 --- a/RATapi/utils/convert.py +++ b/RATapi/utils/convert.py @@ -137,13 +137,13 @@ def read_param(names, constrs, values, fits): # create backgrounds and resolutions from parameters backs = ClassList( [ - Background(name=back_name, value_1=mat_project["back_param_names"][i]) + Background(name=back_name, type="constant", source=mat_project["back_param_names"][i]) for i, back_name in enumerate(mat_project["backsNames"]) ] ) res = ClassList( [ - Resolution(name=res_name, value_1=mat_project["res_param_names"][i]) + Resolution(name=res_name, type="constant", source=mat_project["res_param_names"][i]) for i, res_name in enumerate(mat_project["resolNames"]) ] ) @@ -388,24 +388,24 @@ def convert_parameters( resolutions = { "resolNames": [r.name for r in project.resolutions], - "resolution": [project.resolution_parameters[r.value_1].value for r in project.resolutions], + "resolution": [project.resolution_parameters[r.source].value for r in project.resolutions], "resolution_constr": [ - [project.resolution_parameters[r.value_1].min, project.resolution_parameters[r.value_1].max] + [project.resolution_parameters[r.source].min, project.resolution_parameters[r.source].max] for r in project.resolutions ], - "resolution_fityesno": [int(project.resolution_parameters[r.value_1].fit) for r in project.resolutions], + "resolution_fityesno": [int(project.resolution_parameters[r.source].fit) for r in project.resolutions], "numberOfResolutions": len(project.resolutions), } r1.update(resolutions) backgrounds = { "backsNames": [b.name for b in project.backgrounds], - "backs": [project.background_parameters[b.value_1].value for b in project.backgrounds], + "backs": [project.background_parameters[b.source].value for b in project.backgrounds], "backs_constr": [ - [project.background_parameters[b.value_1].min, project.background_parameters[b.value_1].max] + [project.background_parameters[b.source].min, project.background_parameters[b.source].max] for b in project.backgrounds ], - "backgrounds_fityesno": [int(project.background_parameters[b.value_1].fit) for b in project.backgrounds], + "backgrounds_fityesno": [int(project.background_parameters[b.source].fit) for b in project.backgrounds], "numberOfBacks": len(project.backgrounds), } r1.update(backgrounds) @@ -456,10 +456,10 @@ def convert_parameters( for contrast in project.contrasts: # R1 stores contrast resolutions and background by the index of the relevant parameter resolution = project.resolutions[contrast.resolution] - contrasts["contrastResolutions"].append(project.resolution_parameters.index(resolution.value_1, True)) + contrasts["contrastResolutions"].append(project.resolution_parameters.index(resolution.source, True)) background = project.backgrounds[contrast.background] - contrasts["contrastBacks"].append(project.background_parameters.index(background.value_1, True)) + contrasts["contrastBacks"].append(project.background_parameters.index(background.source, True)) data = project.data[contrast.data] if "simulation" in data.name: diff --git a/tests/conftest.py b/tests/conftest.py index dbdc1741..978e9d22 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -5226,7 +5226,7 @@ def r1_default_project(): ) ), backgrounds=RATapi.ClassList( - RATapi.models.Background(name="Background 1", type="constant", value_1="Background parameter 1") + RATapi.models.Background(name="Background 1", type="constant", source="Background parameter 1") ), background_parameters=RATapi.ClassList( RATapi.models.Parameter( @@ -5241,7 +5241,7 @@ def r1_default_project(): ) ), resolutions=RATapi.ClassList( - RATapi.models.Resolution(name="Resolution 1", type="constant", value_1="Resolution parameter 1") + RATapi.models.Resolution(name="Resolution 1", type="constant", source="Resolution parameter 1") ), resolution_parameters=RATapi.ClassList( RATapi.models.Parameter( @@ -5438,8 +5438,8 @@ def r1_monolayer(): ), backgrounds=RATapi.ClassList( [ - RATapi.models.Background(name="Background D2O", type="constant", value_1="Background parameter 1"), - RATapi.models.Background(name="Background ACMW", type="constant", value_1="Background parameter 2"), + RATapi.models.Background(name="Background D2O", type="constant", source="Background parameter 1"), + RATapi.models.Background(name="Background ACMW", type="constant", source="Background parameter 2"), ] ), background_parameters=RATapi.ClassList( @@ -5467,7 +5467,7 @@ def r1_monolayer(): ] ), resolutions=RATapi.ClassList( - RATapi.models.Resolution(name="Resolution 1", type="constant", value_1="Resolution parameter 1") + RATapi.models.Resolution(name="Resolution 1", type="constant", source="Resolution parameter 1") ), resolution_parameters=RATapi.ClassList( RATapi.models.Parameter( @@ -6182,20 +6182,12 @@ def r1_monolayer_8_contrasts(): RATapi.models.Background( name="Background D2O", type="constant", - value_1="Background parameter 1", - value_2="", - value_3="", - value_4="", - value_5="", + source="Background parameter 1", ), RATapi.models.Background( name="Background ACMW", type="constant", - value_1="Background parameter 2", - value_2="", - value_3="", - value_4="", - value_5="", + source="Background parameter 2", ), ], resolution_parameters=[ @@ -6214,11 +6206,7 @@ def r1_monolayer_8_contrasts(): RATapi.models.Resolution( name="Resolution 1", type="constant", - value_1="Resolution parameter 1", - value_2="", - value_3="", - value_4="", - value_5="", + source="Resolution parameter 1", ) ], custom_files=[], @@ -6845,11 +6833,7 @@ def r1_orso_polymer(): RATapi.models.Background( name="Background 1", type="constant", - value_1="Background parameter 1", - value_2="", - value_3="", - value_4="", - value_5="", + source="Background parameter 1", ) ], resolution_parameters=[ @@ -6868,11 +6852,7 @@ def r1_orso_polymer(): RATapi.models.Resolution( name="Resolution 1", type="constant", - value_1="Resolution parameter 1", - value_2="", - value_3="", - value_4="", - value_5="", + source="Resolution parameter 1", ) ], custom_files=[], @@ -7049,11 +7029,7 @@ def r1_motofit_bench_mark(): RATapi.models.Background( name="Background 1", type="constant", - value_1="Background parameter 1", - value_2="", - value_3="", - value_4="", - value_5="", + source="Background parameter 1", ) ], resolution_parameters=[ @@ -7072,11 +7048,7 @@ def r1_motofit_bench_mark(): RATapi.models.Resolution( name="Resolution 1", type="constant", - value_1="Resolution parameter 1", - value_2="", - value_3="", - value_4="", - value_5="", + source="Resolution parameter 1", ) ], custom_files=[], diff --git a/tests/test_convert.py b/tests/test_convert.py index b8ca45dc..eef85c7e 100644 --- a/tests/test_convert.py +++ b/tests/test_convert.py @@ -32,15 +32,15 @@ def dspc_bilayer(): for i in range(0, len(project.background_parameters)): param = project.background_parameters[i] for background in project.backgrounds: - if background.value_1 == param.name: - background.value_1 = f"Background parameter {i + 1}" + if background.source == param.name: + background.source = f"Background parameter {i + 1}" param.name = f"Background parameter {i + 1}" for i in range(0, len(project.resolution_parameters)): param = project.resolution_parameters[i] for resolution in project.resolutions: - if resolution.value_1 == param.name: - resolution.value_1 = f"Resolution parameter {i + 1}" + if resolution.source == param.name: + resolution.source = f"Resolution parameter {i + 1}" param.name = f"Resolution parameter {i + 1}" return project diff --git a/tests/test_project.py b/tests/test_project.py index 6bb14326..b6f437bc 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -110,11 +110,11 @@ def default_project_str(): "| 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" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+---------+\n" + "| index | name | type | source | 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" @@ -122,11 +122,11 @@ def default_project_str(): "| 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" + "+-------+--------------+----------+--------------------+---------+---------+---------+---------+---------+\n" + "| index | name | type | source | 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" @@ -627,8 +627,8 @@ def test_check_protected_parameters(delete_operation) -> None: @pytest.mark.parametrize( ["model", "field"], [ - ("background_parameters", "value_1"), - ("resolution_parameters", "value_1"), + ("background_parameters", "source"), + ("resolution_parameters", "source"), ("parameters", "roughness"), ("data", "data"), ("backgrounds", "background"), @@ -648,25 +648,23 @@ def test_rename_models(test_project, model: str, field: str) -> None: @pytest.mark.parametrize( - "field", + "background_type, expected_field", [ - "value_1", - "value_2", - "value_3", - "value_4", - "value_5", + [TypeOptions.Constant, "background_parameters"], + [TypeOptions.Data, "data"], + [TypeOptions.Function, "custom_files"], ], ) -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 +def test_allowed_backgrounds(background_type, expected_field) -> None: + """If the source field of the Background model are set to values that are not specified in the background parameters, we should raise a ValidationError. """ - test_background = RATapi.models.Background(**{field: "undefined"}) + test_background = RATapi.models.Background(type=background_type, source="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".', + match="1 validation error for Project\n Value error, The value " + '"undefined" in the "source" field of "backgrounds" must be ' + f'defined in "{expected_field}".', ): RATapi.Project(backgrounds=RATapi.ClassList(test_background)) @@ -739,25 +737,23 @@ def test_allowed_absorption_layers(field: str) -> None: @pytest.mark.parametrize( - "field", + "resolution_type, expected_field", [ - "value_1", - "value_2", - "value_3", - "value_4", - "value_5", + [TypeOptions.Constant, "resolution_parameters"], + [TypeOptions.Data, "data"], + [TypeOptions.Function, "custom_files"], ], ) -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 +def test_allowed_resolutions(resolution_type, expected_field) -> None: + """If the "value" fields of the Resolution model are set to values that are not specified in the resolution parameters, we should raise a ValidationError. """ - test_resolution = RATapi.models.Resolution(**{field: "undefined"}) + test_resolution = RATapi.models.Resolution(type=resolution_type, source="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".', + match="1 validation error for Project\n Value error, The value " + '"undefined" in the "source" field of "resolutions" must be ' + f'defined in "{expected_field}".', ): RATapi.Project(resolutions=RATapi.ClassList(test_resolution)) @@ -967,17 +963,12 @@ def test_check_allowed_values_not_on_list(test_value: str) -> None: def test_check_allowed_background_resolution_values_constant(test_value: str) -> None: """We should not raise an error if string values are defined and on the appropriate list of allowed values.""" project = RATapi.Project.model_construct( - backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Constant, value_1=test_value)) - ) - assert ( - project.check_allowed_background_resolution_values( - "backgrounds", ["value_1"], ["Background Param 1"], ["Simulation"] - ) - is None + background_parameters=RATapi.ClassList(RATapi.models.Parameter(name="Background Param 1")), + backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Constant, source=test_value)), ) + assert project.check_allowed_source("backgrounds") is None -@pytest.mark.skip("Data backgrounds not currently supported.") @pytest.mark.parametrize( "test_value", [ @@ -988,14 +979,9 @@ def test_check_allowed_background_resolution_values_constant(test_value: str) -> def test_check_allowed_background_resolution_values_data(test_value: str) -> None: """We should not raise an error if string values are defined and on the appropriate list of allowed values.""" project = RATapi.Project.model_construct( - backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Data, value_1=test_value)) - ) - assert ( - project.check_allowed_background_resolution_values( - "backgrounds", ["value_1"], ["Background Param 1"], ["Simulation"] - ) - is None + backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Data, source=test_value)) ) + assert project.check_allowed_source("backgrounds") is None @pytest.mark.parametrize( @@ -1007,19 +993,18 @@ def test_check_allowed_background_resolution_values_not_on_constant_list(test_va ValueError. """ project = RATapi.Project.model_construct( - backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Constant, value_1=test_value)) + backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Constant, source=test_value)) ) with pytest.raises( ValueError, - match=f'The value "{test_value}" in the "value_1" field of "backgrounds" must be ' + match=f'The value "{test_value}" in the "source" field of "backgrounds" must be ' f'defined in "background_parameters".', ): - project.check_allowed_background_resolution_values( - "backgrounds", ["value_1"], ["Background Param 1"], ["Simulation"] + project.check_allowed_source( + "backgrounds", ) -@pytest.mark.skip("Data backgrounds not currently supported.") @pytest.mark.parametrize( "test_value", [ @@ -1032,15 +1017,13 @@ def test_check_allowed_background_resolution_values_on_data_list(test_value: str ValueError. """ project = RATapi.Project.model_construct( - backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Data, value_1=test_value)) + backgrounds=RATapi.ClassList(RATapi.models.Background(type=TypeOptions.Data, source=test_value)) ) with pytest.raises( ValueError, - match=f'The value "{test_value}" in the "value_1" field of "backgrounds" must be defined in "data".', + match=f'The value "{test_value}" in the "source" field of "backgrounds" must be defined in "data".', ): - project.check_allowed_background_resolution_values( - "backgrounds", ["value_1"], ["Background Param 1"], ["Simulation"] - ) + project.check_allowed_source("backgrounds") @pytest.mark.parametrize( @@ -1148,16 +1131,18 @@ def test_write_script_wrong_extension(test_project, extension: str) -> None: @pytest.mark.parametrize( ["class_list", "model_type", "field"], [ - ("backgrounds", "constant", "value_1"), - ("backgrounds", "constant", "value_2"), - ("backgrounds", "constant", "value_3"), - ("backgrounds", "constant", "value_4"), - ("backgrounds", "constant", "value_5"), - ("resolutions", "constant", "value_1"), - ("resolutions", "constant", "value_2"), - ("resolutions", "constant", "value_3"), - ("resolutions", "constant", "value_4"), - ("resolutions", "constant", "value_5"), + ("backgrounds", "constant", "source"), + ("backgrounds", "", "value_1"), + ("backgrounds", "", "value_2"), + ("backgrounds", "", "value_3"), + ("backgrounds", "", "value_4"), + ("backgrounds", "", "value_5"), + ("resolutions", "constant", "source"), + ("resolutions", "", "value_1"), + ("resolutions", "", "value_2"), + ("resolutions", "", "value_3"), + ("resolutions", "", "value_4"), + ("resolutions", "", "value_5"), ("layers", "", "thickness"), ("layers", "", "SLD"), ("layers", "", "roughness"), @@ -1191,8 +1176,8 @@ def test_wrap_set(test_project, class_list: str, model_type: str, field: str) -> @pytest.mark.parametrize( ["class_list", "parameter", "field"], [ - ("background_parameters", "Background Param 1", "value_1"), - ("resolution_parameters", "Resolution Param 1", "value_1"), + ("background_parameters", "Background Param 1", "source"), + ("resolution_parameters", "Resolution Param 1", "source"), ("parameters", "Test SLD", "SLD"), ("data", "Simulation", "data"), ("backgrounds", "Background 1", "background"), @@ -1224,16 +1209,18 @@ def test_wrap_del(test_project, class_list: str, parameter: str, field: str) -> @pytest.mark.parametrize( ["class_list", "model_type", "field", "model_params"], [ - ("backgrounds", "constant", "value_1", {}), - ("backgrounds", "constant", "value_2", {}), - ("backgrounds", "constant", "value_3", {}), - ("backgrounds", "constant", "value_4", {}), - ("backgrounds", "constant", "value_5", {}), - ("resolutions", "constant", "value_1", {}), - ("resolutions", "constant", "value_2", {}), - ("resolutions", "constant", "value_3", {}), - ("resolutions", "constant", "value_4", {}), - ("resolutions", "constant", "value_5", {}), + ("backgrounds", "constant", "source", {}), + ("backgrounds", "", "value_1", {}), + ("backgrounds", "", "value_2", {}), + ("backgrounds", "", "value_3", {}), + ("backgrounds", "", "value_4", {}), + ("backgrounds", "", "value_5", {}), + ("resolutions", "constant", "source", {}), + ("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), @@ -1268,16 +1255,18 @@ def test_wrap_iadd(test_project, class_list: str, model_type: str, field: str, m @pytest.mark.parametrize( ["class_list", "model_type", "field", "model_params"], [ - ("backgrounds", "constant", "value_1", {}), - ("backgrounds", "constant", "value_2", {}), - ("backgrounds", "constant", "value_3", {}), - ("backgrounds", "constant", "value_4", {}), - ("backgrounds", "constant", "value_5", {}), - ("resolutions", "constant", "value_1", {}), - ("resolutions", "constant", "value_2", {}), - ("resolutions", "constant", "value_3", {}), - ("resolutions", "constant", "value_4", {}), - ("resolutions", "constant", "value_5", {}), + ("backgrounds", "constant", "source", {}), + ("backgrounds", "", "value_1", {}), + ("backgrounds", "", "value_2", {}), + ("backgrounds", "", "value_3", {}), + ("backgrounds", "", "value_4", {}), + ("backgrounds", "", "value_5", {}), + ("resolutions", "constant", "source", {}), + ("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), @@ -1313,16 +1302,18 @@ def test_wrap_append(test_project, class_list: str, model_type: str, field: str, @pytest.mark.parametrize( ["class_list", "model_type", "field", "model_params"], [ - ("backgrounds", "constant", "value_1", {}), - ("backgrounds", "constant", "value_2", {}), - ("backgrounds", "constant", "value_3", {}), - ("backgrounds", "constant", "value_4", {}), - ("backgrounds", "constant", "value_5", {}), - ("resolutions", "constant", "value_1", {}), - ("resolutions", "constant", "value_2", {}), - ("resolutions", "constant", "value_3", {}), - ("resolutions", "constant", "value_4", {}), - ("resolutions", "constant", "value_5", {}), + ("backgrounds", "constant", "source", {}), + ("backgrounds", "", "value_1", {}), + ("backgrounds", "", "value_2", {}), + ("backgrounds", "", "value_3", {}), + ("backgrounds", "", "value_4", {}), + ("backgrounds", "", "value_5", {}), + ("resolutions", "constant", "source", {}), + ("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), @@ -1357,11 +1348,13 @@ def test_wrap_insert(test_project, class_list: str, model_type: str, field: str, @pytest.mark.parametrize( ["class_list", "field"], [ + ("backgrounds", "source"), ("backgrounds", "value_1"), ("backgrounds", "value_2"), ("backgrounds", "value_3"), ("backgrounds", "value_4"), ("backgrounds", "value_5"), + ("resolutions", "source"), ("resolutions", "value_1"), ("resolutions", "value_2"), ("resolutions", "value_3"), @@ -1391,8 +1384,8 @@ 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"), + ("background_parameters", "Background Param 1", "source"), + ("resolution_parameters", "Resolution Param 1", "source"), ("parameters", "Test SLD", "SLD"), ("data", "Simulation", "data"), ("backgrounds", "Background 1", "background"), @@ -1424,8 +1417,8 @@ 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"), + ("background_parameters", "Background Param 1", "source"), + ("resolution_parameters", "Resolution Param 1", "source"), ("parameters", "Test SLD", "SLD"), ("data", "Simulation", "data"), ("backgrounds", "Background 1", "background"), @@ -1456,8 +1449,8 @@ 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"), + ("background_parameters", "Background Param 1", "source"), + ("resolution_parameters", "Resolution Param 1", "source"), ("parameters", "Test Thickness", "thickness"), ("data", "Simulation", "data"), ("backgrounds", "Background 1", "background"), @@ -1488,16 +1481,18 @@ def test_wrap_clear(test_project, class_list: str, parameter: str, field: str) - @pytest.mark.parametrize( ["class_list", "model_type", "field", "model_params"], [ - ("backgrounds", "constant", "value_1", {}), - ("backgrounds", "constant", "value_2", {}), - ("backgrounds", "constant", "value_3", {}), - ("backgrounds", "constant", "value_4", {}), - ("backgrounds", "constant", "value_5", {}), - ("resolutions", "constant", "value_1", {}), - ("resolutions", "constant", "value_2", {}), - ("resolutions", "constant", "value_3", {}), - ("resolutions", "constant", "value_4", {}), - ("resolutions", "constant", "value_5", {}), + ("backgrounds", "constant", "source", {}), + ("backgrounds", "", "value_1", {}), + ("backgrounds", "", "value_2", {}), + ("backgrounds", "", "value_3", {}), + ("backgrounds", "", "value_4", {}), + ("backgrounds", "", "value_5", {}), + ("resolutions", "constant", "source", {}), + ("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), From a5697b3faad92fb2737ebe0f1b6bf5e0d5dcde68 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Mon, 9 Dec 2024 15:07:10 +0000 Subject: [PATCH 02/15] added model renaming for data and function backgrounds/resolutions --- RATapi/project.py | 48 ++++++++++++++++++++++++------------------- tests/test_project.py | 43 +++++++++++++++++++++++--------------- 2 files changed, 53 insertions(+), 38 deletions(-) diff --git a/RATapi/project.py b/RATapi/project.py index 67090142..eec7ec99 100644 --- a/RATapi/project.py +++ b/RATapi/project.py @@ -80,20 +80,25 @@ def discriminate_contrasts(contrast_input): AllFields = collections.namedtuple("AllFields", ["attribute", "fields"]) model_names_used_in = { - "background_parameters": AllFields( - "backgrounds", ["source", "value_1", "value_2", "value_3", "value_4", "value_5"] - ), - "resolution_parameters": AllFields( - "resolutions", ["source", "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"]), + "background_parameters": [ + AllFields("backgrounds", ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]) + ], + "resolution_parameters": [ + AllFields("resolutions", ["source", "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"]), + AllFields("backgrounds", ["source"]), + AllFields("resolutions", ["source"]), + ], + "custom_files": [AllFields("backgrounds", ["source"]), AllFields("resolutions", ["source"])], + "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 @@ -508,18 +513,19 @@ def set_absorption(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: + for class_list, fields_to_update in model_names_used_in.items(): old_names = self._all_names[class_list] 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: - 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: - if field in fields: - setattr(model_names_list[index], field, new_name) + for field in fields_to_update: + project_field = getattr(self, field.attribute) + all_matches = project_field.get_all_matches(old_name) + params = field.fields + for index, param in all_matches: + if param in params: + setattr(project_field[index], param, new_name) self._all_names = self.get_all_names() return self diff --git a/tests/test_project.py b/tests/test_project.py index b6f437bc..8224f7c1 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -625,26 +625,35 @@ def test_check_protected_parameters(delete_operation) -> None: @pytest.mark.parametrize( - ["model", "field"], + ["model", "fields"], [ - ("background_parameters", "source"), - ("resolution_parameters", "source"), - ("parameters", "roughness"), - ("data", "data"), - ("backgrounds", "background"), - ("bulk_in", "bulk_in"), - ("bulk_out", "bulk_out"), - ("scalefactors", "scalefactor"), - ("resolutions", "resolution"), + ("background_parameters", ["source"]), + ("resolution_parameters", ["source"]), + ("parameters", ["roughness"]), + ("data", ["data", "source", "source"]), + ("custom_files", ["source", "source"]), + ("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: +def test_rename_models(test_project, model: str, fields: list[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. """ + if model == "data": + test_project.backgrounds[0] = RATapi.models.Background(type="data", source="Simulation") + test_project.resolutions[0] = RATapi.models.Resolution(type="data", source="Simulation") + if model == "custom_files": + test_project.backgrounds[0] = RATapi.models.Background(type="function", source="Test Custom File") + test_project.resolutions[0] = RATapi.models.Resolution(type="function", source="Test Custom File") getattr(test_project, model).set_fields(-1, name="New Name") - attribute = RATapi.project.model_names_used_in[model].attribute - assert getattr(getattr(test_project, attribute)[-1], field) == "New Name" + model_name_lists = RATapi.project.model_names_used_in[model] + for model_name_list, field in zip(model_name_lists, fields): + attribute = model_name_list.attribute + assert getattr(getattr(test_project, attribute)[-1], field) == "New Name" @pytest.mark.parametrize( @@ -1197,7 +1206,7 @@ def test_wrap_del(test_project, class_list: str, parameter: str, field: str) -> pydantic.ValidationError, match=f"1 validation error for Project\n Value error, The value " f'"{parameter}" in the "{field}" field of ' - f'"{RATapi.project.model_names_used_in[class_list].attribute}" ' + f'"{RATapi.project.model_names_used_in[class_list][0].attribute}" ' f'must be defined in "{class_list}".', ): del test_attribute[index] @@ -1405,7 +1414,7 @@ def test_wrap_pop(test_project, class_list: str, parameter: str, field: str) -> pydantic.ValidationError, match=f"1 validation error for Project\n Value error, The value " f'"{parameter}" in the "{field}" field of ' - f'"{RATapi.project.model_names_used_in[class_list].attribute}" ' + f'"{RATapi.project.model_names_used_in[class_list][0].attribute}" ' f'must be defined in "{class_list}".', ): test_attribute.pop(index) @@ -1437,7 +1446,7 @@ def test_wrap_remove(test_project, class_list: str, parameter: str, field: str) pydantic.ValidationError, match=f"1 validation error for Project\n Value error, The value " f'"{parameter}" in the "{field}" field of ' - f'"{RATapi.project.model_names_used_in[class_list].attribute}" ' + f'"{RATapi.project.model_names_used_in[class_list][0].attribute}" ' f'must be defined in "{class_list}".', ): test_attribute.remove(parameter) @@ -1469,7 +1478,7 @@ def test_wrap_clear(test_project, class_list: str, parameter: str, field: str) - pydantic.ValidationError, match=f"1 validation error for Project\n Value error, The value " f'"{parameter}" in the "{field}" field of ' - f'"{RATapi.project.model_names_used_in[class_list].attribute}" ' + f'"{RATapi.project.model_names_used_in[class_list][0].attribute}" ' f'must be defined in "{class_list}".', ): test_attribute.clear() From 65febe3e9a000034abdb695c65f288c4c46a481f Mon Sep 17 00:00:00 2001 From: alexhroom Date: Tue, 10 Dec 2024 10:40:52 +0000 Subject: [PATCH 03/15] added validators for value warnings and unimplemented behaviour --- RATapi/models.py | 71 +++++++++++++++++++++++++++++++++++++------ tests/test_project.py | 9 ++++-- 2 files changed, 69 insertions(+), 11 deletions(-) diff --git a/RATapi/models.py b/RATapi/models.py index c055f536..aab06b59 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -1,6 +1,7 @@ """The models module. Contains the pydantic models used by RAT to store project parameters.""" import pathlib +import warnings from itertools import count from typing import Any @@ -60,7 +61,7 @@ class Background(RATModel): - if type is 'function', this should be the name of a custom function defined in `Project.custom_files`. value_1, value_2, ..., value_5 : str Values required by the background. - - if type is 'constant', this should be blank. + - if type is 'constant', all values will be ignored. - if type is 'data', value_1 may be the parameter name for an optional offset. Other values are ignored. - if type is 'function', these values may be the names of up to 5 parameters which are passed to the function. @@ -75,6 +76,26 @@ class Background(RATModel): value_4: str = "" value_5: str = "" + @model_validator(mode="after") + def warn_parameters(self): + """Raise a warning if the parameters given are not expected for the given type.""" + if self.type == TypeOptions.Constant: + expected_empty_fields = ["value_1", "value_2", "value_3", "value_4", "value_5"] + elif self.type == TypeOptions.Data: + expected_empty_fields = ["value_2", "value_3", "value_4", "value_5"] + else: + return self + + non_empty_fields = [v for v in expected_empty_fields if getattr(self, v) != ""] + if non_empty_fields: + warnings.warn( + "The following values are not recognised by this background type and will be ignored: " + f"{', '.join(non_empty_fields)}", + stacklevel=2, + ) + + return self + class Contrast(RATModel): """A group of all of the components of a model. @@ -515,19 +536,20 @@ class Resolution(RATModel): Parameters ---------- name : str - The name of the background. + The name of the resolution. type : TypeOptions - The type of background (constant, function or data) + The type of resolution: 'constant', 'data', or (NOT YET IMPLEMENTED) 'function'. source : str - The source data for the background; + The source data for the resolution; - if type is 'constant', this should be the name of a background parameter. - - if type is 'data', this should be the name of a dataset defined in `Project.data`. - - if type is 'function', this should be the name of a custom function defined in `Project.custom_files`. + - if type is 'data', this should be empty (resolution data is in the contrast data). + - if type is 'function' (NOT YET IMPLEMENTED), + this should be the name of a custom function defined in `Project.custom_files`. value_1, value_2, ..., value_5 : str Values required by the background. - - if type is 'constant', this should be blank. - - if type is 'data', value_1 may be the parameter name for an optional offset. Other values are ignored. - - if type is 'function', these values may be the names of up to 5 parameters which are passed to the function. + - if type is 'constant' or 'data', all values will be ignored. + - if type is 'function' (NOT YET IMPLEMENTED), + these values may be the names of up to 5 parameters which are passed to the function. """ @@ -539,3 +561,34 @@ class Resolution(RATModel): value_3: str = "" value_4: str = "" value_5: str = "" + + @field_validator("type") + @classmethod + def validate_unimplemented_resolutions(cls, type: TypeOptions): + """Raise an error if currently unsupported function resolutions are used.""" + # when function resolutions are added, fix the commented-out parts of + # test_project.py::test_rename_models + # and test_project.py::test_allowed_resolutions + if type == TypeOptions.Function: + raise NotImplementedError("Function resolutions are not yet supported.") + return type + + @model_validator(mode="after") + def warn_parameters(self): + """Raise a warning if the parameters given are not expected for the given type.""" + if self.type == TypeOptions.Constant: + expected_empty_fields = ["value_1", "value_2", "value_3", "value_4", "value_5"] + elif self.type == TypeOptions.Data: + expected_empty_fields = ["source", "value_1", "value_2", "value_3", "value_4", "value_5"] + else: + return self + + non_empty_fields = [v for v in expected_empty_fields if getattr(self, v) != ""] + if non_empty_fields: + warnings.warn( + "The following values are not recognised by this resolution type and will be ignored: " + f"{', '.join(non_empty_fields)}", + stacklevel=2, + ) + + return self diff --git a/tests/test_project.py b/tests/test_project.py index 8224f7c1..5cc0cae8 100644 --- a/tests/test_project.py +++ b/tests/test_project.py @@ -648,7 +648,11 @@ def test_rename_models(test_project, model: str, fields: list[str]) -> None: test_project.resolutions[0] = RATapi.models.Resolution(type="data", source="Simulation") if model == "custom_files": test_project.backgrounds[0] = RATapi.models.Background(type="function", source="Test Custom File") - test_project.resolutions[0] = RATapi.models.Resolution(type="function", source="Test Custom File") + # workaround until function resolutions are added + # test_project.resolutions[0] = RATapi.models.Resolution(type="function", source="Test Custom File") + test_project.resolution_parameters.append(RATapi.models.Parameter(name="New Name")) + test_project.resolutions[0] = RATapi.models.Resolution(type="constant", source="New Name") + getattr(test_project, model).set_fields(-1, name="New Name") model_name_lists = RATapi.project.model_names_used_in[model] for model_name_list, field in zip(model_name_lists, fields): @@ -750,7 +754,8 @@ def test_allowed_absorption_layers(field: str) -> None: [ [TypeOptions.Constant, "resolution_parameters"], [TypeOptions.Data, "data"], - [TypeOptions.Function, "custom_files"], + # uncomment when function resolutions are added! + # [TypeOptions.Function, "custom_files"], ], ) def test_allowed_resolutions(resolution_type, expected_field) -> None: From a432de521edb16be3224b3f657e22cc3deea91e5 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Wed, 11 Dec 2024 08:46:33 +0000 Subject: [PATCH 04/15] added data and function background examples and example test --- RATapi/examples/__init__.py | 6 +- RATapi/examples/data/d2o_background_data.dat | 82 +++++++ .../non_polarised/DSPC_data_background.py | 218 ++++++++++++++++++ .../non_polarised/DSPC_function_background.py | 218 ++++++++++++++++++ .../non_polarised/backgroundFunction.m | 14 ++ tests/test_examples.py | 43 ++++ 6 files changed, 580 insertions(+), 1 deletion(-) create mode 100644 RATapi/examples/data/d2o_background_data.dat create mode 100644 RATapi/examples/non_polarised/DSPC_data_background.py create mode 100644 RATapi/examples/non_polarised/DSPC_function_background.py create mode 100644 RATapi/examples/non_polarised/backgroundFunction.m create mode 100644 tests/test_examples.py diff --git a/RATapi/examples/__init__.py b/RATapi/examples/__init__.py index 6d29206f..355de43a 100644 --- a/RATapi/examples/__init__.py +++ b/RATapi/examples/__init__.py @@ -1,10 +1,12 @@ from RATapi.examples.absorption.absorption import absorption -from RATapi.examples.convert_rascal_project import convert_rascal +from RATapi.examples.convert_rascal_project.convert_rascal import convert_rascal from RATapi.examples.domains.domains_custom_layers import domains_custom_layers from RATapi.examples.domains.domains_custom_XY import domains_custom_XY from RATapi.examples.domains.domains_standard_layers import domains_standard_layers from RATapi.examples.non_polarised.DSPC_custom_layers import DSPC_custom_layers from RATapi.examples.non_polarised.DSPC_custom_XY import DSPC_custom_XY +from RATapi.examples.non_polarised.DSPC_data_background import DSPC_data_background +from RATapi.examples.non_polarised.DSPC_function_background import DSPC_function_background from RATapi.examples.non_polarised.DSPC_standard_layers import DSPC_standard_layers __all__ = [ @@ -15,5 +17,7 @@ "DSPC_custom_layers", "DSPC_custom_XY", "DSPC_standard_layers", + "DSPC_data_background", + "DSPC_function_background", "convert_rascal", ] diff --git a/RATapi/examples/data/d2o_background_data.dat b/RATapi/examples/data/d2o_background_data.dat new file mode 100644 index 00000000..93b2fd38 --- /dev/null +++ b/RATapi/examples/data/d2o_background_data.dat @@ -0,0 +1,82 @@ +0.011403 1.90034798968076e-06 -9.65035874608547e-07 +0.011973 1.81361996101478e-06 -4.67852140773275e-09 +0.012572 1.5763144199176e-06 -1.36151693657437e-06 +0.013201 2.66861902156408e-06 5.28737972088124e-07 +0.013861 1.19184137102607e-06 6.5429177234931e-07 +0.014554 1.75409120253422e-06 -4.50039804064594e-07 +0.015281 1.51534921649628e-06 3.54060605152339e-07 +0.016045 1.76294603973655e-06 2.30273872099416e-07 +0.016848 1.66691808740296e-06 1.02729272466451e-06 +0.01769 2.2391873415575e-06 2.02710446930881e-07 +0.018575 1.47947093349392e-06 9.31566506641381e-07 +0.019503 1.58967045467121e-06 6.58308695637623e-09 +0.020479 2.05056480300655e-06 -4.18637528083399e-07 +0.021502 2.54199126843229e-06 6.96150360898885e-08 +0.022578 1.07273638130851e-06 -5.94598550176916e-08 +0.023706 2.89987639289405e-06 8.05335897612554e-07 +0.024892 1.56081284721073e-06 5.50847432744533e-08 +0.026136 2.32386636321661e-06 -5.72991095509919e-07 +0.027443 2.52478251612625e-06 -2.38750704688337e-08 +0.028815 2.66154668842285e-06 1.05087573637142e-06 +0.030256 1.74132051718101e-06 -2.3192582338868e-07 +0.031769 2.01215747485553e-06 -4.26893410009117e-07 +0.033357 1.91881656322323e-06 8.87192534478923e-07 +0.035025 1.69716945696631e-06 1.80981995646851e-07 +0.036777 2.13585488200451e-06 -1.90164659296111e-07 +0.038615 2.17932170608953e-06 -4.91062265460096e-07 +0.040546 1.82384243860966e-06 -1.08387392818395e-06 +0.042573 2.06617996409986e-06 2.22697399577916e-07 +0.044702 3.33679088859971e-06 -1.53613977488785e-07 +0.046937 1.99621696227773e-06 3.55134601922439e-07 +0.049284 2.3551514683307e-06 -1.06523727035749e-06 +0.051748 1.76873031859355e-06 1.15746760150783e-06 +0.054336 1.73606104969994e-06 4.89763150040874e-07 +0.057052 1.82710646654775e-06 -1.15330099931726e-06 +0.059905 2.28227888686718e-06 9.45647703159621e-07 +0.0629 1.59923042799896e-06 -3.70062237263438e-07 +0.066045 1.07352660741589e-06 3.41777294345909e-07 +0.069348 2.02660800872659e-06 2.75569910736786e-07 +0.072815 1.78179239147256e-06 -6.0256088120987e-07 +0.076456 2.14000373613308e-06 -2.18911980741668e-06 +0.080279 2.04230294359047e-06 5.284442372682e-07 +0.084292 2.36455952978407e-06 -1.73629847921614e-07 +0.088507 2.45037654782291e-06 -3.78287083788833e-07 +0.092932 1.33718085273407e-06 4.35130166728242e-07 +0.097579 1.23771058575749e-06 -4.51744576618967e-07 +0.10246 2.25121666653143e-06 6.36952833032752e-07 +0.10758 2.15319571282475e-06 2.20717968415214e-07 +0.11296 1.53041976734067e-06 -6.93055107868105e-07 +0.11861 2.61870106321507e-06 7.69463958198665e-07 +0.12454 2.01650595727752e-06 4.79607176188218e-07 +0.13077 2.06178415911062e-06 1.10173268381594e-06 +0.1373 2.13696968977538e-06 -6.14098124528528e-07 +0.14417 2.3770829041412e-06 -6.16219383494697e-07 +0.15138 2.7622487528669e-06 3.50660742020884e-07 +0.15895 1.86901211887215e-06 6.33626529015099e-07 +0.16689 1.13465824611353e-06 7.62493241842828e-07 +0.17524 2.50673214999504e-06 1.05207422581854e-07 +0.184 1.53613195077685e-06 -8.87137577093779e-08 +0.1932 2.62184014311179e-06 6.22256875962273e-07 +0.20286 1.70525565120706e-06 1.67162600924891e-06 +0.213 2.74574086739327e-06 8.07667338780613e-07 +0.22365 1.59701095264287e-06 1.67629565660922e-07 +0.23484 1.4437004736234e-06 -3.59611249006791e-07 +0.24658 2.47846978404969e-06 4.99800504594724e-07 +0.25891 1.31847420553136e-06 -2.87283586089481e-07 +0.27185 2.34029248136207e-06 2.90756508518823e-07 +0.28544 2.21891181128286e-06 7.22598553179947e-07 +0.29972 1.92836622666398e-06 8.39418345516717e-07 +0.3147 1.82923374769972e-06 5.70510331434857e-07 +0.33044 3.02890531703209e-06 1.20234742176368e-06 +0.34696 1.8127324822871e-06 1.58096394623758e-06 +0.36431 1.90489840127358e-06 -1.41813181395574e-06 +0.38252 1.53063379469415e-06 -1.32294734328625e-06 +0.40165 1.59326881780842e-06 -6.38112989285387e-07 +0.42173 1.7081906242357e-06 -1.49834166690205e-06 +0.44282 2.85476268526193e-06 -1.85680046178151e-07 +0.46496 1.80479009111448e-06 -1.69120607911057e-07 +0.48821 1.6549673183601e-06 3.00340803829798e-07 +0.51262 1.77588964903545e-06 3.28929520936145e-07 +0.53825 2.35718817504981e-06 -4.48934854489947e-07 +0.56516 1.34213437585845e-06 -5.50651590195596e-07 +0.59342 2.31356104763158e-06 7.347220470815e-07 diff --git a/RATapi/examples/non_polarised/DSPC_data_background.py b/RATapi/examples/non_polarised/DSPC_data_background.py new file mode 100644 index 00000000..02987226 --- /dev/null +++ b/RATapi/examples/non_polarised/DSPC_data_background.py @@ -0,0 +1,218 @@ +import pathlib + +import numpy as np + +import RATapi as RAT + + +def DSPC_data_background(): + """Standard Layers fit of a DSPC floating bilayer""" + problem = RAT.Project(name="original_dspc_bilayer", model="standard layers", geometry="substrate/liquid") + + # Set up the relevant parameters + problem.parameters.append(name="Oxide Thickness", min=5.0, value=19.54, max=60.0, fit=True) + problem.parameters.append(name="Oxide SLD", min=3.39e-06, value=3.39e-06, max=3.41e-06, fit=False) + problem.parameters.append(name="SAM Tails Thickness", min=15.0, value=22.66, max=35.0, fit=True) + problem.parameters.append(name="SAM Tails SLD", min=-5e-07, value=-4.01e-07, max=-3e-07, fit=False) + problem.parameters.append(name="SAM Tails Hydration", min=1.0, value=5.252, max=50.0, fit=True) + problem.parameters.append(name="SAM Roughness", min=1.0, value=5.64, max=15.0, fit=True) + problem.parameters.append(name="CW Thickness", min=10.0, value=17.12, max=28.0, fit=True) + problem.parameters.append(name="CW SLD", min=0.0, value=0.0, max=1e-09, fit=False) + + 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) + problem.parameters.append( + name="SAM Heads Hydration", + min=10.0, + value=45.45, + max=50.0, + fit=True, + prior_type="gaussian", + 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) + problem.parameters.append(name="Bilayer Roughness", min=2.0, value=6.014, max=15.0, fit=True) + problem.parameters.append(name="Bilayer Tails Thickness", min=14.0, value=17.82, max=22.0, fit=True) + problem.parameters.append(name="Bilayer Tails SLD", min=-5.0e-07, value=-4.61e-07, max=0.0, fit=False) + problem.parameters.append(name="Bilayer Tails Hydration", min=10.0, value=17.64, max=50.0, fit=True) + problem.parameters.append(name="Bilayer Heads Hydration", min=10.0, value=36.15, max=50.0, fit=True) + problem.parameters.append(name="CW Hydration", min=99.9, value=100.0, max=100.0, fit=False) + problem.parameters.append(name="Oxide Hydration", min=0.0, value=23.61, max=60.0, fit=True) + + 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="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="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 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] + problem.bulk_in.append(name="Silicon", min=2.0e-06, value=2.073e-06, max=2.1e-06, fit=False) + + del problem.bulk_out[0] + problem.bulk_out.append(name="D2O", min=5.50e-06, value=5.98e-06, max=6.4e-06, fit=True) + problem.bulk_out.append(name="SMW", min=1.0e-06, value=2.21e-06, max=4.99e-06, fit=True) + + # Set the scalefactors - use one for each contrast + del problem.scalefactors[0] + problem.scalefactors.append(name="Scalefactor 1", min=0.05, value=0.10, max=0.2, fit=False) + problem.scalefactors.append(name="Scalefactor 2", min=0.05, value=0.15, max=0.2, fit=False) + + # Now deal with the backgrounds + # SMW has a constant background + del problem.backgrounds[0] + del problem.background_parameters[0] + 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="SMW Background", type="constant", source="Background parameter SMW") + + data_path = pathlib.Path(__file__).parents[1] / "data" + + # load in background data for D2O + d2o_background = np.loadtxt(data_path / "d2o_background_data.dat") + problem.data.append(name="D2O Background Data", data=d2o_background) + + # add background parameter for the offset + problem.background_parameters.append( + name="D2O Data Offset", + min=-1e-8, + value=0, + max=1e-8, + fit=True, + ) + + # add the background with data and offset + problem.backgrounds.append( + name="D2O Data Background", + type="data", + source="D2O Background Data", + value_1="D2O Data Offset", + ) + + # Now add the data + d2o_dat = np.loadtxt(data_path / "DSPC_D2O.dat", delimiter=",") + problem.data.append(name="dspc_bil_D2O", data=d2o_dat) + + smw_dat = np.loadtxt(data_path / "DSPC_SMW.dat", delimiter=",") + 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", + ] + + # Then make the two contrasts + problem.contrasts.append( + name="D2O", + bulk_in="Silicon", + bulk_out="D2O", + background="D2O Data 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, + ) + + controls = RAT.Controls() + problem, results = RAT.run(problem, controls) + + return problem, results + + +if __name__ == "__main__": + problem, results = DSPC_data_background() + RAT.plotting.plot_ref_sld(problem, results, True) diff --git a/RATapi/examples/non_polarised/DSPC_function_background.py b/RATapi/examples/non_polarised/DSPC_function_background.py new file mode 100644 index 00000000..1ad863fa --- /dev/null +++ b/RATapi/examples/non_polarised/DSPC_function_background.py @@ -0,0 +1,218 @@ +import pathlib + +import numpy as np + +import RATapi as RAT + + +def DSPC_function_background(): + """Standard Layers fit of a DSPC floating bilayer""" + problem = RAT.Project(name="original_dspc_bilayer", model="standard layers", geometry="substrate/liquid") + + # Set up the relevant parameters + problem.parameters.append(name="Oxide Thickness", min=5.0, value=19.54, max=60.0, fit=True) + problem.parameters.append(name="Oxide SLD", min=3.39e-06, value=3.39e-06, max=3.41e-06, fit=False) + problem.parameters.append(name="SAM Tails Thickness", min=15.0, value=22.66, max=35.0, fit=True) + problem.parameters.append(name="SAM Tails SLD", min=-5e-07, value=-4.01e-07, max=-3e-07, fit=False) + problem.parameters.append(name="SAM Tails Hydration", min=1.0, value=5.252, max=50.0, fit=True) + problem.parameters.append(name="SAM Roughness", min=1.0, value=5.64, max=15.0, fit=True) + problem.parameters.append(name="CW Thickness", min=10.0, value=17.12, max=28.0, fit=True) + problem.parameters.append(name="CW SLD", min=0.0, value=0.0, max=1e-09, fit=False) + + 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) + problem.parameters.append( + name="SAM Heads Hydration", + min=10.0, + value=45.45, + max=50.0, + fit=True, + prior_type="gaussian", + 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) + problem.parameters.append(name="Bilayer Roughness", min=2.0, value=6.014, max=15.0, fit=True) + problem.parameters.append(name="Bilayer Tails Thickness", min=14.0, value=17.82, max=22.0, fit=True) + problem.parameters.append(name="Bilayer Tails SLD", min=-5.0e-07, value=-4.61e-07, max=0.0, fit=False) + problem.parameters.append(name="Bilayer Tails Hydration", min=10.0, value=17.64, max=50.0, fit=True) + problem.parameters.append(name="Bilayer Heads Hydration", min=10.0, value=36.15, max=50.0, fit=True) + problem.parameters.append(name="CW Hydration", min=99.9, value=100.0, max=100.0, fit=False) + problem.parameters.append(name="Oxide Hydration", min=0.0, value=23.61, max=60.0, fit=True) + + 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="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="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 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] + problem.bulk_in.append(name="Silicon", min=2.0e-06, value=2.073e-06, max=2.1e-06, fit=False) + + del problem.bulk_out[0] + problem.bulk_out.append(name="D2O", min=5.50e-06, value=5.98e-06, max=6.4e-06, fit=True) + problem.bulk_out.append(name="SMW", min=1.0e-06, value=2.21e-06, max=4.99e-06, fit=True) + + # Set the scalefactors - use one for each contrast + del problem.scalefactors[0] + problem.scalefactors.append(name="Scalefactor 1", min=0.05, value=0.10, max=0.2, fit=False) + problem.scalefactors.append(name="Scalefactor 2", min=0.05, value=0.15, max=0.2, fit=False) + + # Now deal with the backgrounds + # SMW has a constant background + del problem.backgrounds[0] + del problem.background_parameters[0] + 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="SMW Background", type="constant", source="Background parameter SMW") + + # FIXME: replace this with a Python custom file when Python backgrounds are added! + problem.custom_files.append( + name="D2O Background Function", + filename="backgroundFunction.m", + language="matlab", + path=pathlib.Path(__file__).parent.resolve(), + ) + + problem.background_parameters.append(name="Fn Ao", min=5e-7, value=8e-6, max=5e-5) + problem.background_parameters.append(name="Fn k", min=40, value=70, max=90) + problem.background_parameters.append(name="Fn Const", min=1e-7, value=8e-6, max=1e-5) + + problem.backgrounds.append( + name="D2O Function Background", + type="function", + source="D2O Background Function", + value_1="Fn Ao", + value_2="Fn k", + value_3="Fn Const", + ) + + # Now add the data + data_path = pathlib.Path(__file__).parents[1] / "data" + + d2o_dat = np.loadtxt(data_path / "DSPC_D2O.dat", delimiter=",") + problem.data.append(name="dspc_bil_D2O", data=d2o_dat) + + smw_dat = np.loadtxt(data_path / "DSPC_SMW.dat", delimiter=",") + 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", + ] + + # Then make the two contrasts + problem.contrasts.append( + name="D2O", + bulk_in="Silicon", + bulk_out="D2O", + background="D2O Function 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, + ) + + controls = RAT.Controls() + problem, results = RAT.run(problem, controls) + + return problem, results + + +if __name__ == "__main__": + problem, results = DSPC_function_background() + RAT.plotting.plot_ref_sld(problem, results, True) diff --git a/RATapi/examples/non_polarised/backgroundFunction.m b/RATapi/examples/non_polarised/backgroundFunction.m new file mode 100644 index 00000000..181e0f58 --- /dev/null +++ b/RATapi/examples/non_polarised/backgroundFunction.m @@ -0,0 +1,14 @@ +function background = backgroundFunction(xdata,params) + +% Split up the params array.... +Ao = params(1); +k = params(2); +backConst = params(3); + +% Make an exponential decay background.... +background = zeros(numel(xdata),1); +for i = 1:numel(xdata) + background(i) = Ao*exp(-k*xdata(i)) + backConst; +end + +end diff --git a/tests/test_examples.py b/tests/test_examples.py new file mode 100644 index 00000000..cb5425b5 --- /dev/null +++ b/tests/test_examples.py @@ -0,0 +1,43 @@ +"""Test the RAT examples.""" + +import importlib + +import pytest + +import RATapi.examples as examples + + +@pytest.mark.parametrize( + "example_name", + [ + "absorption", + "domains_custom_layers", + "domains_custom_XY", + "domains_standard_layers", + "DSPC_custom_layers", + "DSPC_custom_XY", + "DSPC_standard_layers", + "DSPC_data_background", + ], +) +def test_rat_examples(example_name): + """Test that the RAT example projects run successfully.""" + p, r = getattr(examples, example_name)() + assert p is not None + assert r is not None + + +@pytest.mark.parametrize( + "example_name", + [ + "DSPC_function_background", + # FIXME: https://github.com/RascalSoftware/python-RAT/issues/102 + # "convert_rascal", + ], +) +@pytest.mark.skipif(importlib.util.find_spec("matlab") is None, reason="Matlab not installed") +def test_matlab_examples(example_name): + """Test examples which rely on MATLAB engine being installed.""" + p, r = getattr(examples, example_name)() + assert p is not None + assert r is not None From 80962300ae5817e76dea34dae7867a057347cbfc Mon Sep 17 00:00:00 2001 From: alexhroom Date: Wed, 11 Dec 2024 13:16:50 +0000 Subject: [PATCH 05/15] updated check_indices to be nested --- RATapi/inputs.py | 79 ++++++++++++++++---- tests/test_inputs.py | 169 +++++++++++++++++++++++++------------------ 2 files changed, 163 insertions(+), 85 deletions(-) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index 670d5f92..01c7b6d3 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -50,7 +50,7 @@ class FileHandles: def __init__(self, files=None): self.index = 0 - self.files = [] if files is None else [file.dict() for file in files] + self.files = [] if files is None else [file.model_dump() for file in files] def __iter__(self): self.index = 0 @@ -90,6 +90,9 @@ def __next__(self): else: raise StopIteration + def __len__(self): + return len(self.files) + def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[ProblemDefinition, Limits, Priors, Control]: """Constructs the inputs required for the compiled RAT code using the data defined in the input project and @@ -106,8 +109,6 @@ def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[Prob ------- problem : RAT.rat_core.ProblemDefinition The problem input used in the compiled RAT code. - cells : RAT.rat_core.Cells - The set of inputs that are defined in MATLAB as cell arrays. limits : RAT.rat_core.Limits A list of min/max values for each parameter defined in the project. priors : RAT.rat_core.Priors @@ -232,10 +233,31 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: for contrast in project.contrasts: background = project.backgrounds[contrast.background] contrast_background_types.append(background.type) + contrast_background_param = [] if background.type == TypeOptions.Data: - contrast_background_params.append([-1]) + contrast_background_param.append(project.data.index(background.source, True)) + if background.value_1 != "": + contrast_background_param.append(project.background_parameters.index(background.value_1)) + elif background.type == TypeOptions.Function: + contrast_background_param.append(project.custom_files.index(background.source, True)) + contrast_background_param.extend( + [ + project.background_parameters.index(value, True) + for value in [ + background.value_1, + background.value_2, + background.value_3, + background.value_4, + background.value_5, + ] + if value != "" + ] + ) + else: - contrast_background_params.append([project.background_parameters.index(background.source, True)]) + contrast_background_param.append(project.background_parameters.index(background.source, True)) + + contrast_background_params.append(contrast_background_param) # Set resolution parameters, with -1 used to indicate a data resolution all_data = [] @@ -412,27 +434,58 @@ def check_indices(problem: ProblemDefinition) -> None: "bulkOuts": "contrastBulkOuts", "scalefactors": "contrastScalefactors", "domainRatios": "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]) - ): + if len(param_list) > 0: elements = [ element for element in getattr(problem, index_list[params]) - if not ((element > 0 or element == -1) and element <= len(param_list)) + if (element != -1) and not (0 < element <= len(param_list)) ] + if elements: + raise IndexError( + f'The problem field "{index_list[params]}" contains: {", ".join(str(i) for i in elements)}' + f', which lie{"s"*(len(elements)==1)} outside of the range of "{params}"', + ) + + # backgroundParams has a different structure, so is handled separately: + # it is of type list[list[int]], where each list[int] is the indices for + # source, value_1, value_2, value_3, value_4, value_5 where they are defined + # e.g. for a data background with offset it is [source value_1], for a function + # with 3 values it is [source value_1 value_2 value_3], etc. + + source_param_lists = { + "constant": "backgroundParams", + "data": "data", + "function": "customFiles", + } + + for i, background_data in enumerate(problem.contrastBackgroundParams): + background_type = problem.contrastBackgroundTypes[i] + + # check source param is in range of the relevant parameter list + param_list = getattr(problem, source_param_lists[background_type]) + source_index = background_data[0] + if not 0 < source_index <= 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}"', + f'Entry {i} of contrastBackgroundParams has type "{background_type}" ' + f"and source index {source_index}, " + f'which is outside the range of "{source_param_lists[background_type]}".' ) + # check value params are in range for background params + if len(background_data) > 1: + elements = [element for element in background_data[1:] if not 0 < element <= len(problem.backgroundParams)] + if elements: + raise IndexError( + f'Entry {i} of contrastBackgroundParams contains: {", ".join(str(i) for i in elements)}' + f', which lie{"s"*(len(elements)==1)} outside of the range of "backgroundParams"', + ) + def make_controls(input_controls: RATapi.Controls) -> Control: """Converts the controls object to the format required by the compiled RAT code. diff --git a/tests/test_inputs.py b/tests/test_inputs.py index cc2c9216..e09f45ed 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -8,7 +8,7 @@ import RATapi import RATapi.wrappers -from RATapi.inputs import check_indices, make_controls, make_input, make_problem +from RATapi.inputs import FileHandles, check_indices, make_controls, make_input, make_problem from RATapi.rat_core import Checks, Control, Limits, Priors, ProblemDefinition from RATapi.utils.enums import ( BackgroundActions, @@ -133,6 +133,7 @@ def standard_layers_problem(): problem.contrastScalefactors = [1] problem.contrastBackgroundParams = [[1]] problem.contrastBackgroundActions = [BackgroundActions.Add] + problem.contrastBackgroundTypes = ["constant"] problem.contrastResolutionParams = [1] problem.contrastCustomFiles = [float("NaN")] problem.contrastDomainRatios = [0] @@ -155,6 +156,7 @@ def standard_layers_problem(): [6.2e-06, 6.35e-06], [0.01, 0.05], ] + problem.customFiles = FileHandles([]) return problem @@ -181,6 +183,7 @@ def domains_problem(): problem.contrastScalefactors = [1] problem.contrastBackgroundParams = [[1]] problem.contrastBackgroundActions = [BackgroundActions.Add] + problem.contrastBackgroundTypes = ["constant"] problem.contrastResolutionParams = [1] problem.contrastCustomFiles = [float("NaN")] problem.contrastDomainRatios = [1] @@ -204,6 +207,7 @@ def domains_problem(): [0.01, 0.05], [0.4, 0.6], ] + problem.customFiles = FileHandles([]) return problem @@ -230,6 +234,7 @@ def custom_xy_problem(): problem.contrastScalefactors = [1] problem.contrastBackgroundParams = [[1]] problem.contrastBackgroundActions = [BackgroundActions.Add] + problem.contrastBackgroundTypes = ["constant"] problem.contrastResolutionParams = [1] problem.contrastCustomFiles = [1] problem.contrastDomainRatios = [0] @@ -252,6 +257,9 @@ def custom_xy_problem(): [6.2e-06, 6.35e-06], [0.01, 0.05], ] + problem.customFiles = FileHandles( + [RATapi.models.CustomFile(name="Test Custom File", filename="cpp_test.dll", language="cpp")] + ) return problem @@ -552,81 +560,98 @@ def test_make_problem(test_project, test_problem, test_check, request) -> None: check_problem_equal(problem, test_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) - - 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]), - ], -) -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": "bulkIns", - "contrastBulkOuts": "bulkOuts", - "contrastScalefactors": "scalefactors", - "contrastDomainRatios": "domainRatios", - "contrastBackgroundParams": "backgroundParams", - "contrastResolutionParams": "resolutionParams", - } +@pytest.mark.parametrize("test_problem", ["standard_layers_problem", "custom_xy_problem", "domains_problem"]) +class TestCheckIndices: + """Tests for check_indices over a set of three test problems.""" - test_problem = request.getfixturevalue(test_problem) - setattr(test_problem, index_list, bad_value) + def test_check_indices(self, test_problem, request) -> None: + """The check_indices routine should not raise errors for a properly defined ProblemDefinition object.""" + test_problem = request.getfixturevalue(test_problem) - 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( + "index_list", + [ + "contrastBulkIns", + "contrastBulkOuts", + "contrastScalefactors", + "contrastDomainRatios", + "contrastResolutionParams", + ], + ) + @pytest.mark.parametrize("bad_value", ([0.0], [2.0])) + def test_check_indices_error(self, 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": "bulkIns", + "contrastBulkOuts": "bulkOuts", + "contrastScalefactors": "scalefactors", + "contrastDomainRatios": "domainRatios", + "contrastResolutionParams": "resolutionParams", + } + if (test_problem != "domains_problem") and (index_list == "contrastDomainRatios"): + # we expect this to not raise an error for non-domains problems as domainRatios is empty + pytest.xfail() + + 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 lies ' + f'outside of the range of "{param_list[index_list]}"', + ): + check_indices(test_problem) + + @pytest.mark.parametrize("background_type", ["constant", "data", "function"]) + @pytest.mark.parametrize("bad_value", ([[0.0]], [[2.0]])) + def test_background_params_source_indices(self, test_problem, background_type, bad_value, request): + """check_indices should raise an IndexError for bad sources in the nested list contrastBackgroundParams.""" + test_problem = request.getfixturevalue(test_problem) + test_problem.contrastBackgroundParams = bad_value + test_problem.contrastBackgroundTypes = [background_type] + + source_param_lists = { + "constant": "backgroundParams", + "data": "data", + "function": "customFiles", + } + + with pytest.raises( + IndexError, + match=f'Entry 0 of contrastBackgroundParams has type "{background_type}" ' + f"and source index {bad_value[0][0]}, " + f'which is outside the range of "{source_param_lists[background_type]}".', + ): + check_indices(test_problem) + + @pytest.mark.parametrize( + "bad_value", + ( + [[1.0, 0.0]], + [[1.0, 2.0]], + [[1.0, 1.0, 2.0]], + [[1.0], [1.0, 0.0]], + ), + ) + def test_background_params_value_indices(self, test_problem, bad_value, request): + """check_indices should raise an IndexError for bad values in the nested list contrastBackgroundParams.""" + test_problem = request.getfixturevalue(test_problem) + test_problem.contrastBackgroundParams = bad_value + + if len(bad_value) > 1: + test_problem.contrastBackgroundTypes.append("constant") + + with pytest.raises( + IndexError, + match=f"Entry {len(bad_value)-1} of contrastBackgroundParams contains: {bad_value[-1][-1]}" + f', which lies outside of the range of "backgroundParams"', + ): + check_indices(test_problem) + def test_get_python_handle(): path = pathlib.Path(__file__).parent.resolve() From 7bc5cf0418adcd8724026f1244d283d52dd4db2b Mon Sep 17 00:00:00 2001 From: alexhroom Date: Wed, 11 Dec 2024 14:09:10 +0000 Subject: [PATCH 06/15] data backgrounds add to contrast data --- RATapi/inputs.py | 79 +++++++++++++++++++++++++++++--------------- tests/test_inputs.py | 18 ++++++++++ 2 files changed, 71 insertions(+), 26 deletions(-) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index 01c7b6d3..795df364 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -214,10 +214,6 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: else: contrast_custom_files = [project.custom_files.index(contrast.model[0], True) for contrast in project.contrasts] - # Set background parameters, with -1 used to indicate a data background - contrast_background_params = [] - contrast_background_types = [] - # Get details of defined layers layer_details = [] for layer in project.layers: @@ -230,7 +226,32 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: layer_details.append(layer_params) + contrast_background_params = [] + contrast_background_types = [] + all_data = [] + data_limits = [] + simulation_limits = [] + contrast_resolution_params = [] + + # set data, background and resolution for each contrast for contrast in project.contrasts: + # set data + data_index = project.data.index(contrast.data) + data = project.data[data_index].data + data_range = project.data[data_index].data_range + simulation_range = project.data[data_index].simulation_range + + if data_range: + data_limits.append(data_range) + else: + data_limits.append([0.0, 0.0]) + + if simulation_range: + simulation_limits.append(simulation_range) + else: + simulation_limits.append([0.0, 0.0]) + + # set background parameters background = project.backgrounds[contrast.background] contrast_background_types.append(background.type) contrast_background_param = [] @@ -238,6 +259,9 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: contrast_background_param.append(project.data.index(background.source, True)) if background.value_1 != "": contrast_background_param.append(project.background_parameters.index(background.value_1)) + # if we are using a data background, we add the background data to the contrast data + data = append_data_background(data, project.data[background.source].data) + elif background.type == TypeOptions.Function: contrast_background_param.append(project.custom_files.index(background.source, True)) contrast_background_param.extend( @@ -259,35 +283,16 @@ def make_problem(project: RATapi.Project, checks: Checks) -> ProblemDefinition: contrast_background_params.append(contrast_background_param) - # Set resolution parameters, with -1 used to indicate a data resolution - all_data = [] - data_limits = [] - simulation_limits = [] - contrast_resolution_params = [] + # contrast data has exactly six columns to include background data if relevant + all_data.append(np.column_stack((data, np.zeros((data.shape[0], 6 - data.shape[1]))))) - for contrast in project.contrasts: + # Set resolution parameters, with -1 used to indicate a data resolution resolution = project.resolutions[contrast.resolution] if resolution.type == TypeOptions.Data: contrast_resolution_params.append(-1) else: contrast_resolution_params.append(project.resolution_parameters.index(resolution.source, True)) - data_index = project.data.index(contrast.data) - data = project.data[data_index].data - all_data.append(np.column_stack((data, np.zeros((data.shape[0], 6 - data.shape[1]))))) - data_range = project.data[data_index].data_range - simulation_range = project.data[data_index].simulation_range - - if data_range: - data_limits.append(data_range) - else: - data_limits.append([0.0, 0.0]) - - if simulation_range: - simulation_limits.append(simulation_range) - else: - simulation_limits.append([0.0, 0.0]) - problem = ProblemDefinition() problem.TF = project.calculation @@ -487,6 +492,28 @@ def check_indices(problem: ProblemDefinition) -> None: ) +def append_data_background(data: np.array, background: np.array) -> np.array: + """Add background data to contrast data. + + Parameters + ---------- + data : np.array + The contrast data to which we are adding a background. + background : np.array + The background data to add to the contrast. + + Returns + ------- + np.array + The contrast data with background data added as two additional columns. + + """ + if not np.allclose(data[:, 0], background[:, 0]): + raise ValueError("The q-values of the data and background must be equal.") + + return np.hstack((data, background[:, 1:])) + + def make_controls(input_controls: RATapi.Controls) -> Control: """Converts the controls object to the format required by the compiled RAT code. diff --git a/tests/test_inputs.py b/tests/test_inputs.py index e09f45ed..1328089a 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -653,6 +653,24 @@ def test_background_params_value_indices(self, test_problem, bad_value, request) check_indices(test_problem) +def test_append_data_background(): + """Test that background data is correctly added to contrast data.""" + data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + background = np.array([[1, 10, 11], [4, 12, 13], [7, 14, 15]]) + + result = RATapi.inputs.append_data_background(data, background) + np.testing.assert_allclose(result, np.array([[1, 2, 3, 10, 11], [4, 5, 6, 12, 13], [7, 8, 9, 14, 15]])) + + +def test_append_data_background_error(): + """Test that append_data_background raises an error if the q-values are not equal.""" + data = np.array([[1, 2, 3], [4, 5, 6], [7, 8, 9]]) + background = np.array([[56, 10, 11], [41, 12, 13], [7, 14, 15]]) + + with pytest.raises(ValueError, match=("The q-values of the data and background must be equal.")): + RATapi.inputs.append_data_background(data, background) + + def test_get_python_handle(): path = pathlib.Path(__file__).parent.resolve() assert RATapi.inputs.get_python_handle("utils.py", "dummy_function", path).__code__ == dummy_function.__code__ From e659a1f6f65ad983a4ba89d1aee7646ed7fac9fc Mon Sep 17 00:00:00 2001 From: alexhroom Date: Wed, 11 Dec 2024 14:40:53 +0000 Subject: [PATCH 07/15] added source/value wiping for type change --- RATapi/models.py | 18 ++++++++++++++++++ tests/test_models.py | 20 ++++++++++++++++++++ 2 files changed, 38 insertions(+) diff --git a/RATapi/models.py b/RATapi/models.py index aab06b59..8965d600 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -96,6 +96,15 @@ def warn_parameters(self): return self + def __setattr__(self, name, value): + if name == "type": + for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + super().__setattr__(attr, "") + + super().__setattr__(name, value) + class Contrast(RATModel): """A group of all of the components of a model. @@ -592,3 +601,12 @@ def warn_parameters(self): ) return self + + def __setattr__(self, name, value): + if name == "type": + for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + super().__setattr__(attr, "") + + super().__setattr__(name, value) diff --git a/tests/test_models.py b/tests/test_models.py index f207955d..3912804e 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -332,3 +332,23 @@ def test_contrast_bad_ratio(): ), ): RATapi.models.Contrast(name="My Contrast", domain_ratio="bad ratio") + + +@pytest.mark.parametrize("model", [RATapi.models.Background, RATapi.models.Resolution]) +def test_type_change_clear(model): + """If the type of a background or resolution is changed, it should wipe the other fields.""" + + model_instance = model( + name="Test", + type="constant", + source="src", + value_1="val1", + value_2="val2", + value_3="val3", + value_4="val4", + value_5="val5", + ) + + model_instance.type = "data" + for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: + assert getattr(model_instance, attr) == "" From 9a7c30467ce0b98c01ce49041dc2600b138ae7f6 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Wed, 11 Dec 2024 15:35:54 +0000 Subject: [PATCH 08/15] created shared parent class for backgrounds and resolutions --- RATapi/models.py | 58 ++++++++++++++++++++---------------------------- 1 file changed, 24 insertions(+), 34 deletions(-) diff --git a/RATapi/models.py b/RATapi/models.py index 8965d600..7b4b6c21 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -45,7 +45,29 @@ def __str__(self): return table.get_string() -class Background(RATModel): +class Signal(RATModel): + """Base model for background & resolution signals.""" + + name: str = Field(default="New Signal", min_length=1) + type: TypeOptions = TypeOptions.Constant + source: str = "" + value_1: str = "" + value_2: str = "" + value_3: str = "" + value_4: str = "" + value_5: str = "" + + def __setattr__(self, name, value): + if name == "type": + for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: + with warnings.catch_warnings(): + warnings.simplefilter("ignore") + super().__setattr__(attr, "") + + super().__setattr__(name, value) + + +class Background(Signal): """A background signal. Parameters @@ -68,13 +90,6 @@ class Background(RATModel): """ name: str = Field(default_factory=lambda: f"New Background {next(background_number)}", min_length=1) - type: TypeOptions = TypeOptions.Constant - source: str = "" - value_1: str = "" - value_2: str = "" - value_3: str = "" - value_4: str = "" - value_5: str = "" @model_validator(mode="after") def warn_parameters(self): @@ -96,15 +111,6 @@ def warn_parameters(self): return self - def __setattr__(self, name, value): - if name == "type": - for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__setattr__(attr, "") - - super().__setattr__(name, value) - class Contrast(RATModel): """A group of all of the components of a model. @@ -539,7 +545,7 @@ class ProtectedParameter(Parameter): name: str = Field(frozen=True, min_length=1) -class Resolution(RATModel): +class Resolution(Signal): """An instrument resolution. Parameters @@ -563,13 +569,6 @@ class Resolution(RATModel): """ name: str = Field(default_factory=lambda: f"New Resolution {next(resolution_number)}", min_length=1) - type: TypeOptions = TypeOptions.Constant - source: str = "" - value_1: str = "" - value_2: str = "" - value_3: str = "" - value_4: str = "" - value_5: str = "" @field_validator("type") @classmethod @@ -601,12 +600,3 @@ def warn_parameters(self): ) return self - - def __setattr__(self, name, value): - if name == "type": - for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: - with warnings.catch_warnings(): - warnings.simplefilter("ignore") - super().__setattr__(attr, "") - - super().__setattr__(name, value) From 5fd7390489e4b6e2f15b9e4227cfc96585d172c6 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Thu, 12 Dec 2024 13:30:43 +0000 Subject: [PATCH 09/15] review fixes to docstrings and data backgrounds --- RATapi/inputs.py | 13 ++----------- RATapi/models.py | 24 ++++++++++++------------ tests/test_inputs.py | 11 ++++++++++- 3 files changed, 24 insertions(+), 24 deletions(-) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index 795df364..268ecdd0 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -126,15 +126,6 @@ def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[Prob "background_parameters": "backgroundParams", "resolution_parameters": "resolutionParams", } - checks_field = { - "parameters": "params", - "bulk_in": "bulkIns", - "bulk_out": "bulkOuts", - "scalefactors": "scalefactors", - "domain_ratios": "domainRatios", - "background_parameters": "backgroundParams", - "resolution_parameters": "resolutionParams", - } prior_id = {"uniform": 1, "gaussian": 2, "jeffreys": 3} @@ -143,7 +134,7 @@ def make_input(project: RATapi.Project, controls: RATapi.Controls) -> tuple[Prob priors = Priors() for class_list in RATapi.project.parameter_class_lists: - setattr(checks, checks_field[class_list], [int(element.fit) for element in getattr(project, class_list)]) + setattr(checks, parameter_field[class_list], [int(element.fit) for element in getattr(project, class_list)]) setattr( limits, parameter_field[class_list], @@ -511,7 +502,7 @@ def append_data_background(data: np.array, background: np.array) -> np.array: if not np.allclose(data[:, 0], background[:, 0]): raise ValueError("The q-values of the data and background must be equal.") - return np.hstack((data, background[:, 1:])) + return np.hstack((data, np.zeros((data.shape[0], 4 - data.shape[1])), background[:, 1:])) def make_controls(input_controls: RATapi.Controls) -> Control: diff --git a/RATapi/models.py b/RATapi/models.py index 7b4b6c21..42114816 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -77,7 +77,7 @@ class Background(Signal): type : TypeOptions The type of background (constant, function or data) source : str - The source data for the background; + The source of the background; - if type is 'constant', this should be the name of a background parameter. - if type is 'data', this should be the name of a dataset defined in `Project.data`. - if type is 'function', this should be the name of a custom function defined in `Project.custom_files`. @@ -132,6 +132,7 @@ class Contrast(RATModel): The name of the bulk-out parameter which defines the SLD of the interface between the last layer and the environment. scalefactor : str + The name of the scalefactor which defines how much the data for this contrast should be scaled. resolution : str The name of the instrument resolution for this contrast. resample : bool @@ -139,8 +140,8 @@ class Contrast(RATModel): model : list[str] If this is a standard layers model, this should be a list of layer names that make up the slab model for this contrast. - For custom models, this should be a list of custom file names of the custom - model functions. + For custom models, this should be a list containing just the custom file name for the + custom model function. """ @@ -213,11 +214,13 @@ class ContrastWithRatio(RATModel): resample : bool Whether adaptive resampling should be used for interface microslicing. domain_ratio : str + The name of the domain ratio parameter describing how the first domain should be weighted + relative to the second. model : list[str] - If this is a standard layers model, this should be a list of layer names - that make up the slab model for this contrast. - For custom models, this should be a list of custom file names of the custom - model functions. + If this is a standard layers model, this should be a list of the names of the two domain contrasts + which make up the domain model for this contrast. + For custom models, this should be a list containing just the custom file name for the + custom model function. """ @@ -302,7 +305,7 @@ class Data(RATModel, arbitrary_types_allowed=True): name : str The name of this dataset. data : np.ndarray[np.float64] - The (x,y,z) data for this dataset, given as a Numpy array of three columns. + The (x, y, error) data for this dataset, given as a Numpy array of three columns. data_range : list[float] simulation_range : list[float] @@ -405,10 +408,7 @@ class DomainContrast(RATModel): name : str The name of this domain contrast. model : list[str] - If this is a standard layers model, this should be a list of layer names - that make up the slab model for this contrast. - For custom models, this should be a list of custom file names of the custom - model functions. + A list of layer names that make up the slab model for this contrast. """ diff --git a/tests/test_inputs.py b/tests/test_inputs.py index 1328089a..b725ff88 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -659,7 +659,16 @@ def test_append_data_background(): background = np.array([[1, 10, 11], [4, 12, 13], [7, 14, 15]]) result = RATapi.inputs.append_data_background(data, background) - np.testing.assert_allclose(result, np.array([[1, 2, 3, 10, 11], [4, 5, 6, 12, 13], [7, 8, 9, 14, 15]])) + np.testing.assert_allclose(result, np.array([[1, 2, 3, 0, 10, 11], [4, 5, 6, 0, 12, 13], [7, 8, 9, 0, 14, 15]])) + + +def test_append_data_background_res(): + """Test that background data is correctly added to contrast data when a resolution is in the data.""" + data = np.array([[1, 2, 3, 4], [4, 5, 6, 6], [7, 8, 9, 72]]) + background = np.array([[1, 10, 11], [4, 12, 13], [7, 14, 15]]) + + result = RATapi.inputs.append_data_background(data, background) + np.testing.assert_allclose(result, np.array([[1, 2, 3, 4, 10, 11], [4, 5, 6, 6, 12, 13], [7, 8, 9, 72, 14, 15]])) def test_append_data_background_error(): From 91942254eadfe2dab056459fa8fbdeac03f0748e Mon Sep 17 00:00:00 2001 From: alexhroom Date: Thu, 12 Dec 2024 14:51:25 +0000 Subject: [PATCH 10/15] reordered check_indices list to match packParams --- RATapi/inputs.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index 268ecdd0..4145b6c0 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -426,11 +426,11 @@ def check_indices(problem: ProblemDefinition) -> None: """ index_list = { + "scalefactors": "contrastScalefactors", "bulkIns": "contrastBulkIns", "bulkOuts": "contrastBulkOuts", - "scalefactors": "contrastScalefactors", - "domainRatios": "contrastDomainRatios", "resolutionParams": "contrastResolutionParams", + "domainRatios": "contrastDomainRatios", } # Check the indices -- note we have switched to 1-based indexing at this point From c8a2cf40b52bb0edc5497695e0f54002bef2c9f5 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Fri, 13 Dec 2024 14:49:08 +0000 Subject: [PATCH 11/15] added backgrounds to outputs --- RATapi/outputs.py | 3 + tests/conftest.py | 350 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 353 insertions(+) diff --git a/RATapi/outputs.py b/RATapi/outputs.py index 54ed27a2..bfd7f521 100644 --- a/RATapi/outputs.py +++ b/RATapi/outputs.py @@ -81,6 +81,7 @@ class Results: reflectivity: list simulation: list shiftedData: list + backgrounds: list layerSlds: list sldProfiles: list resampledLayers: list @@ -237,6 +238,7 @@ def make_results( reflectivity=output_results.reflectivity, simulation=output_results.simulation, shiftedData=output_results.shiftedData, + backgrounds=output_results.backgrounds, layerSlds=output_results.layerSlds, sldProfiles=output_results.sldProfiles, resampledLayers=output_results.resampledLayers, @@ -257,6 +259,7 @@ def make_results( reflectivity=output_results.reflectivity, simulation=output_results.simulation, shiftedData=output_results.shiftedData, + backgrounds=output_results.backgrounds, layerSlds=output_results.layerSlds, sldProfiles=output_results.sldProfiles, resampledLayers=output_results.resampledLayers, diff --git a/tests/conftest.py b/tests/conftest.py index 978e9d22..20a84855 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -417,6 +417,182 @@ def reflectivity_calculation_output_results(): ], ), ] + results.backgrounds = ( + [ + np.array( + [ + [1.1403e-02, 2.2300e-06, 0.0000e00], + [1.1973e-02, 2.2300e-06, 0.0000e00], + [1.2572e-02, 2.2300e-06, 0.0000e00], + [1.3201e-02, 2.2300e-06, 0.0000e00], + [1.3861e-02, 2.2300e-06, 0.0000e00], + [1.4554e-02, 2.2300e-06, 0.0000e00], + [1.5281e-02, 2.2300e-06, 0.0000e00], + [1.6045e-02, 2.2300e-06, 0.0000e00], + [1.6848e-02, 2.2300e-06, 0.0000e00], + [1.7690e-02, 2.2300e-06, 0.0000e00], + [1.8575e-02, 2.2300e-06, 0.0000e00], + [1.9503e-02, 2.2300e-06, 0.0000e00], + [2.0479e-02, 2.2300e-06, 0.0000e00], + [2.1502e-02, 2.2300e-06, 0.0000e00], + [2.2578e-02, 2.2300e-06, 0.0000e00], + [2.3706e-02, 2.2300e-06, 0.0000e00], + [2.4892e-02, 2.2300e-06, 0.0000e00], + [2.6136e-02, 2.2300e-06, 0.0000e00], + [2.7443e-02, 2.2300e-06, 0.0000e00], + [2.8815e-02, 2.2300e-06, 0.0000e00], + [3.0256e-02, 2.2300e-06, 0.0000e00], + [3.1769e-02, 2.2300e-06, 0.0000e00], + [3.3357e-02, 2.2300e-06, 0.0000e00], + [3.5025e-02, 2.2300e-06, 0.0000e00], + [3.6777e-02, 2.2300e-06, 0.0000e00], + [3.8615e-02, 2.2300e-06, 0.0000e00], + [4.0546e-02, 2.2300e-06, 0.0000e00], + [4.2573e-02, 2.2300e-06, 0.0000e00], + [4.4702e-02, 2.2300e-06, 0.0000e00], + [4.6937e-02, 2.2300e-06, 0.0000e00], + [4.9284e-02, 2.2300e-06, 0.0000e00], + [5.1748e-02, 2.2300e-06, 0.0000e00], + [5.4336e-02, 2.2300e-06, 0.0000e00], + [5.7052e-02, 2.2300e-06, 0.0000e00], + [5.9905e-02, 2.2300e-06, 0.0000e00], + [6.2900e-02, 2.2300e-06, 0.0000e00], + [6.6045e-02, 2.2300e-06, 0.0000e00], + [6.9348e-02, 2.2300e-06, 0.0000e00], + [7.2815e-02, 2.2300e-06, 0.0000e00], + [7.6456e-02, 2.2300e-06, 0.0000e00], + [8.0279e-02, 2.2300e-06, 0.0000e00], + [8.4292e-02, 2.2300e-06, 0.0000e00], + [8.8507e-02, 2.2300e-06, 0.0000e00], + [9.2932e-02, 2.2300e-06, 0.0000e00], + [9.7579e-02, 2.2300e-06, 0.0000e00], + [1.0246e-01, 2.2300e-06, 0.0000e00], + [1.0758e-01, 2.2300e-06, 0.0000e00], + [1.1296e-01, 2.2300e-06, 0.0000e00], + [1.1861e-01, 2.2300e-06, 0.0000e00], + [1.2454e-01, 2.2300e-06, 0.0000e00], + [1.3077e-01, 2.2300e-06, 0.0000e00], + [1.3730e-01, 2.2300e-06, 0.0000e00], + [1.4417e-01, 2.2300e-06, 0.0000e00], + [1.5138e-01, 2.2300e-06, 0.0000e00], + [1.5895e-01, 2.2300e-06, 0.0000e00], + [1.6689e-01, 2.2300e-06, 0.0000e00], + [1.7524e-01, 2.2300e-06, 0.0000e00], + [1.8400e-01, 2.2300e-06, 0.0000e00], + [1.9320e-01, 2.2300e-06, 0.0000e00], + [2.0286e-01, 2.2300e-06, 0.0000e00], + [2.1300e-01, 2.2300e-06, 0.0000e00], + [2.2365e-01, 2.2300e-06, 0.0000e00], + [2.3484e-01, 2.2300e-06, 0.0000e00], + [2.4658e-01, 2.2300e-06, 0.0000e00], + [2.5891e-01, 2.2300e-06, 0.0000e00], + [2.7185e-01, 2.2300e-06, 0.0000e00], + [2.8544e-01, 2.2300e-06, 0.0000e00], + [2.9972e-01, 2.2300e-06, 0.0000e00], + [3.1470e-01, 2.2300e-06, 0.0000e00], + [3.3044e-01, 2.2300e-06, 0.0000e00], + [3.4696e-01, 2.2300e-06, 0.0000e00], + [3.6431e-01, 2.2300e-06, 0.0000e00], + [3.8252e-01, 2.2300e-06, 0.0000e00], + [4.0165e-01, 2.2300e-06, 0.0000e00], + [4.2173e-01, 2.2300e-06, 0.0000e00], + [4.4282e-01, 2.2300e-06, 0.0000e00], + [4.6496e-01, 2.2300e-06, 0.0000e00], + [4.8821e-01, 2.2300e-06, 0.0000e00], + [5.1262e-01, 2.2300e-06, 0.0000e00], + [5.3825e-01, 2.2300e-06, 0.0000e00], + [5.6516e-01, 2.2300e-06, 0.0000e00], + [5.9342e-01, 2.2300e-06, 0.0000e00], + ] + ), + np.array( + [ + [1.1403e-02, 3.3800e-06, 0.0000e00], + [1.1973e-02, 3.3800e-06, 0.0000e00], + [1.2572e-02, 3.3800e-06, 0.0000e00], + [1.3201e-02, 3.3800e-06, 0.0000e00], + [1.3861e-02, 3.3800e-06, 0.0000e00], + [1.4554e-02, 3.3800e-06, 0.0000e00], + [1.5281e-02, 3.3800e-06, 0.0000e00], + [1.6045e-02, 3.3800e-06, 0.0000e00], + [1.6848e-02, 3.3800e-06, 0.0000e00], + [1.7690e-02, 3.3800e-06, 0.0000e00], + [1.8575e-02, 3.3800e-06, 0.0000e00], + [1.9503e-02, 3.3800e-06, 0.0000e00], + [2.0479e-02, 3.3800e-06, 0.0000e00], + [2.1502e-02, 3.3800e-06, 0.0000e00], + [2.2578e-02, 3.3800e-06, 0.0000e00], + [2.3706e-02, 3.3800e-06, 0.0000e00], + [2.4892e-02, 3.3800e-06, 0.0000e00], + [2.6136e-02, 3.3800e-06, 0.0000e00], + [2.7443e-02, 3.3800e-06, 0.0000e00], + [2.8815e-02, 3.3800e-06, 0.0000e00], + [3.0256e-02, 3.3800e-06, 0.0000e00], + [3.1769e-02, 3.3800e-06, 0.0000e00], + [3.3357e-02, 3.3800e-06, 0.0000e00], + [3.5025e-02, 3.3800e-06, 0.0000e00], + [3.6777e-02, 3.3800e-06, 0.0000e00], + [3.8615e-02, 3.3800e-06, 0.0000e00], + [4.0546e-02, 3.3800e-06, 0.0000e00], + [4.2573e-02, 3.3800e-06, 0.0000e00], + [4.4702e-02, 3.3800e-06, 0.0000e00], + [4.6937e-02, 3.3800e-06, 0.0000e00], + [4.9284e-02, 3.3800e-06, 0.0000e00], + [5.1748e-02, 3.3800e-06, 0.0000e00], + [5.4336e-02, 3.3800e-06, 0.0000e00], + [5.7052e-02, 3.3800e-06, 0.0000e00], + [5.9905e-02, 3.3800e-06, 0.0000e00], + [6.2900e-02, 3.3800e-06, 0.0000e00], + [6.6045e-02, 3.3800e-06, 0.0000e00], + [6.9348e-02, 3.3800e-06, 0.0000e00], + [7.2815e-02, 3.3800e-06, 0.0000e00], + [7.6456e-02, 3.3800e-06, 0.0000e00], + [8.0279e-02, 3.3800e-06, 0.0000e00], + [8.4292e-02, 3.3800e-06, 0.0000e00], + [8.8507e-02, 3.3800e-06, 0.0000e00], + [9.2932e-02, 3.3800e-06, 0.0000e00], + [9.7579e-02, 3.3800e-06, 0.0000e00], + [1.0246e-01, 3.3800e-06, 0.0000e00], + [1.0758e-01, 3.3800e-06, 0.0000e00], + [1.1296e-01, 3.3800e-06, 0.0000e00], + [1.1861e-01, 3.3800e-06, 0.0000e00], + [1.2454e-01, 3.3800e-06, 0.0000e00], + [1.3077e-01, 3.3800e-06, 0.0000e00], + [1.3730e-01, 3.3800e-06, 0.0000e00], + [1.4417e-01, 3.3800e-06, 0.0000e00], + [1.5138e-01, 3.3800e-06, 0.0000e00], + [1.5895e-01, 3.3800e-06, 0.0000e00], + [1.6689e-01, 3.3800e-06, 0.0000e00], + [1.7524e-01, 3.3800e-06, 0.0000e00], + [1.8400e-01, 3.3800e-06, 0.0000e00], + [1.9320e-01, 3.3800e-06, 0.0000e00], + [2.0286e-01, 3.3800e-06, 0.0000e00], + [2.1300e-01, 3.3800e-06, 0.0000e00], + [2.2365e-01, 3.3800e-06, 0.0000e00], + [2.3484e-01, 3.3800e-06, 0.0000e00], + [2.4658e-01, 3.3800e-06, 0.0000e00], + [2.5891e-01, 3.3800e-06, 0.0000e00], + [2.7185e-01, 3.3800e-06, 0.0000e00], + [2.8544e-01, 3.3800e-06, 0.0000e00], + [2.9972e-01, 3.3800e-06, 0.0000e00], + [3.1470e-01, 3.3800e-06, 0.0000e00], + [3.3044e-01, 3.3800e-06, 0.0000e00], + [3.4696e-01, 3.3800e-06, 0.0000e00], + [3.6431e-01, 3.3800e-06, 0.0000e00], + [3.8252e-01, 3.3800e-06, 0.0000e00], + [4.0165e-01, 3.3800e-06, 0.0000e00], + [4.2173e-01, 3.3800e-06, 0.0000e00], + [4.4282e-01, 3.3800e-06, 0.0000e00], + [4.6496e-01, 3.3800e-06, 0.0000e00], + [4.8821e-01, 3.3800e-06, 0.0000e00], + [5.1262e-01, 3.3800e-06, 0.0000e00], + [5.3825e-01, 3.3800e-06, 0.0000e00], + [5.6516e-01, 3.3800e-06, 0.0000e00], + [5.9342e-01, 3.3800e-06, 0.0000e00], + ] + ), + ], + ) results.layerSlds = [ [ np.array( @@ -726,6 +902,180 @@ def reflectivity_calculation_results(): ], ), ], + backgrounds=[ + np.array( + [ + [1.1403e-02, 2.2300e-06, 0.0000e00], + [1.1973e-02, 2.2300e-06, 0.0000e00], + [1.2572e-02, 2.2300e-06, 0.0000e00], + [1.3201e-02, 2.2300e-06, 0.0000e00], + [1.3861e-02, 2.2300e-06, 0.0000e00], + [1.4554e-02, 2.2300e-06, 0.0000e00], + [1.5281e-02, 2.2300e-06, 0.0000e00], + [1.6045e-02, 2.2300e-06, 0.0000e00], + [1.6848e-02, 2.2300e-06, 0.0000e00], + [1.7690e-02, 2.2300e-06, 0.0000e00], + [1.8575e-02, 2.2300e-06, 0.0000e00], + [1.9503e-02, 2.2300e-06, 0.0000e00], + [2.0479e-02, 2.2300e-06, 0.0000e00], + [2.1502e-02, 2.2300e-06, 0.0000e00], + [2.2578e-02, 2.2300e-06, 0.0000e00], + [2.3706e-02, 2.2300e-06, 0.0000e00], + [2.4892e-02, 2.2300e-06, 0.0000e00], + [2.6136e-02, 2.2300e-06, 0.0000e00], + [2.7443e-02, 2.2300e-06, 0.0000e00], + [2.8815e-02, 2.2300e-06, 0.0000e00], + [3.0256e-02, 2.2300e-06, 0.0000e00], + [3.1769e-02, 2.2300e-06, 0.0000e00], + [3.3357e-02, 2.2300e-06, 0.0000e00], + [3.5025e-02, 2.2300e-06, 0.0000e00], + [3.6777e-02, 2.2300e-06, 0.0000e00], + [3.8615e-02, 2.2300e-06, 0.0000e00], + [4.0546e-02, 2.2300e-06, 0.0000e00], + [4.2573e-02, 2.2300e-06, 0.0000e00], + [4.4702e-02, 2.2300e-06, 0.0000e00], + [4.6937e-02, 2.2300e-06, 0.0000e00], + [4.9284e-02, 2.2300e-06, 0.0000e00], + [5.1748e-02, 2.2300e-06, 0.0000e00], + [5.4336e-02, 2.2300e-06, 0.0000e00], + [5.7052e-02, 2.2300e-06, 0.0000e00], + [5.9905e-02, 2.2300e-06, 0.0000e00], + [6.2900e-02, 2.2300e-06, 0.0000e00], + [6.6045e-02, 2.2300e-06, 0.0000e00], + [6.9348e-02, 2.2300e-06, 0.0000e00], + [7.2815e-02, 2.2300e-06, 0.0000e00], + [7.6456e-02, 2.2300e-06, 0.0000e00], + [8.0279e-02, 2.2300e-06, 0.0000e00], + [8.4292e-02, 2.2300e-06, 0.0000e00], + [8.8507e-02, 2.2300e-06, 0.0000e00], + [9.2932e-02, 2.2300e-06, 0.0000e00], + [9.7579e-02, 2.2300e-06, 0.0000e00], + [1.0246e-01, 2.2300e-06, 0.0000e00], + [1.0758e-01, 2.2300e-06, 0.0000e00], + [1.1296e-01, 2.2300e-06, 0.0000e00], + [1.1861e-01, 2.2300e-06, 0.0000e00], + [1.2454e-01, 2.2300e-06, 0.0000e00], + [1.3077e-01, 2.2300e-06, 0.0000e00], + [1.3730e-01, 2.2300e-06, 0.0000e00], + [1.4417e-01, 2.2300e-06, 0.0000e00], + [1.5138e-01, 2.2300e-06, 0.0000e00], + [1.5895e-01, 2.2300e-06, 0.0000e00], + [1.6689e-01, 2.2300e-06, 0.0000e00], + [1.7524e-01, 2.2300e-06, 0.0000e00], + [1.8400e-01, 2.2300e-06, 0.0000e00], + [1.9320e-01, 2.2300e-06, 0.0000e00], + [2.0286e-01, 2.2300e-06, 0.0000e00], + [2.1300e-01, 2.2300e-06, 0.0000e00], + [2.2365e-01, 2.2300e-06, 0.0000e00], + [2.3484e-01, 2.2300e-06, 0.0000e00], + [2.4658e-01, 2.2300e-06, 0.0000e00], + [2.5891e-01, 2.2300e-06, 0.0000e00], + [2.7185e-01, 2.2300e-06, 0.0000e00], + [2.8544e-01, 2.2300e-06, 0.0000e00], + [2.9972e-01, 2.2300e-06, 0.0000e00], + [3.1470e-01, 2.2300e-06, 0.0000e00], + [3.3044e-01, 2.2300e-06, 0.0000e00], + [3.4696e-01, 2.2300e-06, 0.0000e00], + [3.6431e-01, 2.2300e-06, 0.0000e00], + [3.8252e-01, 2.2300e-06, 0.0000e00], + [4.0165e-01, 2.2300e-06, 0.0000e00], + [4.2173e-01, 2.2300e-06, 0.0000e00], + [4.4282e-01, 2.2300e-06, 0.0000e00], + [4.6496e-01, 2.2300e-06, 0.0000e00], + [4.8821e-01, 2.2300e-06, 0.0000e00], + [5.1262e-01, 2.2300e-06, 0.0000e00], + [5.3825e-01, 2.2300e-06, 0.0000e00], + [5.6516e-01, 2.2300e-06, 0.0000e00], + [5.9342e-01, 2.2300e-06, 0.0000e00], + ] + ), + np.array( + [ + [1.1403e-02, 3.3800e-06, 0.0000e00], + [1.1973e-02, 3.3800e-06, 0.0000e00], + [1.2572e-02, 3.3800e-06, 0.0000e00], + [1.3201e-02, 3.3800e-06, 0.0000e00], + [1.3861e-02, 3.3800e-06, 0.0000e00], + [1.4554e-02, 3.3800e-06, 0.0000e00], + [1.5281e-02, 3.3800e-06, 0.0000e00], + [1.6045e-02, 3.3800e-06, 0.0000e00], + [1.6848e-02, 3.3800e-06, 0.0000e00], + [1.7690e-02, 3.3800e-06, 0.0000e00], + [1.8575e-02, 3.3800e-06, 0.0000e00], + [1.9503e-02, 3.3800e-06, 0.0000e00], + [2.0479e-02, 3.3800e-06, 0.0000e00], + [2.1502e-02, 3.3800e-06, 0.0000e00], + [2.2578e-02, 3.3800e-06, 0.0000e00], + [2.3706e-02, 3.3800e-06, 0.0000e00], + [2.4892e-02, 3.3800e-06, 0.0000e00], + [2.6136e-02, 3.3800e-06, 0.0000e00], + [2.7443e-02, 3.3800e-06, 0.0000e00], + [2.8815e-02, 3.3800e-06, 0.0000e00], + [3.0256e-02, 3.3800e-06, 0.0000e00], + [3.1769e-02, 3.3800e-06, 0.0000e00], + [3.3357e-02, 3.3800e-06, 0.0000e00], + [3.5025e-02, 3.3800e-06, 0.0000e00], + [3.6777e-02, 3.3800e-06, 0.0000e00], + [3.8615e-02, 3.3800e-06, 0.0000e00], + [4.0546e-02, 3.3800e-06, 0.0000e00], + [4.2573e-02, 3.3800e-06, 0.0000e00], + [4.4702e-02, 3.3800e-06, 0.0000e00], + [4.6937e-02, 3.3800e-06, 0.0000e00], + [4.9284e-02, 3.3800e-06, 0.0000e00], + [5.1748e-02, 3.3800e-06, 0.0000e00], + [5.4336e-02, 3.3800e-06, 0.0000e00], + [5.7052e-02, 3.3800e-06, 0.0000e00], + [5.9905e-02, 3.3800e-06, 0.0000e00], + [6.2900e-02, 3.3800e-06, 0.0000e00], + [6.6045e-02, 3.3800e-06, 0.0000e00], + [6.9348e-02, 3.3800e-06, 0.0000e00], + [7.2815e-02, 3.3800e-06, 0.0000e00], + [7.6456e-02, 3.3800e-06, 0.0000e00], + [8.0279e-02, 3.3800e-06, 0.0000e00], + [8.4292e-02, 3.3800e-06, 0.0000e00], + [8.8507e-02, 3.3800e-06, 0.0000e00], + [9.2932e-02, 3.3800e-06, 0.0000e00], + [9.7579e-02, 3.3800e-06, 0.0000e00], + [1.0246e-01, 3.3800e-06, 0.0000e00], + [1.0758e-01, 3.3800e-06, 0.0000e00], + [1.1296e-01, 3.3800e-06, 0.0000e00], + [1.1861e-01, 3.3800e-06, 0.0000e00], + [1.2454e-01, 3.3800e-06, 0.0000e00], + [1.3077e-01, 3.3800e-06, 0.0000e00], + [1.3730e-01, 3.3800e-06, 0.0000e00], + [1.4417e-01, 3.3800e-06, 0.0000e00], + [1.5138e-01, 3.3800e-06, 0.0000e00], + [1.5895e-01, 3.3800e-06, 0.0000e00], + [1.6689e-01, 3.3800e-06, 0.0000e00], + [1.7524e-01, 3.3800e-06, 0.0000e00], + [1.8400e-01, 3.3800e-06, 0.0000e00], + [1.9320e-01, 3.3800e-06, 0.0000e00], + [2.0286e-01, 3.3800e-06, 0.0000e00], + [2.1300e-01, 3.3800e-06, 0.0000e00], + [2.2365e-01, 3.3800e-06, 0.0000e00], + [2.3484e-01, 3.3800e-06, 0.0000e00], + [2.4658e-01, 3.3800e-06, 0.0000e00], + [2.5891e-01, 3.3800e-06, 0.0000e00], + [2.7185e-01, 3.3800e-06, 0.0000e00], + [2.8544e-01, 3.3800e-06, 0.0000e00], + [2.9972e-01, 3.3800e-06, 0.0000e00], + [3.1470e-01, 3.3800e-06, 0.0000e00], + [3.3044e-01, 3.3800e-06, 0.0000e00], + [3.4696e-01, 3.3800e-06, 0.0000e00], + [3.6431e-01, 3.3800e-06, 0.0000e00], + [3.8252e-01, 3.3800e-06, 0.0000e00], + [4.0165e-01, 3.3800e-06, 0.0000e00], + [4.2173e-01, 3.3800e-06, 0.0000e00], + [4.4282e-01, 3.3800e-06, 0.0000e00], + [4.6496e-01, 3.3800e-06, 0.0000e00], + [4.8821e-01, 3.3800e-06, 0.0000e00], + [5.1262e-01, 3.3800e-06, 0.0000e00], + [5.3825e-01, 3.3800e-06, 0.0000e00], + [5.6516e-01, 3.3800e-06, 0.0000e00], + [5.9342e-01, 3.3800e-06, 0.0000e00], + ] + ), + ], layerSlds=[ [ np.array( From 9d08b459f23bf47870c3ea0923c3616c04f438c5 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Tue, 7 Jan 2025 12:43:15 +0000 Subject: [PATCH 12/15] updated tests --- tests/conftest.py | 524 ++++++++++++++++++++++++++++-------------- tests/test_outputs.py | 2 + 2 files changed, 350 insertions(+), 176 deletions(-) diff --git a/tests/conftest.py b/tests/conftest.py index 20a84855..ecd9ddd6 100644 --- a/tests/conftest.py +++ b/tests/conftest.py @@ -417,182 +417,180 @@ def reflectivity_calculation_output_results(): ], ), ] - results.backgrounds = ( - [ - np.array( - [ - [1.1403e-02, 2.2300e-06, 0.0000e00], - [1.1973e-02, 2.2300e-06, 0.0000e00], - [1.2572e-02, 2.2300e-06, 0.0000e00], - [1.3201e-02, 2.2300e-06, 0.0000e00], - [1.3861e-02, 2.2300e-06, 0.0000e00], - [1.4554e-02, 2.2300e-06, 0.0000e00], - [1.5281e-02, 2.2300e-06, 0.0000e00], - [1.6045e-02, 2.2300e-06, 0.0000e00], - [1.6848e-02, 2.2300e-06, 0.0000e00], - [1.7690e-02, 2.2300e-06, 0.0000e00], - [1.8575e-02, 2.2300e-06, 0.0000e00], - [1.9503e-02, 2.2300e-06, 0.0000e00], - [2.0479e-02, 2.2300e-06, 0.0000e00], - [2.1502e-02, 2.2300e-06, 0.0000e00], - [2.2578e-02, 2.2300e-06, 0.0000e00], - [2.3706e-02, 2.2300e-06, 0.0000e00], - [2.4892e-02, 2.2300e-06, 0.0000e00], - [2.6136e-02, 2.2300e-06, 0.0000e00], - [2.7443e-02, 2.2300e-06, 0.0000e00], - [2.8815e-02, 2.2300e-06, 0.0000e00], - [3.0256e-02, 2.2300e-06, 0.0000e00], - [3.1769e-02, 2.2300e-06, 0.0000e00], - [3.3357e-02, 2.2300e-06, 0.0000e00], - [3.5025e-02, 2.2300e-06, 0.0000e00], - [3.6777e-02, 2.2300e-06, 0.0000e00], - [3.8615e-02, 2.2300e-06, 0.0000e00], - [4.0546e-02, 2.2300e-06, 0.0000e00], - [4.2573e-02, 2.2300e-06, 0.0000e00], - [4.4702e-02, 2.2300e-06, 0.0000e00], - [4.6937e-02, 2.2300e-06, 0.0000e00], - [4.9284e-02, 2.2300e-06, 0.0000e00], - [5.1748e-02, 2.2300e-06, 0.0000e00], - [5.4336e-02, 2.2300e-06, 0.0000e00], - [5.7052e-02, 2.2300e-06, 0.0000e00], - [5.9905e-02, 2.2300e-06, 0.0000e00], - [6.2900e-02, 2.2300e-06, 0.0000e00], - [6.6045e-02, 2.2300e-06, 0.0000e00], - [6.9348e-02, 2.2300e-06, 0.0000e00], - [7.2815e-02, 2.2300e-06, 0.0000e00], - [7.6456e-02, 2.2300e-06, 0.0000e00], - [8.0279e-02, 2.2300e-06, 0.0000e00], - [8.4292e-02, 2.2300e-06, 0.0000e00], - [8.8507e-02, 2.2300e-06, 0.0000e00], - [9.2932e-02, 2.2300e-06, 0.0000e00], - [9.7579e-02, 2.2300e-06, 0.0000e00], - [1.0246e-01, 2.2300e-06, 0.0000e00], - [1.0758e-01, 2.2300e-06, 0.0000e00], - [1.1296e-01, 2.2300e-06, 0.0000e00], - [1.1861e-01, 2.2300e-06, 0.0000e00], - [1.2454e-01, 2.2300e-06, 0.0000e00], - [1.3077e-01, 2.2300e-06, 0.0000e00], - [1.3730e-01, 2.2300e-06, 0.0000e00], - [1.4417e-01, 2.2300e-06, 0.0000e00], - [1.5138e-01, 2.2300e-06, 0.0000e00], - [1.5895e-01, 2.2300e-06, 0.0000e00], - [1.6689e-01, 2.2300e-06, 0.0000e00], - [1.7524e-01, 2.2300e-06, 0.0000e00], - [1.8400e-01, 2.2300e-06, 0.0000e00], - [1.9320e-01, 2.2300e-06, 0.0000e00], - [2.0286e-01, 2.2300e-06, 0.0000e00], - [2.1300e-01, 2.2300e-06, 0.0000e00], - [2.2365e-01, 2.2300e-06, 0.0000e00], - [2.3484e-01, 2.2300e-06, 0.0000e00], - [2.4658e-01, 2.2300e-06, 0.0000e00], - [2.5891e-01, 2.2300e-06, 0.0000e00], - [2.7185e-01, 2.2300e-06, 0.0000e00], - [2.8544e-01, 2.2300e-06, 0.0000e00], - [2.9972e-01, 2.2300e-06, 0.0000e00], - [3.1470e-01, 2.2300e-06, 0.0000e00], - [3.3044e-01, 2.2300e-06, 0.0000e00], - [3.4696e-01, 2.2300e-06, 0.0000e00], - [3.6431e-01, 2.2300e-06, 0.0000e00], - [3.8252e-01, 2.2300e-06, 0.0000e00], - [4.0165e-01, 2.2300e-06, 0.0000e00], - [4.2173e-01, 2.2300e-06, 0.0000e00], - [4.4282e-01, 2.2300e-06, 0.0000e00], - [4.6496e-01, 2.2300e-06, 0.0000e00], - [4.8821e-01, 2.2300e-06, 0.0000e00], - [5.1262e-01, 2.2300e-06, 0.0000e00], - [5.3825e-01, 2.2300e-06, 0.0000e00], - [5.6516e-01, 2.2300e-06, 0.0000e00], - [5.9342e-01, 2.2300e-06, 0.0000e00], - ] - ), - np.array( - [ - [1.1403e-02, 3.3800e-06, 0.0000e00], - [1.1973e-02, 3.3800e-06, 0.0000e00], - [1.2572e-02, 3.3800e-06, 0.0000e00], - [1.3201e-02, 3.3800e-06, 0.0000e00], - [1.3861e-02, 3.3800e-06, 0.0000e00], - [1.4554e-02, 3.3800e-06, 0.0000e00], - [1.5281e-02, 3.3800e-06, 0.0000e00], - [1.6045e-02, 3.3800e-06, 0.0000e00], - [1.6848e-02, 3.3800e-06, 0.0000e00], - [1.7690e-02, 3.3800e-06, 0.0000e00], - [1.8575e-02, 3.3800e-06, 0.0000e00], - [1.9503e-02, 3.3800e-06, 0.0000e00], - [2.0479e-02, 3.3800e-06, 0.0000e00], - [2.1502e-02, 3.3800e-06, 0.0000e00], - [2.2578e-02, 3.3800e-06, 0.0000e00], - [2.3706e-02, 3.3800e-06, 0.0000e00], - [2.4892e-02, 3.3800e-06, 0.0000e00], - [2.6136e-02, 3.3800e-06, 0.0000e00], - [2.7443e-02, 3.3800e-06, 0.0000e00], - [2.8815e-02, 3.3800e-06, 0.0000e00], - [3.0256e-02, 3.3800e-06, 0.0000e00], - [3.1769e-02, 3.3800e-06, 0.0000e00], - [3.3357e-02, 3.3800e-06, 0.0000e00], - [3.5025e-02, 3.3800e-06, 0.0000e00], - [3.6777e-02, 3.3800e-06, 0.0000e00], - [3.8615e-02, 3.3800e-06, 0.0000e00], - [4.0546e-02, 3.3800e-06, 0.0000e00], - [4.2573e-02, 3.3800e-06, 0.0000e00], - [4.4702e-02, 3.3800e-06, 0.0000e00], - [4.6937e-02, 3.3800e-06, 0.0000e00], - [4.9284e-02, 3.3800e-06, 0.0000e00], - [5.1748e-02, 3.3800e-06, 0.0000e00], - [5.4336e-02, 3.3800e-06, 0.0000e00], - [5.7052e-02, 3.3800e-06, 0.0000e00], - [5.9905e-02, 3.3800e-06, 0.0000e00], - [6.2900e-02, 3.3800e-06, 0.0000e00], - [6.6045e-02, 3.3800e-06, 0.0000e00], - [6.9348e-02, 3.3800e-06, 0.0000e00], - [7.2815e-02, 3.3800e-06, 0.0000e00], - [7.6456e-02, 3.3800e-06, 0.0000e00], - [8.0279e-02, 3.3800e-06, 0.0000e00], - [8.4292e-02, 3.3800e-06, 0.0000e00], - [8.8507e-02, 3.3800e-06, 0.0000e00], - [9.2932e-02, 3.3800e-06, 0.0000e00], - [9.7579e-02, 3.3800e-06, 0.0000e00], - [1.0246e-01, 3.3800e-06, 0.0000e00], - [1.0758e-01, 3.3800e-06, 0.0000e00], - [1.1296e-01, 3.3800e-06, 0.0000e00], - [1.1861e-01, 3.3800e-06, 0.0000e00], - [1.2454e-01, 3.3800e-06, 0.0000e00], - [1.3077e-01, 3.3800e-06, 0.0000e00], - [1.3730e-01, 3.3800e-06, 0.0000e00], - [1.4417e-01, 3.3800e-06, 0.0000e00], - [1.5138e-01, 3.3800e-06, 0.0000e00], - [1.5895e-01, 3.3800e-06, 0.0000e00], - [1.6689e-01, 3.3800e-06, 0.0000e00], - [1.7524e-01, 3.3800e-06, 0.0000e00], - [1.8400e-01, 3.3800e-06, 0.0000e00], - [1.9320e-01, 3.3800e-06, 0.0000e00], - [2.0286e-01, 3.3800e-06, 0.0000e00], - [2.1300e-01, 3.3800e-06, 0.0000e00], - [2.2365e-01, 3.3800e-06, 0.0000e00], - [2.3484e-01, 3.3800e-06, 0.0000e00], - [2.4658e-01, 3.3800e-06, 0.0000e00], - [2.5891e-01, 3.3800e-06, 0.0000e00], - [2.7185e-01, 3.3800e-06, 0.0000e00], - [2.8544e-01, 3.3800e-06, 0.0000e00], - [2.9972e-01, 3.3800e-06, 0.0000e00], - [3.1470e-01, 3.3800e-06, 0.0000e00], - [3.3044e-01, 3.3800e-06, 0.0000e00], - [3.4696e-01, 3.3800e-06, 0.0000e00], - [3.6431e-01, 3.3800e-06, 0.0000e00], - [3.8252e-01, 3.3800e-06, 0.0000e00], - [4.0165e-01, 3.3800e-06, 0.0000e00], - [4.2173e-01, 3.3800e-06, 0.0000e00], - [4.4282e-01, 3.3800e-06, 0.0000e00], - [4.6496e-01, 3.3800e-06, 0.0000e00], - [4.8821e-01, 3.3800e-06, 0.0000e00], - [5.1262e-01, 3.3800e-06, 0.0000e00], - [5.3825e-01, 3.3800e-06, 0.0000e00], - [5.6516e-01, 3.3800e-06, 0.0000e00], - [5.9342e-01, 3.3800e-06, 0.0000e00], - ] - ), - ], - ) + results.backgrounds = [ + np.array( + [ + [1.1403e-02, 2.2300e-06, 0.0000e00], + [1.1973e-02, 2.2300e-06, 0.0000e00], + [1.2572e-02, 2.2300e-06, 0.0000e00], + [1.3201e-02, 2.2300e-06, 0.0000e00], + [1.3861e-02, 2.2300e-06, 0.0000e00], + [1.4554e-02, 2.2300e-06, 0.0000e00], + [1.5281e-02, 2.2300e-06, 0.0000e00], + [1.6045e-02, 2.2300e-06, 0.0000e00], + [1.6848e-02, 2.2300e-06, 0.0000e00], + [1.7690e-02, 2.2300e-06, 0.0000e00], + [1.8575e-02, 2.2300e-06, 0.0000e00], + [1.9503e-02, 2.2300e-06, 0.0000e00], + [2.0479e-02, 2.2300e-06, 0.0000e00], + [2.1502e-02, 2.2300e-06, 0.0000e00], + [2.2578e-02, 2.2300e-06, 0.0000e00], + [2.3706e-02, 2.2300e-06, 0.0000e00], + [2.4892e-02, 2.2300e-06, 0.0000e00], + [2.6136e-02, 2.2300e-06, 0.0000e00], + [2.7443e-02, 2.2300e-06, 0.0000e00], + [2.8815e-02, 2.2300e-06, 0.0000e00], + [3.0256e-02, 2.2300e-06, 0.0000e00], + [3.1769e-02, 2.2300e-06, 0.0000e00], + [3.3357e-02, 2.2300e-06, 0.0000e00], + [3.5025e-02, 2.2300e-06, 0.0000e00], + [3.6777e-02, 2.2300e-06, 0.0000e00], + [3.8615e-02, 2.2300e-06, 0.0000e00], + [4.0546e-02, 2.2300e-06, 0.0000e00], + [4.2573e-02, 2.2300e-06, 0.0000e00], + [4.4702e-02, 2.2300e-06, 0.0000e00], + [4.6937e-02, 2.2300e-06, 0.0000e00], + [4.9284e-02, 2.2300e-06, 0.0000e00], + [5.1748e-02, 2.2300e-06, 0.0000e00], + [5.4336e-02, 2.2300e-06, 0.0000e00], + [5.7052e-02, 2.2300e-06, 0.0000e00], + [5.9905e-02, 2.2300e-06, 0.0000e00], + [6.2900e-02, 2.2300e-06, 0.0000e00], + [6.6045e-02, 2.2300e-06, 0.0000e00], + [6.9348e-02, 2.2300e-06, 0.0000e00], + [7.2815e-02, 2.2300e-06, 0.0000e00], + [7.6456e-02, 2.2300e-06, 0.0000e00], + [8.0279e-02, 2.2300e-06, 0.0000e00], + [8.4292e-02, 2.2300e-06, 0.0000e00], + [8.8507e-02, 2.2300e-06, 0.0000e00], + [9.2932e-02, 2.2300e-06, 0.0000e00], + [9.7579e-02, 2.2300e-06, 0.0000e00], + [1.0246e-01, 2.2300e-06, 0.0000e00], + [1.0758e-01, 2.2300e-06, 0.0000e00], + [1.1296e-01, 2.2300e-06, 0.0000e00], + [1.1861e-01, 2.2300e-06, 0.0000e00], + [1.2454e-01, 2.2300e-06, 0.0000e00], + [1.3077e-01, 2.2300e-06, 0.0000e00], + [1.3730e-01, 2.2300e-06, 0.0000e00], + [1.4417e-01, 2.2300e-06, 0.0000e00], + [1.5138e-01, 2.2300e-06, 0.0000e00], + [1.5895e-01, 2.2300e-06, 0.0000e00], + [1.6689e-01, 2.2300e-06, 0.0000e00], + [1.7524e-01, 2.2300e-06, 0.0000e00], + [1.8400e-01, 2.2300e-06, 0.0000e00], + [1.9320e-01, 2.2300e-06, 0.0000e00], + [2.0286e-01, 2.2300e-06, 0.0000e00], + [2.1300e-01, 2.2300e-06, 0.0000e00], + [2.2365e-01, 2.2300e-06, 0.0000e00], + [2.3484e-01, 2.2300e-06, 0.0000e00], + [2.4658e-01, 2.2300e-06, 0.0000e00], + [2.5891e-01, 2.2300e-06, 0.0000e00], + [2.7185e-01, 2.2300e-06, 0.0000e00], + [2.8544e-01, 2.2300e-06, 0.0000e00], + [2.9972e-01, 2.2300e-06, 0.0000e00], + [3.1470e-01, 2.2300e-06, 0.0000e00], + [3.3044e-01, 2.2300e-06, 0.0000e00], + [3.4696e-01, 2.2300e-06, 0.0000e00], + [3.6431e-01, 2.2300e-06, 0.0000e00], + [3.8252e-01, 2.2300e-06, 0.0000e00], + [4.0165e-01, 2.2300e-06, 0.0000e00], + [4.2173e-01, 2.2300e-06, 0.0000e00], + [4.4282e-01, 2.2300e-06, 0.0000e00], + [4.6496e-01, 2.2300e-06, 0.0000e00], + [4.8821e-01, 2.2300e-06, 0.0000e00], + [5.1262e-01, 2.2300e-06, 0.0000e00], + [5.3825e-01, 2.2300e-06, 0.0000e00], + [5.6516e-01, 2.2300e-06, 0.0000e00], + [5.9342e-01, 2.2300e-06, 0.0000e00], + ] + ), + np.array( + [ + [1.1403e-02, 3.3800e-06, 0.0000e00], + [1.1973e-02, 3.3800e-06, 0.0000e00], + [1.2572e-02, 3.3800e-06, 0.0000e00], + [1.3201e-02, 3.3800e-06, 0.0000e00], + [1.3861e-02, 3.3800e-06, 0.0000e00], + [1.4554e-02, 3.3800e-06, 0.0000e00], + [1.5281e-02, 3.3800e-06, 0.0000e00], + [1.6045e-02, 3.3800e-06, 0.0000e00], + [1.6848e-02, 3.3800e-06, 0.0000e00], + [1.7690e-02, 3.3800e-06, 0.0000e00], + [1.8575e-02, 3.3800e-06, 0.0000e00], + [1.9503e-02, 3.3800e-06, 0.0000e00], + [2.0479e-02, 3.3800e-06, 0.0000e00], + [2.1502e-02, 3.3800e-06, 0.0000e00], + [2.2578e-02, 3.3800e-06, 0.0000e00], + [2.3706e-02, 3.3800e-06, 0.0000e00], + [2.4892e-02, 3.3800e-06, 0.0000e00], + [2.6136e-02, 3.3800e-06, 0.0000e00], + [2.7443e-02, 3.3800e-06, 0.0000e00], + [2.8815e-02, 3.3800e-06, 0.0000e00], + [3.0256e-02, 3.3800e-06, 0.0000e00], + [3.1769e-02, 3.3800e-06, 0.0000e00], + [3.3357e-02, 3.3800e-06, 0.0000e00], + [3.5025e-02, 3.3800e-06, 0.0000e00], + [3.6777e-02, 3.3800e-06, 0.0000e00], + [3.8615e-02, 3.3800e-06, 0.0000e00], + [4.0546e-02, 3.3800e-06, 0.0000e00], + [4.2573e-02, 3.3800e-06, 0.0000e00], + [4.4702e-02, 3.3800e-06, 0.0000e00], + [4.6937e-02, 3.3800e-06, 0.0000e00], + [4.9284e-02, 3.3800e-06, 0.0000e00], + [5.1748e-02, 3.3800e-06, 0.0000e00], + [5.4336e-02, 3.3800e-06, 0.0000e00], + [5.7052e-02, 3.3800e-06, 0.0000e00], + [5.9905e-02, 3.3800e-06, 0.0000e00], + [6.2900e-02, 3.3800e-06, 0.0000e00], + [6.6045e-02, 3.3800e-06, 0.0000e00], + [6.9348e-02, 3.3800e-06, 0.0000e00], + [7.2815e-02, 3.3800e-06, 0.0000e00], + [7.6456e-02, 3.3800e-06, 0.0000e00], + [8.0279e-02, 3.3800e-06, 0.0000e00], + [8.4292e-02, 3.3800e-06, 0.0000e00], + [8.8507e-02, 3.3800e-06, 0.0000e00], + [9.2932e-02, 3.3800e-06, 0.0000e00], + [9.7579e-02, 3.3800e-06, 0.0000e00], + [1.0246e-01, 3.3800e-06, 0.0000e00], + [1.0758e-01, 3.3800e-06, 0.0000e00], + [1.1296e-01, 3.3800e-06, 0.0000e00], + [1.1861e-01, 3.3800e-06, 0.0000e00], + [1.2454e-01, 3.3800e-06, 0.0000e00], + [1.3077e-01, 3.3800e-06, 0.0000e00], + [1.3730e-01, 3.3800e-06, 0.0000e00], + [1.4417e-01, 3.3800e-06, 0.0000e00], + [1.5138e-01, 3.3800e-06, 0.0000e00], + [1.5895e-01, 3.3800e-06, 0.0000e00], + [1.6689e-01, 3.3800e-06, 0.0000e00], + [1.7524e-01, 3.3800e-06, 0.0000e00], + [1.8400e-01, 3.3800e-06, 0.0000e00], + [1.9320e-01, 3.3800e-06, 0.0000e00], + [2.0286e-01, 3.3800e-06, 0.0000e00], + [2.1300e-01, 3.3800e-06, 0.0000e00], + [2.2365e-01, 3.3800e-06, 0.0000e00], + [2.3484e-01, 3.3800e-06, 0.0000e00], + [2.4658e-01, 3.3800e-06, 0.0000e00], + [2.5891e-01, 3.3800e-06, 0.0000e00], + [2.7185e-01, 3.3800e-06, 0.0000e00], + [2.8544e-01, 3.3800e-06, 0.0000e00], + [2.9972e-01, 3.3800e-06, 0.0000e00], + [3.1470e-01, 3.3800e-06, 0.0000e00], + [3.3044e-01, 3.3800e-06, 0.0000e00], + [3.4696e-01, 3.3800e-06, 0.0000e00], + [3.6431e-01, 3.3800e-06, 0.0000e00], + [3.8252e-01, 3.3800e-06, 0.0000e00], + [4.0165e-01, 3.3800e-06, 0.0000e00], + [4.2173e-01, 3.3800e-06, 0.0000e00], + [4.4282e-01, 3.3800e-06, 0.0000e00], + [4.6496e-01, 3.3800e-06, 0.0000e00], + [4.8821e-01, 3.3800e-06, 0.0000e00], + [5.1262e-01, 3.3800e-06, 0.0000e00], + [5.3825e-01, 3.3800e-06, 0.0000e00], + [5.6516e-01, 3.3800e-06, 0.0000e00], + [5.9342e-01, 3.3800e-06, 0.0000e00], + ] + ), + ] results.layerSlds = [ [ np.array( @@ -3538,6 +3536,180 @@ def dream_results(): ], ), ], + backgrounds=[ + np.array( + [ + [1.14030000e-02, 2.26745452e-06, 0.00000000e00], + [1.19730000e-02, 2.26745452e-06, 0.00000000e00], + [1.25720000e-02, 2.26745452e-06, 0.00000000e00], + [1.32010000e-02, 2.26745452e-06, 0.00000000e00], + [1.38610000e-02, 2.26745452e-06, 0.00000000e00], + [1.45540000e-02, 2.26745452e-06, 0.00000000e00], + [1.52810000e-02, 2.26745452e-06, 0.00000000e00], + [1.60450000e-02, 2.26745452e-06, 0.00000000e00], + [1.68480000e-02, 2.26745452e-06, 0.00000000e00], + [1.76900000e-02, 2.26745452e-06, 0.00000000e00], + [1.85750000e-02, 2.26745452e-06, 0.00000000e00], + [1.95030000e-02, 2.26745452e-06, 0.00000000e00], + [2.04790000e-02, 2.26745452e-06, 0.00000000e00], + [2.15020000e-02, 2.26745452e-06, 0.00000000e00], + [2.25780000e-02, 2.26745452e-06, 0.00000000e00], + [2.37060000e-02, 2.26745452e-06, 0.00000000e00], + [2.48920000e-02, 2.26745452e-06, 0.00000000e00], + [2.61360000e-02, 2.26745452e-06, 0.00000000e00], + [2.74430000e-02, 2.26745452e-06, 0.00000000e00], + [2.88150000e-02, 2.26745452e-06, 0.00000000e00], + [3.02560000e-02, 2.26745452e-06, 0.00000000e00], + [3.17690000e-02, 2.26745452e-06, 0.00000000e00], + [3.33570000e-02, 2.26745452e-06, 0.00000000e00], + [3.50250000e-02, 2.26745452e-06, 0.00000000e00], + [3.67770000e-02, 2.26745452e-06, 0.00000000e00], + [3.86150000e-02, 2.26745452e-06, 0.00000000e00], + [4.05460000e-02, 2.26745452e-06, 0.00000000e00], + [4.25730000e-02, 2.26745452e-06, 0.00000000e00], + [4.47020000e-02, 2.26745452e-06, 0.00000000e00], + [4.69370000e-02, 2.26745452e-06, 0.00000000e00], + [4.92840000e-02, 2.26745452e-06, 0.00000000e00], + [5.17480000e-02, 2.26745452e-06, 0.00000000e00], + [5.43360000e-02, 2.26745452e-06, 0.00000000e00], + [5.70520000e-02, 2.26745452e-06, 0.00000000e00], + [5.99050000e-02, 2.26745452e-06, 0.00000000e00], + [6.29000000e-02, 2.26745452e-06, 0.00000000e00], + [6.60450000e-02, 2.26745452e-06, 0.00000000e00], + [6.93480000e-02, 2.26745452e-06, 0.00000000e00], + [7.28150000e-02, 2.26745452e-06, 0.00000000e00], + [7.64560000e-02, 2.26745452e-06, 0.00000000e00], + [8.02790000e-02, 2.26745452e-06, 0.00000000e00], + [8.42920000e-02, 2.26745452e-06, 0.00000000e00], + [8.85070000e-02, 2.26745452e-06, 0.00000000e00], + [9.29320000e-02, 2.26745452e-06, 0.00000000e00], + [9.75790000e-02, 2.26745452e-06, 0.00000000e00], + [1.02460000e-01, 2.26745452e-06, 0.00000000e00], + [1.07580000e-01, 2.26745452e-06, 0.00000000e00], + [1.12960000e-01, 2.26745452e-06, 0.00000000e00], + [1.18610000e-01, 2.26745452e-06, 0.00000000e00], + [1.24540000e-01, 2.26745452e-06, 0.00000000e00], + [1.30770000e-01, 2.26745452e-06, 0.00000000e00], + [1.37300000e-01, 2.26745452e-06, 0.00000000e00], + [1.44170000e-01, 2.26745452e-06, 0.00000000e00], + [1.51380000e-01, 2.26745452e-06, 0.00000000e00], + [1.58950000e-01, 2.26745452e-06, 0.00000000e00], + [1.66890000e-01, 2.26745452e-06, 0.00000000e00], + [1.75240000e-01, 2.26745452e-06, 0.00000000e00], + [1.84000000e-01, 2.26745452e-06, 0.00000000e00], + [1.93200000e-01, 2.26745452e-06, 0.00000000e00], + [2.02860000e-01, 2.26745452e-06, 0.00000000e00], + [2.13000000e-01, 2.26745452e-06, 0.00000000e00], + [2.23650000e-01, 2.26745452e-06, 0.00000000e00], + [2.34840000e-01, 2.26745452e-06, 0.00000000e00], + [2.46580000e-01, 2.26745452e-06, 0.00000000e00], + [2.58910000e-01, 2.26745452e-06, 0.00000000e00], + [2.71850000e-01, 2.26745452e-06, 0.00000000e00], + [2.85440000e-01, 2.26745452e-06, 0.00000000e00], + [2.99720000e-01, 2.26745452e-06, 0.00000000e00], + [3.14700000e-01, 2.26745452e-06, 0.00000000e00], + [3.30440000e-01, 2.26745452e-06, 0.00000000e00], + [3.46960000e-01, 2.26745452e-06, 0.00000000e00], + [3.64310000e-01, 2.26745452e-06, 0.00000000e00], + [3.82520000e-01, 2.26745452e-06, 0.00000000e00], + [4.01650000e-01, 2.26745452e-06, 0.00000000e00], + [4.21730000e-01, 2.26745452e-06, 0.00000000e00], + [4.42820000e-01, 2.26745452e-06, 0.00000000e00], + [4.64960000e-01, 2.26745452e-06, 0.00000000e00], + [4.88210000e-01, 2.26745452e-06, 0.00000000e00], + [5.12620000e-01, 2.26745452e-06, 0.00000000e00], + [5.38250000e-01, 2.26745452e-06, 0.00000000e00], + [5.65160000e-01, 2.26745452e-06, 0.00000000e00], + [5.93420000e-01, 2.26745452e-06, 0.00000000e00], + ] + ), + np.array( + [ + [1.1403000e-02, 2.0963874e-06, 0.0000000e00], + [1.1973000e-02, 2.0963874e-06, 0.0000000e00], + [1.2572000e-02, 2.0963874e-06, 0.0000000e00], + [1.3201000e-02, 2.0963874e-06, 0.0000000e00], + [1.3861000e-02, 2.0963874e-06, 0.0000000e00], + [1.4554000e-02, 2.0963874e-06, 0.0000000e00], + [1.5281000e-02, 2.0963874e-06, 0.0000000e00], + [1.6045000e-02, 2.0963874e-06, 0.0000000e00], + [1.6848000e-02, 2.0963874e-06, 0.0000000e00], + [1.7690000e-02, 2.0963874e-06, 0.0000000e00], + [1.8575000e-02, 2.0963874e-06, 0.0000000e00], + [1.9503000e-02, 2.0963874e-06, 0.0000000e00], + [2.0479000e-02, 2.0963874e-06, 0.0000000e00], + [2.1502000e-02, 2.0963874e-06, 0.0000000e00], + [2.2578000e-02, 2.0963874e-06, 0.0000000e00], + [2.3706000e-02, 2.0963874e-06, 0.0000000e00], + [2.4892000e-02, 2.0963874e-06, 0.0000000e00], + [2.6136000e-02, 2.0963874e-06, 0.0000000e00], + [2.7443000e-02, 2.0963874e-06, 0.0000000e00], + [2.8815000e-02, 2.0963874e-06, 0.0000000e00], + [3.0256000e-02, 2.0963874e-06, 0.0000000e00], + [3.1769000e-02, 2.0963874e-06, 0.0000000e00], + [3.3357000e-02, 2.0963874e-06, 0.0000000e00], + [3.5025000e-02, 2.0963874e-06, 0.0000000e00], + [3.6777000e-02, 2.0963874e-06, 0.0000000e00], + [3.8615000e-02, 2.0963874e-06, 0.0000000e00], + [4.0546000e-02, 2.0963874e-06, 0.0000000e00], + [4.2573000e-02, 2.0963874e-06, 0.0000000e00], + [4.4702000e-02, 2.0963874e-06, 0.0000000e00], + [4.6937000e-02, 2.0963874e-06, 0.0000000e00], + [4.9284000e-02, 2.0963874e-06, 0.0000000e00], + [5.1748000e-02, 2.0963874e-06, 0.0000000e00], + [5.4336000e-02, 2.0963874e-06, 0.0000000e00], + [5.7052000e-02, 2.0963874e-06, 0.0000000e00], + [5.9905000e-02, 2.0963874e-06, 0.0000000e00], + [6.2900000e-02, 2.0963874e-06, 0.0000000e00], + [6.6045000e-02, 2.0963874e-06, 0.0000000e00], + [6.9348000e-02, 2.0963874e-06, 0.0000000e00], + [7.2815000e-02, 2.0963874e-06, 0.0000000e00], + [7.6456000e-02, 2.0963874e-06, 0.0000000e00], + [8.0279000e-02, 2.0963874e-06, 0.0000000e00], + [8.4292000e-02, 2.0963874e-06, 0.0000000e00], + [8.8507000e-02, 2.0963874e-06, 0.0000000e00], + [9.2932000e-02, 2.0963874e-06, 0.0000000e00], + [9.7579000e-02, 2.0963874e-06, 0.0000000e00], + [1.0246000e-01, 2.0963874e-06, 0.0000000e00], + [1.0758000e-01, 2.0963874e-06, 0.0000000e00], + [1.1296000e-01, 2.0963874e-06, 0.0000000e00], + [1.1861000e-01, 2.0963874e-06, 0.0000000e00], + [1.2454000e-01, 2.0963874e-06, 0.0000000e00], + [1.3077000e-01, 2.0963874e-06, 0.0000000e00], + [1.3730000e-01, 2.0963874e-06, 0.0000000e00], + [1.4417000e-01, 2.0963874e-06, 0.0000000e00], + [1.5138000e-01, 2.0963874e-06, 0.0000000e00], + [1.5895000e-01, 2.0963874e-06, 0.0000000e00], + [1.6689000e-01, 2.0963874e-06, 0.0000000e00], + [1.7524000e-01, 2.0963874e-06, 0.0000000e00], + [1.8400000e-01, 2.0963874e-06, 0.0000000e00], + [1.9320000e-01, 2.0963874e-06, 0.0000000e00], + [2.0286000e-01, 2.0963874e-06, 0.0000000e00], + [2.1300000e-01, 2.0963874e-06, 0.0000000e00], + [2.2365000e-01, 2.0963874e-06, 0.0000000e00], + [2.3484000e-01, 2.0963874e-06, 0.0000000e00], + [2.4658000e-01, 2.0963874e-06, 0.0000000e00], + [2.5891000e-01, 2.0963874e-06, 0.0000000e00], + [2.7185000e-01, 2.0963874e-06, 0.0000000e00], + [2.8544000e-01, 2.0963874e-06, 0.0000000e00], + [2.9972000e-01, 2.0963874e-06, 0.0000000e00], + [3.1470000e-01, 2.0963874e-06, 0.0000000e00], + [3.3044000e-01, 2.0963874e-06, 0.0000000e00], + [3.4696000e-01, 2.0963874e-06, 0.0000000e00], + [3.6431000e-01, 2.0963874e-06, 0.0000000e00], + [3.8252000e-01, 2.0963874e-06, 0.0000000e00], + [4.0165000e-01, 2.0963874e-06, 0.0000000e00], + [4.2173000e-01, 2.0963874e-06, 0.0000000e00], + [4.4282000e-01, 2.0963874e-06, 0.0000000e00], + [4.6496000e-01, 2.0963874e-06, 0.0000000e00], + [4.8821000e-01, 2.0963874e-06, 0.0000000e00], + [5.1262000e-01, 2.0963874e-06, 0.0000000e00], + [5.3825000e-01, 2.0963874e-06, 0.0000000e00], + [5.6516000e-01, 2.0963874e-06, 0.0000000e00], + [5.9342000e-01, 2.0963874e-06, 0.0000000e00], + ] + ), + ], layerSlds=[ [ np.array( diff --git a/tests/test_outputs.py b/tests/test_outputs.py index 43bfa1c5..fdd22dd7 100644 --- a/tests/test_outputs.py +++ b/tests/test_outputs.py @@ -22,6 +22,7 @@ def reflectivity_calculation_str(): "reflectivity = [Data array: [21 x 2], Data array: [21 x 2]],\n" "simulation = [Data array: [21 x 2], Data array: [21 x 2]],\n" "shiftedData = [Data array: [21 x 3], Data array: [21 x 3]],\n" + "backgrounds = [Data array: [82 x 3], Data array: [82 x 3]],\n" "layerSlds = [[Data array: [8 x 3]], [Data array: [8 x 3]]],\n" "sldProfiles = [[Data array: [25 x 2], Data array: [25 x 2]]],\n" "resampledLayers = [[Data array: [1 x 3]], [Data array: [1 x 3]]],\n" @@ -56,6 +57,7 @@ def dream_str(): "reflectivity = [Data array: [21 x 2], Data array: [21 x 2]],\n" "simulation = [Data array: [21 x 2], Data array: [21 x 2]],\n" "shiftedData = [Data array: [21 x 3], Data array: [21 x 3]],\n" + "backgrounds = [Data array: [82 x 3], Data array: [82 x 3]],\n" "layerSlds = [[Data array: [8 x 3]], [Data array: [8 x 3]]],\n" "sldProfiles = [[Data array: [29 x 2], Data array: [29 x 2]]],\n" "resampledLayers = [[Data array: [1 x 3]], [Data array: [1 x 3]]],\n" From 0d3dd5864c12f8a91ad07c18ccf18f6afacb2c2e Mon Sep 17 00:00:00 2001 From: alexhroom Date: Wed, 8 Jan 2025 13:47:47 +0000 Subject: [PATCH 13/15] review fixes to docstrings --- RATapi/inputs.py | 8 ++++---- RATapi/models.py | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index 4145b6c0..dd76fd34 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -484,19 +484,19 @@ def check_indices(problem: ProblemDefinition) -> None: def append_data_background(data: np.array, background: np.array) -> np.array: - """Add background data to contrast data. + """Append background data to contrast data. Parameters ---------- data : np.array - The contrast data to which we are adding a background. + The contrast data to which we are appending a background. background : np.array - The background data to add to the contrast. + The background data to append to the contrast. Returns ------- np.array - The contrast data with background data added as two additional columns. + The contrast data with background data appended as two additional columns. """ if not np.allclose(data[:, 0], background[:, 0]): diff --git a/RATapi/models.py b/RATapi/models.py index 42114816..a610b1f8 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -258,7 +258,7 @@ def __str__(self): class CustomFile(RATModel): - """A file containing functions to use for a custom model, function background or function resolution. + """A file containing functions to use for a custom model or function background. Parameters ---------- From 6aff1063f075256868e766e95de663ab52838138 Mon Sep 17 00:00:00 2001 From: alexhroom Date: Thu, 9 Jan 2025 15:23:41 +0000 Subject: [PATCH 14/15] ruff format --- RATapi/inputs.py | 6 +++--- tests/test_inputs.py | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/RATapi/inputs.py b/RATapi/inputs.py index dd76fd34..56f27b42 100644 --- a/RATapi/inputs.py +++ b/RATapi/inputs.py @@ -445,7 +445,7 @@ def check_indices(problem: ProblemDefinition) -> None: if elements: raise IndexError( f'The problem field "{index_list[params]}" contains: {", ".join(str(i) for i in elements)}' - f', which lie{"s"*(len(elements)==1)} outside of the range of "{params}"', + f', which lie{"s" * (len(elements) == 1)} outside of the range of "{params}"', ) # backgroundParams has a different structure, so is handled separately: @@ -478,8 +478,8 @@ def check_indices(problem: ProblemDefinition) -> None: elements = [element for element in background_data[1:] if not 0 < element <= len(problem.backgroundParams)] if elements: raise IndexError( - f'Entry {i} of contrastBackgroundParams contains: {", ".join(str(i) for i in elements)}' - f', which lie{"s"*(len(elements)==1)} outside of the range of "backgroundParams"', + f"Entry {i} of contrastBackgroundParams contains: {', '.join(str(i) for i in elements)}" + f', which lie{"s" * (len(elements) == 1)} outside of the range of "backgroundParams"', ) diff --git a/tests/test_inputs.py b/tests/test_inputs.py index b725ff88..70db6c38 100644 --- a/tests/test_inputs.py +++ b/tests/test_inputs.py @@ -647,7 +647,7 @@ def test_background_params_value_indices(self, test_problem, bad_value, request) with pytest.raises( IndexError, - match=f"Entry {len(bad_value)-1} of contrastBackgroundParams contains: {bad_value[-1][-1]}" + match=f"Entry {len(bad_value) - 1} of contrastBackgroundParams contains: {bad_value[-1][-1]}" f', which lies outside of the range of "backgroundParams"', ): check_indices(test_problem) From 841cbd1793223e250e611146d22b97fc5da5144c Mon Sep 17 00:00:00 2001 From: alexhroom Date: Fri, 10 Jan 2025 12:18:39 +0000 Subject: [PATCH 15/15] added warning for fields clearing --- RATapi/models.py | 1 + tests/test_models.py | 5 +++-- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/RATapi/models.py b/RATapi/models.py index a610b1f8..1274a2a8 100644 --- a/RATapi/models.py +++ b/RATapi/models.py @@ -59,6 +59,7 @@ class Signal(RATModel): def __setattr__(self, name, value): if name == "type": + warnings.warn(f"Changing the type of {self.name} clears its source and value fields.", stacklevel=2) for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: with warnings.catch_warnings(): warnings.simplefilter("ignore") diff --git a/tests/test_models.py b/tests/test_models.py index 3912804e..476ebbad 100644 --- a/tests/test_models.py +++ b/tests/test_models.py @@ -336,7 +336,7 @@ def test_contrast_bad_ratio(): @pytest.mark.parametrize("model", [RATapi.models.Background, RATapi.models.Resolution]) def test_type_change_clear(model): - """If the type of a background or resolution is changed, it should wipe the other fields.""" + """If the type of a background or resolution is changed, it should wipe the other fields and warn the user.""" model_instance = model( name="Test", @@ -349,6 +349,7 @@ def test_type_change_clear(model): value_5="val5", ) - model_instance.type = "data" + with pytest.warns(UserWarning, match="Changing the type of Test clears its source and value fields."): + model_instance.type = "data" for attr in ["source", "value_1", "value_2", "value_3", "value_4", "value_5"]: assert getattr(model_instance, attr) == ""