Skip to content

Commit cdaa93d

Browse files
authored
Adds "repeat_layers" option to contrasts (RascalSoftware#178)
* Adds validator to warn for resample=False for custom XY * Adds "repeat_layers" option to contrasts * Reverts changes to test data
1 parent a57541a commit cdaa93d

File tree

6 files changed

+81
-5
lines changed

6 files changed

+81
-5
lines changed

ratapi/inputs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ def make_problem(project: ratapi.Project) -> ProblemDefinition:
302302
problem.numberOfContrasts = len(project.contrasts)
303303
problem.geometry = project.geometry
304304
problem.useImaginary = project.absorption
305-
problem.repeatLayers = [1] * len(project.contrasts)
305+
problem.repeatLayers = [contrast.repeat_layers for contrast in project.contrasts]
306306
problem.contrastBackgroundParams = contrast_background_params
307307
problem.contrastBackgroundTypes = contrast_background_types
308308
problem.contrastBackgroundActions = [contrast.background_action for contrast in project.contrasts]

ratapi/models.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,6 +163,8 @@ class Contrast(RATModel):
163163
The name of the instrument resolution for this contrast.
164164
resample : bool
165165
Whether adaptive resampling should be used for interface microslicing.
166+
repeat_layers : int
167+
For standard layers, the number of times the set of layers defined in the model should be repeated.
166168
model : list[str]
167169
If this is a standard layers model, this should be a list of layer names
168170
that make up the slab model for this contrast.
@@ -180,6 +182,7 @@ class Contrast(RATModel):
180182
scalefactor: str = ""
181183
resolution: str = ""
182184
resample: bool = False
185+
repeat_layers: int = Field(default=1, gt=0)
183186
model: list[str] = []
184187

185188
@model_validator(mode="before")
@@ -208,6 +211,7 @@ def __str__(self):
208211
self.scalefactor,
209212
self.resolution,
210213
self.resample,
214+
self.repeat_layers,
211215
model_entry,
212216
]
213217
)
@@ -238,6 +242,8 @@ class ContrastWithRatio(RATModel):
238242
The name of the instrument resolution for this contrast.
239243
resample : bool
240244
Whether adaptive resampling should be used for interface microslicing.
245+
repeat_layers : int
246+
For standard layers, the number of times the set of layers defined in the model should be repeated.
241247
domain_ratio : str
242248
The name of the domain ratio parameter describing how the first domain should be weighted
243249
relative to the second.
@@ -258,6 +264,7 @@ class ContrastWithRatio(RATModel):
258264
scalefactor: str = ""
259265
resolution: str = ""
260266
resample: bool = False
267+
repeat_layers: int = Field(default=1, gt=0)
261268
domain_ratio: str = ""
262269
model: list[str] = []
263270

@@ -276,6 +283,8 @@ def __str__(self):
276283
self.scalefactor,
277284
self.resolution,
278285
self.resample,
286+
self.repeat_layers,
287+
self.domain_ratio,
279288
model_entry,
280289
]
281290
)

ratapi/project.py

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -361,8 +361,8 @@ def model_post_init(self, __context: Any) -> None:
361361
"""Set up the Class to protect against disallowed modification.
362362
363363
We initialise the class handle in the ClassLists for empty data fields, set protected parameters, get names of
364-
all defined parameters, determine the contents of the "model" field in contrasts,
365-
and wrap ClassList routines to control revalidation.
364+
all defined parameters, determine the contents of the "model" field in contrasts, and wrap ClassList routines
365+
to control revalidation.
366366
"""
367367
# Ensure all ClassLists have the correct _class_handle defined
368368
for field in (fields := Project.model_fields):
@@ -454,6 +454,33 @@ def set_layers(self) -> "Project":
454454
self.layers.data = []
455455
return self
456456

