Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

191 adjust model and modelcollection to hold functionality from app #192

Merged
2 changes: 2 additions & 0 deletions src/easyreflectometry/data/__init__.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
from .data_store import DataSet1D
from .data_store import ProjectData
from .measurement import load

__all__ = [
load,
ProjectData,
DataSet1D,
]
10 changes: 9 additions & 1 deletion src/easyreflectometry/data/data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,8 @@ def __init__(
y_label: str = 'y',
):
self._model = model
self._model.background = np.min(y)
if y is not None and model is not None:
self._model.background = np.min(y)

if x is None:
x = np.array([])
Expand All @@ -92,6 +93,9 @@ def __init__(
if xe is None:
xe = np.zeros_like(x)

if len(x) != len(y):
raise ValueError('x and y must be the same length')

self.name = name
if not isinstance(x, np.ndarray):
x = np.array(x)
Expand All @@ -108,6 +112,10 @@ def __init__(

self._color = None

@property
def data_points(self) -> int:
return zip(self.x, self.y)

andped10 marked this conversation as resolved.
Show resolved Hide resolved
@property
def model(self) -> Model:
return self._model
Expand Down
3 changes: 3 additions & 0 deletions src/easyreflectometry/model/model.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ def __init__(
background: Union[Parameter, Number, None] = None,
resolution_function: Union[ResolutionFunction, None] = None,
name: str = 'EasyModel',
color: str = 'black',
unique_name: Optional[str] = None,
interface=None,
):
Expand All @@ -81,6 +82,7 @@ def __init__(

scale = get_as_parameter('scale', scale, DEFAULTS)
background = get_as_parameter('background', background, DEFAULTS)
self.color = color

super().__init__(
name=name,
Expand Down Expand Up @@ -173,6 +175,7 @@ def _dict_repr(self) -> dict[str, dict[str, str]]:
'scale': float(self.scale.value),
'background': float(self.background.value),
'resolution': resolution,
'color': self.color,
'sample': self.sample._dict_repr,
}
}
Expand Down
27 changes: 14 additions & 13 deletions src/easyreflectometry/model/model_collection.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from __future__ import annotations

__author__ = 'github.com/arm61'

from typing import List
from typing import Optional
from typing import Tuple

from easyreflectometry.sample.collections.base_collection import BaseCollection
Expand Down Expand Up @@ -35,21 +34,24 @@ def __init__(

super().__init__(name, interface, *models, **kwargs)

def add_model(self, new_model: Model):
"""
Add a model to the models.
def add_model(self, model: Optional[Model] = None):
"""Add a model to the collection.

:param new_model: New model to be added.
:param model: Model to add.
"""
self.append(new_model)
if model is None:
model = Model(name='Model new', interface=self.interface)
self.append(model)

def remove_model(self, index: int):
"""
Remove an model from the models.
def duplicate_model(self, index: int):
"""Duplicate a model in the collection.

:param index: Index of the model to remove
:param index: Model to duplicate.
"""
self.pop(index)
to_be_duplicated = self[index]
duplicate = Model.from_dict(to_be_duplicated.as_dict(skip=['unique_name']))
duplicate.name = duplicate.name + ' duplicate'
self.append(duplicate)

def as_dict(self, skip: List[str] | None = None) -> dict:
this_dict = super().as_dict(skip=skip)
Expand All @@ -62,7 +64,6 @@ def from_dict(cls, this_dict: dict) -> ModelCollection:
Create an instance of a collection from a dictionary.

:param data: The dictionary for the collection
:return: An instance of the collection
"""
collection_dict = this_dict.copy()
# We neeed to call from_dict on the base class to get the models
Expand Down
35 changes: 34 additions & 1 deletion src/easyreflectometry/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,11 @@
from typing import Optional
from typing import Union

import numpy as np
from easyscience import global_object
from easyscience.fitting import AvailableMinimizers

from easyreflectometry.data.data_store import DataSet1D
from easyreflectometry.data import DataSet1D
from easyreflectometry.model import Model
from easyreflectometry.model import ModelCollection
from easyreflectometry.sample import Layer
Expand All @@ -18,6 +19,29 @@
from easyreflectometry.sample import Sample
from easyreflectometry.sample.collections.base_collection import BaseCollection

MODELS_SAMPLE_DATA = [
DataSet1D(
name='Sample Data 0',
x=np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
y=np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
)
]
MODELS_MODEL_DATA = [
DataSet1D(
name='Model Data 0',
x=np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
y=np.array([1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5, 10.5]),
)
]
EXPERIMENTAL_DATA = [
DataSet1D(
name='Example Data 0',
x=np.array([1, 2, 3, 4, 5, 6, 7, 8, 9, 10]),
y=np.array([0.5, 1.5, 2.5, 3.5, 4.5, 5.5, 6.5, 7.5, 8.5, 9.5]),
ye=np.array([0.1, 0.2, 0.3, 0.4, 0.5, 0.6, 0.7, 0.8, 0.9, 1.0]),
)
]


class Project:
def __init__(self):
Expand Down Expand Up @@ -95,6 +119,15 @@ def experiments(self, experiments: List[DataSet1D]) -> None:
def path_json(self):
return self.path / 'project.json'

def sample_data_for_model_at_index(self, index: int = 0) -> DataSet1D:
return MODELS_SAMPLE_DATA[index]

def model_data_for_model_at_index(self, index: int = 0) -> DataSet1D:
return MODELS_MODEL_DATA[index]

def experimental_data_for_model_at_index(self, index: int = 0) -> DataSet1D:
return EXPERIMENTAL_DATA[index]

def default_model(self):
self._replace_collection(MaterialCollection(), self._materials)

Expand Down
26 changes: 26 additions & 0 deletions src/easyreflectometry/sample/collections/base_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,32 @@ def names(self) -> list:
"""
return [i.name for i in self]

def move_up(self, index: int):
"""Move the element at the given index up in the collection.

:param index: Index of the element to move up.
"""
if index == 0:
return
self.insert(index - 1, self.pop(index))

def move_down(self, index: int):
"""Move the element at the given index down in the collection.

:param index: Index of the element to move down.
"""
if index == len(self) - 1:
return
self.insert(index + 1, self.pop(index))

def remove(self, index: int):
"""
Remove an element from the elements.

:param index: Index of the element to remove
"""
self.pop(index)

@property
def _dict_repr(self) -> dict:
"""
Expand Down
20 changes: 20 additions & 0 deletions src/easyreflectometry/sample/collections/layer_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,3 +20,23 @@ def __init__(
layers = []

super().__init__(name, interface, unique_name=unique_name, *layers, **kwargs)

def add_layer(self, layer: Optional[Layer] = None):
"""Add a layer to the collection.

:param layer: Layer to add.
"""
if layer is None:
layer = Layer(name='New EasyLayer')
layer.interface = self.interface
self.append(layer)

def duplicate_layer(self, index: int):
"""Duplicate a layer in the collection.

:param layer: Assembly to add.
"""
to_be_duplicated = self[index]
duplicate = Layer.from_dict(to_be_duplicated.as_dict(skip=['unique_name']))
duplicate.name = duplicate.name + ' duplicate'
self.append(duplicate)
28 changes: 2 additions & 26 deletions src/easyreflectometry/sample/collections/material_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,8 @@ def add_material(self, material: Optional[Material] = None):
:param material: Material to add.
"""
if material is None:
material = Material(sld=2.074, isld=0.000, name='Si new', interface=self.interface)
material = Material(sld=2.074, isld=0.000, name='Si new')
material.interface = self.interface
self.append(material)

def duplicate_material(self, index: int):
Expand All @@ -57,28 +58,3 @@ def duplicate_material(self, index: int):
duplicate = Material.from_dict(to_be_duplicated.as_dict(skip=['unique_name']))
duplicate.name = duplicate.name + ' duplicate'
self.append(duplicate)

def move_material_up(self, index: int):
"""Move the material at the given index up in the collection.

:param index: Index of the material to move up.
"""
if index == 0:
return
self.insert(index - 1, self.pop(index))

def move_material_down(self, index: int):
"""Move the material at the given index down in the collection.

:param index: Index of the material to move down.
"""
if index == len(self) - 1:
return
self.insert(index + 1, self.pop(index))

def remove_material(self, index: int):
"""Remove the material at the given index from the collection.

:param index: Index of the material to remove.
"""
self.pop(index)
15 changes: 6 additions & 9 deletions src/easyreflectometry/sample/collections/sample.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,8 @@ def add_assembly(self, assembly: Optional[BaseAssembly] = None):
:param assembly: Assembly to add.
"""
if assembly is None:
assembly = Multilayer(name='New EasyMultilayer', interface=self.interface)
assembly = Multilayer(name='New EasyMultilayer')
assembly.interface = self.interface
self._enable_changes_to_outermost_layers()
self.append(assembly)
self._disable_changes_to_outermost_layers()
Expand All @@ -78,26 +79,22 @@ def duplicate_assembly(self, index: int):
self.append(duplicate)
self._disable_changes_to_outermost_layers()

def move_assembly_up(self, index: int):
def move_up(self, index: int):
"""Move the assembly at the given index up in the sample.

:param index: Index of the assembly to move up.
"""
if index == 0:
return
self._enable_changes_to_outermost_layers()
self.insert(index - 1, self.pop(index))
super().move_up(index)
self._disable_changes_to_outermost_layers()

def move_assembly_down(self, index: int):
def move_down(self, index: int):
"""Move the assembly at the given index down in the sample.

:param index: Index of the assembly to move down.
"""
if index == len(self) - 1:
return
self._enable_changes_to_outermost_layers()
self.insert(index + 1, self.pop(index))
super().move_down(index)
self._disable_changes_to_outermost_layers()

def remove_assembly(self, index: int):
Expand Down
8 changes: 7 additions & 1 deletion src/easyreflectometry/sample/elements/materials/material.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,7 +69,13 @@ def __init__(
unique_name_prefix=f'{unique_name}_Isld',
)

super().__init__(name=name, sld=sld, isld=isld, interface=interface)
super().__init__(
name=name,
sld=sld,
isld=isld,
interface=interface,
unique_name=unique_name,
)

# Representation
@property
Expand Down
6 changes: 4 additions & 2 deletions tests/model/test_model.py
Original file line number Diff line number Diff line change
Expand Up @@ -389,7 +389,7 @@ def test_repr(self):

assert (
model.__repr__()
== 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: 5.0 %\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501
== 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: 5.0 %\n color: black\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501
)

def test_repr_resolution_function(self):
Expand All @@ -398,7 +398,7 @@ def test_repr_resolution_function(self):
model.resolution_function = resolution_function
assert (
model.__repr__()
== 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: function of Q\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501
== 'EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: function of Q\n color: black\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n - EasyMultilayer:\n EasyLayerCollection:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1/Å^2\n isld: 0.000e-6 1/Å^2\n thickness: 10.000 Å\n roughness: 3.300 Å\n' # noqa: E501
)


Expand All @@ -407,6 +407,8 @@ def test_repr_resolution_function(self):
[None, CalculatorFactory()],
)
def test_dict_round_trip(interface):
global_object.map._clear()

# When
resolution_function = LinearSpline([0, 10], [0, 10])
model = Model(interface=interface)
Expand Down
2 changes: 1 addition & 1 deletion tests/model/test_model_collection.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,7 +59,7 @@ def test_delete_model(self):

# Then
collection = ModelCollection(model_1, model_2)
collection.remove_model(0)
collection.remove(0)

# Expect
assert len(collection) == 1
Expand Down
9 changes: 9 additions & 0 deletions tests/sample/assemblies/test_surfactant_layer.py
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ def test_constain_solvent_roughness(self):
assert p.head_layer.roughness.value == 4
assert layer.roughness.value == 4

# Seems to be needed to due to the constraint
global_object.map._clear()

def test_dict_repr(self):
p = SurfactantLayer()
assert p._dict_repr == {
Expand Down Expand Up @@ -227,6 +230,9 @@ def test_dict_round_trip_area_per_molecule_constraint_enabled():
# Expect
assert sorted(p.as_data_dict()) == sorted(q.as_data_dict())

# Seems to be needed to due to the constraint
global_object.map._clear()


def test_dict_round_trip_area_per_molecule_constraint_disabled():
# When
Expand Down Expand Up @@ -256,6 +262,9 @@ def test_dict_round_trip_roughness_constraint_enabled():
# Expect
assert sorted(p.as_data_dict()) == sorted(q.as_data_dict())

# Seems to be needed to due to the constraint
global_object.map._clear()


def test_dict_round_trip_roughness_constraint_disabled():
# When
Expand Down
Loading
Loading