457+
@model_validator(mode="after")
458+
def set_repeat_layers(self) -> "Project":
459+
"""If we are not using a standard layers model, warn that the repeat layers setting is not valid."""
460+
if self.model != LayerModels.StandardLayers:
461+
for contrast in self.contrasts:
462+
if "repeat_layers" in contrast.model_fields_set and contrast.repeat_layers != 1:
463+
warnings.warn(
464+
'For a custom layers or custom XY calculation, the "repeat_layers" setting for each '
465+
"contrast is not valid - resetting to 1.",
466+
stacklevel=2,
467+
)
468+
contrast.repeat_layers = 1
469+
return self
470+
471+
@model_validator(mode="after")
472+
def set_resample(self) -> "Project":
473+
"""If we are using a custom XY model, warn that the resample setting for each contrast must always be True."""
474+
if self.model == LayerModels.CustomXY:
475+
for contrast in self.contrasts:
476+
if "resample" in contrast.model_fields_set and contrast.resample is False:
477+
warnings.warn(
478+
'For a custom XY calculation, "resample" must be True for each contrast - resetting to True.',
479+
stacklevel=2,
480+
)
481+
contrast.resample = True
482+
return self
483+
457484
@model_validator(mode="after")
458485
def set_calculation(self) -> "Project":
459486
"""Apply the calc setting to the project."""

tests/test_inputs.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -306,7 +306,7 @@ def custom_xy_problem(test_names, test_checks):
306306
problem.contrastResolutionTypes = ["constant"]
307307
problem.contrastCustomFiles = [1]
308308
problem.contrastDomainRatios = [0]
309-
problem.resample = [False]
309+
problem.resample = [True]
310310
problem.dataPresent = [0]
311311
problem.data = [np.empty([0, 6])]
312312
problem.dataLimits = [[0.0, 0.0]]

tests/test_project.py

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -443,6 +443,46 @@ def test_set_layers(project_parameters: dict) -> None:
443443
assert project.layers == []
444444

445445

446+
@pytest.mark.parametrize(
447+
"project_parameters",
448+
[
449+
(
450+
{
451+
"model": LayerModels.CustomLayers,
452+
"contrasts": [ratapi.models.Contrast(name="Test Contrast", repeat_layers=2)],
453+
}
454+
),
455+
({"model": LayerModels.CustomXY, "contrasts": [ratapi.models.Contrast(name="Test Contrast", repeat_layers=2)]}),
456+
],
457+
)
458+
def test_set_repeat_layers(project_parameters: dict) -> None:
459+
"""If we are using a custom layers of custom XY model, the "resample" field of all the contrasts should always
460+
be 1."""
461+
with pytest.warns(
462+
match='For a custom layers or custom XY calculation, the "repeat_layers" setting for each '
463+
"contrast is not valid - resetting to 1."
464+
):
465+
project = ratapi.Project(**project_parameters)
466+
assert all(contrast.repeat_layers == 1 for contrast in project.contrasts)
467+
468+
469+
@pytest.mark.parametrize(
470+
"project_parameters",
471+
[
472+
({"model": LayerModels.CustomXY, "contrasts": [ratapi.models.Contrast(name="Test Contrast")]}),
473+
],
474+
)
475+
def test_set_resample(project_parameters: dict) -> None:
476+
"""If we are using a custom XY model, the "resample" field of all the contrasts should always be True."""
477+
project = ratapi.Project(**project_parameters)
478+
assert all(contrast.resample for contrast in project.contrasts)
479+
with pytest.warns(
480+
match='For a custom XY calculation, "resample" must be True for each contrast - resetting to True.'
481+
):
482+
project.contrasts.append(name="New Contrast", resample=False)
483+
assert all(contrast.resample for contrast in project.contrasts)
484+
485+
446486
@pytest.mark.parametrize(
447487
["input_calculation", "input_contrast", "new_calculation", "new_contrast_model", "num_domain_ratios"],
448488
[

0 commit comments

Comments
 (0)