diff --git a/EasyReflectometry/calculators/bornagain/calculator.py b/EasyReflectometry/calculators/bornagain/calculator.py index aa51dba3..ddd51013 100644 --- a/EasyReflectometry/calculators/bornagain/calculator.py +++ b/EasyReflectometry/calculators/bornagain/calculator.py @@ -4,10 +4,10 @@ from easyCore.Objects.Inferface import ItemContainer from EasyReflectometry.experiment.model import Model -from EasyReflectometry.sample.items import MultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.material import MaterialMixture +from EasyReflectometry.sample import Layer +from EasyReflectometry.sample import Material +from EasyReflectometry.sample import MaterialMixture +from EasyReflectometry.sample import Multilayer from ..calculator_base import CalculatorBase from .wrapper import BornAgainWrapper @@ -55,7 +55,7 @@ def reset_storage(self) -> None: """ self._wrapper.reset_storage() - def create(self, model: Material | Layer | MultiLayer | Model) -> list[ItemContainer]: + def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemContainer]: """ Creation function @@ -103,7 +103,7 @@ def create(self, model: Material | Layer | MultiLayer | Model) -> list[ItemConta ) ) self.assign_material_to_layer(model.material.uid, key) - elif issubclass(t_, MultiLayer): + elif issubclass(t_, Multilayer): key = model.uid self._wrapper.create_item(key) r_list.append( diff --git a/EasyReflectometry/calculators/bornagain/wrapper.py b/EasyReflectometry/calculators/bornagain/wrapper.py index a2f547b4..15e60fa3 100644 --- a/EasyReflectometry/calculators/bornagain/wrapper.py +++ b/EasyReflectometry/calculators/bornagain/wrapper.py @@ -160,7 +160,7 @@ def create_model(self): """ Create a model for analysis """ - self.storage['model'] = ba.MultiLayer() + self.storage['model'] = ba.Multilayer() self.storage['model'].setRoughnessModel(ba.RoughnessModel.NEVOT_CROCE) self.storage['model_items'] = [] self.storage['model_parameters']['scale'] = 1 @@ -267,7 +267,7 @@ def calculate(self, x_array: np.ndarray) -> np.ndarray: simulation = ba.SpecularSimulation() simulation.setScan(scan) - total_model = ba.MultiLayer() + total_model = ba.Multilayer() for i in self.storage['model_items']: for k in range(int(self.storage['item_repeats'][i])): for j in self.storage['item'][i]: diff --git a/EasyReflectometry/calculators/calculator_base.py b/EasyReflectometry/calculators/calculator_base.py index f44e0479..bf8e7971 100644 --- a/EasyReflectometry/calculators/calculator_base.py +++ b/EasyReflectometry/calculators/calculator_base.py @@ -7,10 +7,10 @@ from easyCore.Objects.Inferface import ItemContainer from EasyReflectometry.experiment.model import Model -from EasyReflectometry.sample.items import MultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.material import MaterialMixture +from EasyReflectometry.sample import Layer +from EasyReflectometry.sample import Material +from EasyReflectometry.sample import MaterialMixture +from EasyReflectometry.sample import Multilayer from .wrapper_base import WrapperBase @@ -51,7 +51,7 @@ def reset_storage(self) -> None: """ self._wrapper.reset_storage() - def create(self, model: Material | Layer | MultiLayer | Model) -> list[ItemContainer]: + def create(self, model: Material | Layer | Multilayer | Model) -> list[ItemContainer]: """ Creation function @@ -97,7 +97,7 @@ def create(self, model: Material | Layer | MultiLayer | Model) -> list[ItemConta ) ) self.assign_material_to_layer(model.material.uid, key) - elif issubclass(t_, MultiLayer): + elif issubclass(t_, Multilayer): key = model.uid self._wrapper.create_item(key) r_list.append( @@ -121,7 +121,7 @@ def create(self, model: Material | Layer | MultiLayer | Model) -> list[ItemConta self._wrapper.update_model, ) ) - for i in model.structure: + for i in model.sample: self.add_item_to_model(i.uid, key) return r_list diff --git a/EasyReflectometry/experiment/model.py b/EasyReflectometry/experiment/model.py index 1d4f8a6b..7e88eb07 100644 --- a/EasyReflectometry/experiment/model.py +++ b/EasyReflectometry/experiment/model.py @@ -11,10 +11,10 @@ from easyCore.Objects.ObjectClasses import Parameter from EasyReflectometry.sample import Layer -from EasyReflectometry.sample import Layers -from EasyReflectometry.sample import Structure -from EasyReflectometry.sample.items import MultiLayer -from EasyReflectometry.sample.items import RepeatingMultiLayer +from EasyReflectometry.sample import LayerCollection +from EasyReflectometry.sample import Multilayer +from EasyReflectometry.sample import RepeatingMultilayer +from EasyReflectometry.sample import Sample LAYER_DETAILS = { 'scale': { @@ -47,7 +47,7 @@ class Model(BaseObj): def __init__( self, - structure: Structure, + sample: Sample, scale: Parameter, background: Parameter, resolution: Parameter, @@ -56,7 +56,7 @@ def __init__( ): super().__init__( name=name, - structure=structure, + sample=sample, scale=scale, background=background, resolution=resolution, @@ -72,16 +72,16 @@ def default(cls, interface=None) -> Model: :return: Default model container :rtype: Model """ - structure = Structure.default() + sample = Sample.default() scale = Parameter('scale', **LAYER_DETAILS['scale']) background = Parameter('background', **LAYER_DETAILS['background']) resolution = Parameter('resolution', **LAYER_DETAILS['resolution']) - return cls(structure, scale, background, resolution, interface=interface) + return cls(sample, scale, background, resolution, interface=interface) @classmethod def from_pars( cls, - structure: Structure, + sample: Sample, scale: Parameter, background: Parameter, resolution: Parameter, @@ -91,7 +91,7 @@ def from_pars( """ Constructor of a reflectometry experiment model where the parameters are known. - :param structure: The structure being modelled + :param sample: The sample being modelled :param scale: Scaling factor of profile :param background: Linear background magnitude :param background: Constant resolution smearing percentage @@ -107,7 +107,7 @@ def from_pars( resolution = Parameter('resolution', resolution, **default_options['resolution']) return cls( - structure=structure, + sample=sample, scale=scale, background=background, resolution=resolution, @@ -115,25 +115,25 @@ def from_pars( interface=interface, ) - def add_item(self, *items: Union[Layer, RepeatingMultiLayer]) -> None: + def add_item(self, *items: Union[Layer, RepeatingMultilayer]) -> None: """ - Add a layer or item to the model structure. + Add a layer or item to the model sample. - :param *items: Layers or items to add to model structure + :param *items: Layers or items to add to model sample """ for arg in items: - if issubclass(arg.__class__, MultiLayer): - self.structure.append(arg) + if issubclass(arg.__class__, Multilayer): + self.sample.append(arg) if self.interface is not None: self.interface().add_item_to_model(arg.uid, self.uid) def duplicate_item(self, idx: int) -> None: """ - Duplicate a given item or layer in a structure. + Duplicate a given item or layer in a sample. :param idx: Index of the item or layer to duplicate """ - to_duplicate = self.structure[idx] + to_duplicate = self.sample[idx] duplicate_layers = [] for i in to_duplicate.layers: duplicate_layers.append( @@ -145,7 +145,7 @@ def duplicate_item(self, idx: int) -> None: ) ) duplicate = to_duplicate.__class__.from_pars( - Layers.from_pars(*duplicate_layers, name=to_duplicate.layers.name + ' duplicate'), + LayerCollection.from_pars(*duplicate_layers, name=to_duplicate.layers.name + ' duplicate'), name=to_duplicate.name + ' duplicate', ) self.add_item(duplicate) @@ -158,8 +158,8 @@ def remove_item(self, idx) -> None: :type idx: int """ if self.interface is not None: - self.interface().remove_item_from_model(self.structure[idx].uid, self.uid) - del self.structure[idx] + self.interface().remove_item_from_model(self.sample[idx].uid, self.uid) + del self.sample[idx] @property def uid(self) -> int: @@ -181,7 +181,7 @@ def _dict_repr(self) -> dict[str, dict[str, str]]: 'scale': self.scale.raw_value, 'background': self.background.raw_value, 'resolution': f'{self.resolution.raw_value} %', - 'structure': self.structure._dict_repr, + 'sample': self.sample._dict_repr, } } diff --git a/EasyReflectometry/sample/__init__.py b/EasyReflectometry/sample/__init__.py index 87e38e05..efc74256 100644 --- a/EasyReflectometry/sample/__init__.py +++ b/EasyReflectometry/sample/__init__.py @@ -1,6 +1,25 @@ -from .layer import Layer -from .layers import Layers -from .materials import Materials -from .structure import Structure +from .assemblies.gradient_layer import GradientLayer +from .assemblies.multilayer import Multilayer +from .assemblies.repeating_multilayer import RepeatingMultilayer +from .assemblies.surfactant_layer import SurfactantLayer +from .elements.layer_collection import LayerCollection +from .elements.layers.layer import Layer +from .elements.layers.layer_apm import LayerApm +from .elements.material_collection import MaterialCollection +from .elements.materials.material import Material +from .elements.materials.material_mixture import MaterialMixture +from .sample import Sample -_ = (Layer, Layers, Materials, Structure) +__all__ = ( + GradientLayer, + Multilayer, + RepeatingMultilayer, + SurfactantLayer, + Layer, + LayerApm, + LayerCollection, + Material, + MaterialMixture, + MaterialCollection, + Sample, +) diff --git a/EasyReflectometry/sample/assemblies/base_assembly.py b/EasyReflectometry/sample/assemblies/base_assembly.py new file mode 100644 index 00000000..c271321a --- /dev/null +++ b/EasyReflectometry/sample/assemblies/base_assembly.py @@ -0,0 +1,40 @@ +from abc import abstractmethod +from typing import Any + +import yaml +from easyCore.Objects.ObjectClasses import BaseObj + + +class BaseAssembly(BaseObj): + def __init__( + self, + name: str, + interface, + **kwargs, + ): + super().__init__(name=name, **kwargs) + self.interface = interface + + @abstractmethod + def default(cls, interface=None) -> Any: + ... + + @abstractmethod + def _dict_repr(self) -> dict[str, str]: + ... + + @property + def uid(self) -> int: + """ + :return: UID from the borg map + """ + return self._borg.map.convert_id_to_key(self) + + def __repr__(self) -> str: + """ + String representation of the layer. + + :return: a string representation of the layer + :rtype: str + """ + return yaml.dump(self._dict_repr, sort_keys=False) diff --git a/EasyReflectometry/sample/items/gradient_layer.py b/EasyReflectometry/sample/assemblies/gradient_layer.py similarity index 97% rename from EasyReflectometry/sample/items/gradient_layer.py rename to EasyReflectometry/sample/assemblies/gradient_layer.py index 5bfaa8f9..a1898f85 100644 --- a/EasyReflectometry/sample/items/gradient_layer.py +++ b/EasyReflectometry/sample/assemblies/gradient_layer.py @@ -3,13 +3,12 @@ from easyCore.Fitting.Constraints import ObjConstraint from numpy import arange -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.material import Material +from ..elements.layers.layer import Layer +from ..elements.materials.material import Material +from .multilayer import Multilayer -from .multilayer import MultiLayer - -class GradientLayer(MultiLayer): +class GradientLayer(Multilayer): """ A :py:class:`GradientLayer` constructs a gradient multilayer for the provided initial and final material. diff --git a/EasyReflectometry/sample/items/multilayer.py b/EasyReflectometry/sample/assemblies/multilayer.py similarity index 67% rename from EasyReflectometry/sample/items/multilayer.py rename to EasyReflectometry/sample/assemblies/multilayer.py index e1b0b66d..d960ea52 100644 --- a/EasyReflectometry/sample/items/multilayer.py +++ b/EasyReflectometry/sample/assemblies/multilayer.py @@ -1,17 +1,13 @@ from __future__ import annotations -from typing import Union +from ..elements.layer_collection import LayerCollection +from ..elements.layers.layer import Layer +from .base_assembly import BaseAssembly -import yaml -from easyCore.Objects.ObjectClasses import BaseObj -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers - - -class MultiLayer(BaseObj): +class Multilayer(BaseAssembly): """ - A :py:class:`MultiLayer` consists of a series of + A :py:class:`Multilayer` consists of a series of :py:class:`EasyReflectometry.sample.layer.Layer` or :py:class:`EasyReflectometry.sample.layers.Layers`. This :py:mod:`item` will arrange the layers as slabs, one on top of another, @@ -25,40 +21,39 @@ class MultiLayer(BaseObj): def __init__( self, - layers: Union[Layers, Layer, list[Layer]], - name: str = 'EasyMultiLayer', + layers: LayerCollection | Layer | list[Layer], + name: str = 'EasyMultilayer', interface=None, type: str = 'Multi-layer', ): if isinstance(layers, Layer): - layers = Layers(layers, name=layers.name) + layers = LayerCollection(layers, name=layers.name) elif isinstance(layers, list): - layers = Layers(*layers, name='/'.join([layer.name for layer in layers])) + layers = LayerCollection(*layers, name='/'.join([layer.name for layer in layers])) self.type = type - super().__init__(name, layers=layers) - self.interface = interface + super().__init__(name, layers=layers, interface=interface) # Class constructors @classmethod - def default(cls, interface=None) -> MultiLayer: + def default(cls, interface=None) -> Multilayer: """ Default constructor for a multi-layer item. - :return: MultiLayer container - :rtype: MultiLayer + :return: Multilayer container + :rtype: Multilayer """ - layers = Layers.default() + layers = LayerCollection.default() return cls(layers, interface=interface) @classmethod - def from_pars(cls, layers: Layers, name: str = 'EasyMultiLayer', interface=None) -> MultiLayer: + def from_pars(cls, layers: LayerCollection, name: str = 'EasyMultilayer', interface=None) -> Multilayer: """ Constructor of a multi-layer item where the parameters are known. :param layers: The layers in the multi-layer :type layers: EasyReflectometry.layers.Layers - :return: MultiLayer container - :rtype: MultiLayer + :return: Multilayer container + :rtype: Multilayer """ return cls(layers=layers, name=name, interface=interface) @@ -102,13 +97,6 @@ def remove_layer(self, idx): self.interface().remove_layer_from_item(self.layers[idx].uid, self.uid) del self.layers[idx] - @property - def uid(self): - """ - Return a UID from the borg map - """ - return self._borg.map.convert_id_to_key(self) - # Representation @property def _dict_repr(self) -> dict: @@ -120,12 +108,3 @@ def _dict_repr(self) -> dict: if len(self.layers) == 1: return self.layers[0]._dict_repr return {self.name: self.layers._dict_repr} - - def __repr__(self) -> str: - """ - String representation of the layer. - - :return: a string representation of the layer - :rtype: str - """ - return yaml.dump(self._dict_repr, sort_keys=False) diff --git a/EasyReflectometry/sample/items/repeating_multilayer.py b/EasyReflectometry/sample/assemblies/repeating_multilayer.py similarity index 77% rename from EasyReflectometry/sample/items/repeating_multilayer.py rename to EasyReflectometry/sample/assemblies/repeating_multilayer.py index 346314de..1f704915 100644 --- a/EasyReflectometry/sample/items/repeating_multilayer.py +++ b/EasyReflectometry/sample/assemblies/repeating_multilayer.py @@ -1,15 +1,12 @@ from __future__ import annotations from copy import deepcopy -from typing import List -from typing import Union from easyCore.Objects.ObjectClasses import Parameter -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers - -from .multilayer import MultiLayer +from ..elements.layer_collection import LayerCollection +from ..elements.layers.layer import Layer +from .multilayer import Multilayer REPEATINGMULTILAYER_DETAILS = { 'repetitions': { @@ -22,12 +19,12 @@ } -class RepeatingMultiLayer(MultiLayer): +class RepeatingMultilayer(Multilayer): """ - A :py:class:`RepeatingMultiLayer` takes a :py:class:`MultiLayer` and repeats + A :py:class:`RepeatingMultilayer` takes a :py:class:`Multilayer` and repeats it a some number of times. This enables a computational efficiency in many reflectometry engines as the operation can be performed for a single - :py:class:`MultiLayer` and cheaply combined for the appropriate number of + :py:class:`Multilayer` and cheaply combined for the appropriate number of :py:attr:`repetitions`. More information about the usage of this item is available in the @@ -38,15 +35,15 @@ class RepeatingMultiLayer(MultiLayer): def __init__( self, - layers: Union[Layers, Layer, List[Layer]], + layers: LayerCollection | Layer | list[Layer], repetitions: Parameter, - name: str = 'EasyRepeatingMultiLayer', + name: str = 'EasyRepeatingMultilayer', interface=None, ): if isinstance(layers, Layer): - layers = Layers(layers, name=layers.name) + layers = LayerCollection(layers, name=layers.name) elif isinstance(layers, list): - layers = Layers(*layers, name='/'.join([layer.name for layer in layers])) + layers = LayerCollection(*layers, name='/'.join([layer.name for layer in layers])) super().__init__(layers, name, interface) self._add_component('repetitions', repetitions) self.interface = interface @@ -54,13 +51,13 @@ def __init__( # Class constructors @classmethod - def default(cls, interface=None) -> RepeatingMultiLayer: + def default(cls, interface=None) -> RepeatingMultilayer: """ Default constructor for the reflectometry repeating multi layer. :return: Default repeating multi-layer container """ - layers = Layers.default() + layers = LayerCollection.default() repetitions = Parameter('repetitions', **REPEATINGMULTILAYER_DETAILS['repetitions']) return cls( layers, @@ -71,11 +68,11 @@ def default(cls, interface=None) -> RepeatingMultiLayer: @classmethod def from_pars( cls, - layers: Layers, + layers: LayerCollection, repetitions: float = 1.0, - name: str = 'EasyRepeatingMultiLayer', + name: str = 'EasyRepeatingMultilayer', interface=None, - ) -> RepeatingMultiLayer: + ) -> RepeatingMultilayer: """ Constructor of a reflectometry repeating multi layer where the parameters are known. diff --git a/EasyReflectometry/sample/items/surfactant_layer.py b/EasyReflectometry/sample/assemblies/surfactant_layer.py similarity index 96% rename from EasyReflectometry/sample/items/surfactant_layer.py rename to EasyReflectometry/sample/assemblies/surfactant_layer.py index ad4c3bae..789c544c 100644 --- a/EasyReflectometry/sample/items/surfactant_layer.py +++ b/EasyReflectometry/sample/assemblies/surfactant_layer.py @@ -1,18 +1,15 @@ from __future__ import annotations -from typing import List - from easyCore.Fitting.Constraints import ObjConstraint from easyCore.Objects.ObjectClasses import Parameter -from EasyReflectometry.sample.layer import LayerApm -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material - -from .multilayer import MultiLayer +from ..elements.layer_collection import LayerCollection +from ..elements.layers.layer_apm import LayerApm +from ..elements.materials.material import Material +from .multilayer import Multilayer -class SurfactantLayer(MultiLayer): +class SurfactantLayer(Multilayer): """ A :py:class:`SurfactantLayer` constructs a series of layers representing the head and tail groups of a surfactant. This item allows the definition of a @@ -28,7 +25,7 @@ class SurfactantLayer(MultiLayer): def __init__( self, - layers: List[LayerApm], + layers: list[LayerApm], name: str = 'EasySurfactantLayer', constrain_apm: bool = False, conformal_roughness: bool = False, @@ -39,7 +36,7 @@ def __init__( :param tail: Tail layer object :param name: Name for surfactant layer """ - surfactant = Layers(layers[0], layers[1], name=name) + surfactant = LayerCollection(layers[0], layers[1], name=name) super().__init__(surfactant, name, interface) self.interface = interface diff --git a/EasyReflectometry/sample/elements/base_element.py b/EasyReflectometry/sample/elements/base_element.py new file mode 100644 index 00000000..fc6c8580 --- /dev/null +++ b/EasyReflectometry/sample/elements/base_element.py @@ -0,0 +1,40 @@ +from abc import abstractmethod +from typing import Any + +import yaml +from easyCore.Objects.ObjectClasses import BaseObj + + +class BaseElement(BaseObj): + def __init__( + self, + name: str, + interface, + **kwargs, + ): + super().__init__(name=name, **kwargs) + self.interface = interface + + @abstractmethod + def default(cls, interface=None) -> Any: + ... + + @abstractmethod + def _dict_repr(self) -> dict[str, str]: + ... + + @property + def uid(self) -> int: + """ + :return: UID from the borg map + """ + return self._borg.map.convert_id_to_key(self) + + def __repr__(self) -> str: + """ + String representation of the layer. + + :return: a string representation of the layer + :rtype: str + """ + return yaml.dump(self._dict_repr, sort_keys=False) diff --git a/EasyReflectometry/sample/elements/base_element_collection.py b/EasyReflectometry/sample/elements/base_element_collection.py new file mode 100644 index 00000000..586e5e81 --- /dev/null +++ b/EasyReflectometry/sample/elements/base_element_collection.py @@ -0,0 +1,41 @@ +from abc import abstractmethod +from typing import Any + +import yaml +from easyCore.Objects.Groups import BaseCollection + + +class BaseElementCollection(BaseCollection): + def __init__( + self, + name: str, + interface, + *args, + **kwargs, + ): + super().__init__(name, *args, **kwargs) + self.interface = interface + + @abstractmethod + def default(cls, interface=None) -> Any: + ... + + @abstractmethod + def _dict_repr(self) -> dict[str, str]: + ... + + @property + def uid(self) -> int: + """ + :return: UID from the borg map + """ + return self._borg.map.convert_id_to_key(self) + + def __repr__(self) -> str: + """ + String representation of the layer. + + :return: a string representation of the layer + :rtype: str + """ + return yaml.dump(self._dict_repr, sort_keys=False) diff --git a/EasyReflectometry/sample/elements/layer_collection.py b/EasyReflectometry/sample/elements/layer_collection.py new file mode 100644 index 00000000..15cefd71 --- /dev/null +++ b/EasyReflectometry/sample/elements/layer_collection.py @@ -0,0 +1,54 @@ +from __future__ import annotations + +__author__ = 'github.com/arm61' + +from .base_element_collection import BaseElementCollection +from .layers.layer import Layer + + +class LayerCollection(BaseElementCollection): + def __init__( + self, + *layers: tuple[Layer], + name: str = 'EasyLayers', + interface=None, + **kwargs, + ): + super().__init__(name, interface, *layers, **kwargs) + + # Class constructors + @classmethod + def default(cls, interface=None) -> LayerCollection: + """ + Default constructor for the reflectometry layers. + + :return: Default layers container + """ + layer1 = Layer.default() + layer2 = Layer.default() + return cls(layer1, layer2, interface=interface) + + @classmethod + def from_pars( + cls, + *layers: tuple[Layer], + name: str = 'EasyLayer', + interface=None, + ) -> LayerCollection: + """ + Constructor of a reflectometry layers where the parameters are known. + + :param args: The series of layers + :return: Layers container + """ + return cls(*layers, name=name, interface=interface) + + # Representation + @property + def _dict_repr(self) -> dict: + """ + A simplified dict representation. + + :return: Simple dictionary + """ + return {self.name: [i._dict_repr for i in self]} diff --git a/EasyReflectometry/sample/elements/layers/layer.py b/EasyReflectometry/sample/elements/layers/layer.py new file mode 100644 index 00000000..c5c6e87a --- /dev/null +++ b/EasyReflectometry/sample/elements/layers/layer.py @@ -0,0 +1,134 @@ +from __future__ import annotations + +__author__ = 'github.com/arm61' + +from copy import deepcopy +from typing import ClassVar + +from easyCore import np +from easyCore.Objects.ObjectClasses import Parameter + +from ..base_element import BaseElement +from ..materials.material import Material + +LAYER_DETAILS = { + 'thickness': { + 'description': 'The thickness of the layer in angstroms', + 'url': 'https://github.com/reflectivity/edu_outreach/blob/master/refl_maths/paper.tex', + 'value': 10.0, + 'units': 'angstrom', + 'min': 0.0, + 'max': np.Inf, + 'fixed': True, + }, + 'roughness': { + 'description': 'The interfacial roughness, Nevot-Croce, for the layer in angstroms.', + 'url': 'https://doi.org/10.1051/rphysap:01980001503076100', + 'value': 3.3, + 'units': 'angstrom', + 'min': 0.0, + 'max': np.Inf, + 'fixed': True, + }, +} + + +class Layer(BaseElement): + # Added in super().__init__ + thickness: ClassVar[Parameter] + roughness: ClassVar[Parameter] + + def __init__( + self, + material: Material, + thickness: Parameter, + roughness: Parameter, + name: str = 'EasyLayer', + interface=None, + ): + super().__init__( + name=name, + interface=interface, + material=material, + thickness=thickness, + roughness=roughness, + ) + + # Class constructors + @classmethod + def default(cls, interface=None) -> Layer: + """ + Default constructor for the reflectometry layer. + + :return: Default layer container + :rtype: Layer + """ + material = Material.default() + thickness = Parameter('thickness', **LAYER_DETAILS['thickness']) + roughness = Parameter('roughness', **LAYER_DETAILS['roughness']) + return cls( + material, + thickness, + roughness, + interface=interface, + ) + + @classmethod + def from_pars( + cls, + material: Material, + thickness: float, + roughness: float, + name: str = 'EasyLayer', + interface=None, + ) -> Layer: + """ + Constructor of a reflectometry layer where the parameters are known. + + :param material: The material that makes up the layer + :type material: EasyReflectometry.material.Material + :param thickness: Layer thickness in angstrom + :type thickness: float + :param roughness: Layer roughness in angstrom + :type roughness: float + :return: Layer container + :rtype: Layer + """ + default_options = deepcopy(LAYER_DETAILS) + del default_options['thickness']['value'] + del default_options['roughness']['value'] + + thickness = Parameter('thickness', thickness, **default_options['thickness']) + roughness = Parameter('roughness', roughness, **default_options['roughness']) + + return cls( + material=material, + thickness=thickness, + roughness=roughness, + name=name, + interface=interface, + ) + + def assign_material(self, material: Material) -> None: + """ + Assign a material to the layer interface + """ + self.material = material + if self.interface is not None: + self.interface().assign_material_to_layer(self.material.uid, self.uid) + + # Representation + @property + def _dict_repr(self) -> dict[str, str]: + """ + A simplified dict representation. + + :return: Simple dictionary + """ + return { + self.name: { + 'material': self.material._dict_repr, + 'thickness': f'{self.thickness.raw_value:.3f} {self.thickness.unit}', + 'roughness': f'{self.roughness.raw_value:.3f} {self.roughness.unit}', + } + } diff --git a/EasyReflectometry/sample/elements/layers/layer_apm.py b/EasyReflectometry/sample/elements/layers/layer_apm.py new file mode 100644 index 00000000..b6742035 --- /dev/null +++ b/EasyReflectometry/sample/elements/layers/layer_apm.py @@ -0,0 +1,295 @@ +from __future__ import annotations + +from copy import deepcopy +from typing import ClassVar + +from easyCore import np +from easyCore.Fitting.Constraints import FunctionalConstraint +from easyCore.Objects.ObjectClasses import Parameter + +from EasyReflectometry.special.calculations import apm_to_sld +from EasyReflectometry.special.calculations import neutron_scattering_length + +from ..materials.material import Material +from ..materials.material_mixture import MaterialMixture +from .layer import Layer + +LAYERAPM_DETAILS = { + 'thickness': { + 'description': 'The thickness of the layer in angstroms', + 'value': 10.0, + 'units': 'angstrom', + 'min': 0, + 'max': np.inf, + 'fixed': True, + }, + 'chemical_structure': 'C10H18NO8P', + 'roughness': { + 'description': 'Conformal roughness', + 'value': 3.0, + 'units': 'angstrom', + 'min': 0, + 'max': np.inf, + 'fixed': True, + }, + 'area_per_molecule': { + 'description': 'Surface coverage', + 'value': 48.2, + 'units': 'angstrom ** 2', + 'min': 0, + 'max': np.inf, + 'fixed': True, + }, + 'solvation': { + 'description': 'Fraction of solvent present', + 'value': 0.2, + 'units': 'dimensionless', + 'min': 0, + 'max': 1, + 'fixed': True, + }, + 'sl': { + 'description': 'The real scattering length for a chemical formula in angstrom.', + 'url': 'https://www.ncnr.nist.gov/resources/activation/', + 'value': 4.186, + 'units': 'angstrom', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, + 'isl': { + 'description': 'The real scattering length for a chemical formula in angstrom.', + 'url': 'https://www.ncnr.nist.gov/resources/activation/', + 'value': 0.0, + 'units': 'angstrom', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, +} + + +class LayerApm(Layer): + """ + The :py:class:`LayerApm` class allows a layer to be defined in terms of some + chemical structure (given as a chemical formula) and area per molecule. + + """ + + solvation: ClassVar[Parameter] + # Added in __init__ + area_per_molecule: ClassVar[Parameter] + scattering_length_real: ClassVar[Parameter] + scattering_length_imag: ClassVar[Parameter] + roughness: ClassVar[Parameter] + + # Passed in __init__ + material: MaterialMixture + + def __init__( + self, + chemical_structure: str, + thickness: Parameter, + solvent: Material, + solvation: Parameter, + area_per_molecule: Parameter, + roughness: Parameter, + name: str = 'EasyLayerApm', + interface=None, + ): + """ + :param chemical_structure: Chemical formula for the material in the layer + :param thickness: Layer thickness + :param solvent: Solvent present in material + :param solvation: Fraction of solvent present + :param area_per_molecule: Area per molecule for the chemical material + :param roughness: Upper roughness on the layer + :param name: Identifier, defaults to :py:attr:`EasyLayerApm` + :param interface: Interface object, defaults to :py:attr:`None` + """ + scattering_length = neutron_scattering_length(chemical_structure) + default_options = deepcopy(LAYERAPM_DETAILS) + del default_options['sl']['value'] + del default_options['isl']['value'] + scattering_length_real = Parameter('scattering_length_real', scattering_length.real, **default_options['sl']) + scattering_length_imag = Parameter('scattering_length_imag', scattering_length.imag, **default_options['isl']) + sld = apm_to_sld(scattering_length_real.raw_value, thickness.raw_value, area_per_molecule.raw_value) + isld = apm_to_sld(scattering_length_imag.raw_value, thickness.raw_value, area_per_molecule.raw_value) + + material = Material.from_pars(sld, isld, name=chemical_structure, interface=interface) + + constraint = FunctionalConstraint(material.sld, apm_to_sld, [scattering_length_real, thickness, area_per_molecule]) + thickness.user_constraints['apm'] = constraint + area_per_molecule.user_constraints['apm'] = constraint + scattering_length_real.user_constraints['apm'] = constraint + + iconstraint = FunctionalConstraint(material.isld, apm_to_sld, [scattering_length_imag, thickness, area_per_molecule]) + thickness.user_constraints['iapm'] = iconstraint + area_per_molecule.user_constraints['iapm'] = iconstraint + scattering_length_imag.user_constraints['iapm'] = iconstraint + + solvated_material = MaterialMixture( + material, + solvent, + solvation, + name=chemical_structure + '/' + solvent.name, + interface=interface, + ) + super().__init__( + material=solvated_material, + thickness=thickness, + roughness=roughness, + name=name, + interface=interface, + ) + self._add_component('scattering_length_real', scattering_length_real) + self._add_component('scattering_length_imag', scattering_length_imag) + self._add_component('area_per_molecule', area_per_molecule) + self._add_component('roughness', roughness) + self._chemical_structure = chemical_structure + self.interface = interface + + @property + def solvent(self) -> Material: + """ + :return: Solvent material + """ + return self.material.material_b + + @solvent.setter + def solvent(self, new_solvent: Material) -> None: + """ + :param new_solvent: New solvent material. + """ + self.material.material_b = new_solvent + + @property + def solvation(self) -> Parameter: + """ + :return: Solvation fraction. + """ + return self.material.fraction + + @solvation.setter + def solvation(self, fraction: float) -> None: + """ + :param fraction: Fraction of solvent. + """ + self.material.fraction = fraction + + # Class constructors + @classmethod + def default(cls, interface=None) -> LayerApm: + """ + Default constructor for layer defined from chemical structure + and area per molecule. + + :param interface: Interface object, defaults to :py:attr:`None` + :return: Layer with correct structure + """ + area_per_molecule = Parameter('area_per_molecule', **LAYERAPM_DETAILS['area_per_molecule']) + thickness = Parameter('thickness', **LAYERAPM_DETAILS['thickness']) + roughness = Parameter('roughness', **LAYERAPM_DETAILS['roughness']) + solvent = Material.from_pars(6.36, 0, 'D2O', interface=interface) + solvation = Parameter('solvation', **LAYERAPM_DETAILS['solvation']) + return cls( + LAYERAPM_DETAILS['chemical_structure'], + thickness, + solvent, + solvation, + area_per_molecule, + roughness, + interface=interface, + ) + + @classmethod + def from_pars( + cls, + chemical_structure: str, + thickness: float, + solvent: Material, + solvation: float, + area_per_molecule: float, + roughness: float, + name: str = 'EasyLayerApm', + interface=None, + ) -> LayerApm: + """ + Constructor for a layer described with the area per molecule, + where the parameters are known. + + :param chemical_structure: Chemical formula for the material in the layer + :param thickness: Layer thickness + :param solvent: Solvent present in material + :param solvation: Fraction of solvent present + :param area_per_molecule: Area per molecule for the chemical material + :param roughness: Upper roughness on the layer + :param name: Identifier, defaults to :py:attr:`EasyLayerApm` + :param interface: Interface object, defaults to :py:attr:`None` + :return: Layer with correct structure + """ + default_options = deepcopy(LAYERAPM_DETAILS) + del default_options['area_per_molecule']['value'] + del default_options['thickness']['value'] + del default_options['roughness']['value'] + del default_options['solvation']['value'] + del default_options['chemical_structure'] + + area_per_molecule = Parameter('area_per_molecule', area_per_molecule, **default_options['area_per_molecule']) + thickness = Parameter('thickness', thickness, **default_options['thickness']) + roughness = Parameter('roughness', roughness, **default_options['roughness']) + solvation = Parameter('solvation', solvation, **default_options['solvation']) + + return cls( + chemical_structure, + thickness, + solvent, + solvation, + area_per_molecule, + roughness, + name=name, + interface=interface, + ) + + @property + def chemical_structure(self) -> str: + """ + :return: Chemical structure + """ + return self._chemical_structure + + @chemical_structure.setter + def chemical_structure(self, structure_string: str) -> None: + """ + :param structure_string: String that defines the chemical structure. + """ + self._chemical_structure = structure_string + scattering_length = neutron_scattering_length(structure_string) + self.scattering_length_real.value = scattering_length.real + self.scattering_length_imag.value = scattering_length.imag + self.material.name = structure_string + '/' + self.material._material_b.name + + @property + def _dict_repr(self) -> dict[str, str]: + """ + Dictionary representation of the :py:class:`LayerApm` object. + + :return: Simple dictionary + """ + layerapm_dict = super()._dict_repr + layerapm_dict['chemical_structure'] = self._chemical_structure + layerapm_dict['area_per_molecule'] = f'{self.area_per_molecule.raw_value:.1f} ' f'{self.area_per_molecule.unit}' + return layerapm_dict + + def as_dict(self, skip: list = None) -> dict[str, str]: + """ + Custom as_dict method to skip necessary things. + + :return: Cleaned dictionary. + """ + if skip is None: + skip = [] + this_dict = super().as_dict(skip=skip) + del this_dict['material'], this_dict['scattering_length_real'] + del this_dict['scattering_length_imag'] + return this_dict diff --git a/EasyReflectometry/sample/elements/material_collection.py b/EasyReflectometry/sample/elements/material_collection.py new file mode 100644 index 00000000..52256d06 --- /dev/null +++ b/EasyReflectometry/sample/elements/material_collection.py @@ -0,0 +1,70 @@ +from __future__ import annotations + +__author__ = 'github.com/arm61' + +from .base_element_collection import BaseElementCollection +from .materials.material import Material +from .materials.material_mixture import MaterialMixture + + +class MaterialCollection(BaseElementCollection): + def __init__( + self, + *materials: tuple[Material | MaterialMixture], + name: str = 'EasyMaterials', + interface=None, + **kwargs, + ): + super().__init__( + name, + interface, + *materials, + **kwargs, + ) + + # Class constructors + @classmethod + def default(cls, interface=None) -> MaterialCollection: + """ + Default constructor for materials. + + :return: Default materials container + :rtype: Materials + """ + material1 = Material.default() + material2 = Material.default() + return cls(material1, material2, interface=interface) + + @classmethod + def from_pars( + cls, + *materials: tuple[Material | MaterialMixture], + name: str = 'EasyMaterials', + interface=None, + ) -> MaterialCollection: + """ + Constructor of materials where the parameters are known. + + :param args: The series of material + :type args: list[Material | MaterialMixture] + :return: Materials container + :rtype: Materials + """ + return cls(*materials, name=name, interface=interface) + + @property + def names(self) -> list: + """ + :returns: list of names for the materials. + """ + return [i.name for i in self] + + # Representation + @property + def _dict_repr(self) -> dict: + """ + A simplified dict representation. + + :return: Simple dictionary + """ + return {self.name: [i._dict_repr for i in self]} diff --git a/EasyReflectometry/sample/elements/materials/material.py b/EasyReflectometry/sample/elements/materials/material.py new file mode 100644 index 00000000..7afcf8ec --- /dev/null +++ b/EasyReflectometry/sample/elements/materials/material.py @@ -0,0 +1,98 @@ +from __future__ import annotations + +__author__ = 'github.com/arm61' + +from copy import deepcopy +from typing import ClassVar + +from easyCore import np +from easyCore.Objects.ObjectClasses import Parameter + +from ..base_element import BaseElement + +MATERIAL_DEFAULTS = { + 'sld': { + 'description': 'The real scattering length density for a material in e-6 per squared angstrom.', + 'url': 'https://www.ncnr.nist.gov/resources/activation/', + 'value': 4.186, + 'units': '1 / angstrom ** 2', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, + 'isld': { + 'description': 'The imaginary scattering length density for a material in e-6 per squared angstrom.', + 'url': 'https://www.ncnr.nist.gov/resources/activation/', + 'value': 0.0, + 'units': '1 / angstrom ** 2', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, +} + + +class Material(BaseElement): + # Added in super().__init__ + sld: ClassVar[Parameter] + isld: ClassVar[Parameter] + + def __init__( + self, + sld: Parameter, + isld: Parameter, + name: str = 'EasyMaterial', + interface=None, + ): + super().__init__(name=name, interface=interface, sld=sld, isld=isld) + + # Class constructors + @classmethod + def default(cls, interface=None) -> Material: + """ + Default constructor for the reflectometry material. + + :return: Default material container + """ + sld = Parameter('sld', **MATERIAL_DEFAULTS['sld']) + isld = Parameter('isld', **MATERIAL_DEFAULTS['isld']) + return cls(sld, isld, interface=interface) + + @classmethod + def from_pars( + cls, + sld: float, + isld: float, + name: str = 'EasyMaterial', + interface=None, + ) -> Material: + """ + Constructor of a reflectometry material where the parameters are known. + + :param sld: Real scattering length density + :param isld: Imaginary scattering length density + :return: Material container + """ + default_options = deepcopy(MATERIAL_DEFAULTS) + del default_options['sld']['value'] + del default_options['isld']['value'] + + sld = Parameter('sld', sld, **default_options['sld']) + isld = Parameter('isld', isld, **default_options['isld']) + + return cls(sld=sld, isld=isld, name=name, interface=interface) + + # Representation + @property + def _dict_repr(self) -> dict: + """ + A simplified dict representation. + + :return: Simple dictionary + """ + return { + self.name: { + 'sld': f'{self.sld.raw_value:.3f}e-6 {self.sld.unit}', + 'isld': f'{self.isld.raw_value:.3f}e-6 {self.isld.unit}', + } + } diff --git a/EasyReflectometry/sample/elements/materials/material_density.py b/EasyReflectometry/sample/elements/materials/material_density.py new file mode 100644 index 00000000..efcf7418 --- /dev/null +++ b/EasyReflectometry/sample/elements/materials/material_density.py @@ -0,0 +1,192 @@ +from __future__ import annotations + +from copy import deepcopy +from typing import ClassVar + +import numpy as np +from easyCore.Fitting.Constraints import FunctionalConstraint +from easyCore.Objects.ObjectClasses import Parameter + +from EasyReflectometry.special.calculations import density_to_sld +from EasyReflectometry.special.calculations import molecular_weight +from EasyReflectometry.special.calculations import neutron_scattering_length + +from .material import MATERIAL_DEFAULTS +from .material import Material + +MATERIALDENSITY_DEFAULTS = { + 'chemical_structure': 'Si', + 'sl': { + 'description': 'The real scattering length for a chemical formula in angstrom.', + 'url': 'https://www.ncnr.nist.gov/resources/activation/', + 'value': 4.1491, + 'units': 'angstrom', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, + 'isl': { + 'description': 'The real scattering length for a chemical formula in angstrom.', + 'url': 'https://www.ncnr.nist.gov/resources/activation/', + 'value': 0.0, + 'units': 'angstrom', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, + 'density': { + 'description': 'The mass density of the material.', + 'url': 'https://en.wikipedia.org/wiki/Density', + 'value': 2.33, + 'units': 'gram / centimeter ** 3', + 'min': 0, + 'max': np.Inf, + 'fixed': True, + }, + 'molecular_weight': { + 'description': 'The molecular weight of a material.', + 'url': 'https://en.wikipedia.org/wiki/Molecular_mass', + 'value': 28.02, + 'units': 'g / mole', + 'min': -np.Inf, + 'max': np.Inf, + 'fixed': True, + }, +} + + +class MaterialDensity(Material): + # Added in __init__ + scattering_length_real: ClassVar[Parameter] + scattering_length_imag: ClassVar[Parameter] + molecular_weight: ClassVar[Parameter] + density: ClassVar[Parameter] + + def __init__( + self, + chemical_structure: str, + density: Parameter, + name: str = 'EasyMaterialDensity', + interface=None, + ) -> MaterialDensity: + """ + :param chemical_structure: Chemical formula for the material + :param density: Mass density for the material + :param name: Identifier, defaults to :py:attr:`EasyMaterialDensity` + :param interface: Interface object, defaults to :py:attr:`None` + """ + scattering_length = neutron_scattering_length(chemical_structure) + default_options = deepcopy(MATERIALDENSITY_DEFAULTS) + del default_options['molecular_weight']['value'] + del default_options['sl']['value'] + del default_options['isl']['value'] + mw = Parameter('molecular_weight', molecular_weight(chemical_structure), **default_options['molecular_weight']) + scattering_length_real = Parameter('scattering_length_real', scattering_length.real, **default_options['sl']) + scattering_length_imag = Parameter('scattering_length_imag', scattering_length.imag, **default_options['isl']) + default_options = deepcopy(MATERIAL_DEFAULTS) + del default_options['sld']['value'] + del default_options['isld']['value'] + sld = Parameter( + 'sld', + density_to_sld(scattering_length_real.raw_value, mw.raw_value, density.raw_value), + **default_options['sld'], + ) + isld = Parameter( + 'isld', + density_to_sld(scattering_length_imag.raw_value, mw.raw_value, density.raw_value), + **default_options['isld'], + ) + + constraint = FunctionalConstraint(sld, density_to_sld, [scattering_length_real, mw, density]) + scattering_length_real.user_constraints['sld'] = constraint + mw.user_constraints['sld'] = constraint + density.user_constraints['sld'] = constraint + iconstraint = FunctionalConstraint(isld, density_to_sld, [scattering_length_imag, mw, density]) + scattering_length_imag.user_constraints['isld'] = iconstraint + mw.user_constraints['isld'] = iconstraint + density.user_constraints['isld'] = iconstraint + + super().__init__(sld, isld, name=name, interface=interface) + + self._add_component('scattering_length_real', scattering_length_real) + self._add_component('scattering_length_imag', scattering_length_imag) + self._add_component('molecular_weight', mw) + self._add_component('density', density) + self._chemical_structure = chemical_structure + self.interface = interface + + @property + def chemical_structure(self) -> str: + """ + :returns: Chemical structure string + """ + return self._chemical_structure + + @chemical_structure.setter + def chemical_structure(self, structure_string: str) -> None: + """ + :param structure_string: String that defines the chemical structure. + """ + self._chemical_structure = structure_string + scattering_length = neutron_scattering_length(structure_string) + self.scattering_length_real.value = scattering_length.real + self.scattering_length_imag.value = scattering_length.imag + + # Class constructors + @classmethod + def default(cls, interface=None) -> MaterialDensity: + """ + Default constructor for the material defined by density and chemical structure. + + :param interface: Interface object, defaults to :py:attr:`None` + :return: Material container + """ + density = Parameter('density', **MATERIALDENSITY_DEFAULTS['density']) + return cls(MATERIALDENSITY_DEFAULTS['chemical_structure'], density, interface=interface) + + @classmethod + def from_pars( + cls, + chemical_structure: str, + density: float, + name: str = 'EasyMaterialDensity', + interface=None, + ) -> MaterialDensity: + """ + Constructor for a material based on the mass density and chemical structure, + where these are known. + :param chemical_structure: Chemical formula for the material + :param density: Mass density for the material + :param name: Identifier, defaults to :py:attr:`EasyMaterialDensity` + :param interface: Interface object, defaults to :py:attr:`None` + :return: Material container + """ + default_options = deepcopy(MATERIALDENSITY_DEFAULTS) + del default_options['density']['value'] + + density = Parameter('density', density, **default_options['density']) + + return cls(chemical_structure, density, name=name, interface=interface) + + @property + def _dict_repr(self) -> dict[str, str]: + """ + Dictionary representation of the :py:class:`MaterialDensity` object. + + :return: Simple dictionary + """ + mat_dict = super()._dict_repr + mat_dict['chemical_structure'] = self._chemical_structure + mat_dict['density'] = f'{self.density.raw_value:.2e} {self.density.unit}' + return mat_dict + + def as_dict(self, skip: list = []) -> dict[str, str]: + """ + Custom as_dict method to skip necessary things. + + :return: Cleaned dictionary. + """ + this_dict = super().as_dict(skip=skip) + del this_dict['sld'], this_dict['isld'], this_dict['scattering_length_real'] + del this_dict['scattering_length_imag'], this_dict['molecular_weight'] + return this_dict diff --git a/EasyReflectometry/sample/elements/materials/material_mixture.py b/EasyReflectometry/sample/elements/materials/material_mixture.py new file mode 100644 index 00000000..70a19fe1 --- /dev/null +++ b/EasyReflectometry/sample/elements/materials/material_mixture.py @@ -0,0 +1,199 @@ +from __future__ import annotations + +from copy import deepcopy +from typing import ClassVar + +from easyCore.Fitting.Constraints import FunctionalConstraint +from easyCore.Objects.ObjectClasses import Parameter + +from EasyReflectometry.special.calculations import weighted_average_sld + +from ..base_element import BaseElement +from .material import MATERIAL_DEFAULTS +from .material import Material + +MATERIALMIXTURE_DEFAULTS = { + 'fraction': { + 'description': 'The fraction of material b in material a', + 'value': 0.5, + 'units': 'dimensionless', + 'min': 0, + 'max': 1, + 'fixed': True, + } +} + + +class MaterialMixture(BaseElement): + # Added in super().__init__ + fraction: ClassVar[Parameter] + + def __init__( + self, + material_a: Material, + material_b: Material, + fraction: Parameter, + name=None, + interface=None, + ): + if name is None: + name = material_a.name + '/' + material_b.name + super().__init__( + name, + _material_a=material_a, + _material_b=material_b, + fraction=fraction, + interface=interface, + ) + sld = weighted_average_sld(self._material_a.sld.raw_value, self._material_b.sld.raw_value, self.fraction.raw_value) + isld = weighted_average_sld(self._material_a.isld.raw_value, self._material_b.isld.raw_value, self.fraction.raw_value) + default_options = deepcopy(MATERIAL_DEFAULTS) + del default_options['sld']['value'] + del default_options['isld']['value'] + self._slds = [ + Parameter('sld', sld, **default_options['sld']), + Parameter('isld', isld, **default_options['isld']), + ] + self._materials_constraints() + self.interface = interface + + def _get_linkable_attributes(self): + return self._slds + + @property + def sld(self): + return self._slds[0] + + @property + def isld(self): + return self._slds[1] + + def _materials_constraints(self): + self._slds[0].enabled = True + self._slds[1].enabled = True + constraint = FunctionalConstraint( + self._slds[0], weighted_average_sld, [self._material_a.sld, self._material_b.sld, self.fraction] + ) + self._material_a.sld.user_constraints['sld'] = constraint + self._material_b.sld.user_constraints['sld'] = constraint + self.fraction.user_constraints['sld'] = constraint + constraint() + iconstraint = FunctionalConstraint( + self._slds[1], weighted_average_sld, [self._material_a.isld, self._material_b.isld, self.fraction] + ) + self._material_a.isld.user_constraints['isld'] = iconstraint + self._material_b.isld.user_constraints['isld'] = iconstraint + self.fraction.user_constraints['isld'] = iconstraint + iconstraint() + + @property + def material_a(self) -> Material: + """ + :return: the first material. + """ + return self._material_a + + @material_a.setter + def material_a(self, new_material_a: Material) -> None: + """ + Setter for material_a + + :param new_material_a: New material_a + """ + self.name = new_material_a.name + '/' + self._material_b.name + self._material_a = new_material_a + self._materials_constraints() + if self.interface is not None: + self.interface.generate_bindings(self) + + @property + def material_b(self) -> Material: + """ + :return: the second material. + """ + return self._material_b + + @material_b.setter + def material_b(self, new_material_b: Material) -> None: + """ + Setter for material_b + + :param new_material_b: New material_b + """ + self.name = self._material_a.name + '/' + new_material_b.name + self._material_b = new_material_b + self._materials_constraints() + if self.interface is not None: + self.interface.generate_bindings(self) + + # Class constructors + @classmethod + def default(cls, interface=None) -> MaterialMixture: + """ + Default constructor for a mixture of two materials. + + :return: Default material mixture container. + """ + material_a = Material.default() + material_b = Material.default() + fraction = Parameter('fraction', **MATERIALMIXTURE_DEFAULTS['fraction']) + return cls( + material_a, + material_b, + fraction, + interface=interface, + ) + + @classmethod + def from_pars( + cls, + material_a: Material, + material_b: Material, + fraction: float, + name=None, + interface=None, + ) -> MaterialMixture: + """ + Constructor of a mixture of two materials where the parameters are known. + + :param material_a: The first material + :param material_b: The second material + :param fraction: The fraction of material_b in material_a + :return: MaterialMixture container. + """ + default_options = deepcopy(MATERIALMIXTURE_DEFAULTS) + del default_options['fraction']['value'] + fraction = Parameter('fraction', fraction, **default_options['fraction']) + + return cls(material_a=material_a, material_b=material_b, fraction=fraction, name=name, interface=interface) + + # Representation + @property + def _dict_repr(self) -> dict[str, str]: + """ + A simplified dict representation. + + :return: Simple dictionary + """ + return { + self.name: { + 'fraction': self.fraction.raw_value, + 'sld': f'{self.sld.raw_value:.3f}e-6 {self.sld.unit}', + 'isld': f'{self.isld.raw_value:.3f}e-6 {self.isld.unit}', + 'material1': self.material_a._dict_repr, + 'material2': self.material_b._dict_repr, + } + } + + def as_dict(self, skip: list = None) -> dict[str, str]: + """ + Custom as_dict method to skip necessary things. + + :return: Cleaned dictionary. + """ + if skip is None: + skip = [] + this_dict = super().as_dict(skip=skip) + this_dict['material_a'] = self._material_a + this_dict['material_b'] = self._material_b + return this_dict diff --git a/EasyReflectometry/sample/items/__init__.py b/EasyReflectometry/sample/items/__init__.py deleted file mode 100644 index ada54043..00000000 --- a/EasyReflectometry/sample/items/__init__.py +++ /dev/null @@ -1,16 +0,0 @@ -"""The :py:mod:`item` library is the backbone of :py:mod:`EasyReflectometry`. -An :py:mod:`EasyReflectometry.sample.item` allows for the inclusion of physical and -chemical parameterisation into our reflectometry model. -For more information please look at the `item library documentation`_ - -.. _`item library documentation`: ./library.html -""" -__author__ = "github.com/arm61" - -from .gradient_layer import GradientLayer -from .multilayer import MultiLayer -from .repeating_multilayer import RepeatingMultiLayer -from .surfactant_layer import SurfactantLayer - -# Define the __all__ so that the classes can be imported from the module -__all__ = MultiLayer, RepeatingMultiLayer, SurfactantLayer, GradientLayer diff --git a/EasyReflectometry/sample/layer.py b/EasyReflectometry/sample/layer.py deleted file mode 100644 index 4953de8b..00000000 --- a/EasyReflectometry/sample/layer.py +++ /dev/null @@ -1,417 +0,0 @@ -__author__ = 'github.com/arm61' - -from copy import deepcopy -from typing import ClassVar - -import yaml -from easyCore import np -from easyCore.Fitting.Constraints import FunctionalConstraint -from easyCore.Objects.ObjectClasses import BaseObj -from easyCore.Objects.ObjectClasses import Parameter - -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.material import MaterialMixture -from EasyReflectometry.special.calculations import apm_to_sld -from EasyReflectometry.special.calculations import neutron_scattering_length - -LAYER_DETAILS = { - 'thickness': { - 'description': 'The thickness of the layer in angstroms', - 'url': - 'https://github.com/reflectivity/edu_outreach/blob/master/refl_maths/paper.tex', - 'value': 10.0, - 'units': 'angstrom', - 'min': 0.0, - 'max': np.Inf, - 'fixed': True - }, - 'roughness': { - 'description': - 'The interfacial roughness, Nevot-Croce, for the layer in angstroms.', - 'url': 'https://doi.org/10.1051/rphysap:01980001503076100', - 'value': 3.3, - 'units': 'angstrom', - 'min': 0.0, - 'max': np.Inf, - 'fixed': True - } -} - -LAYERAPM_DETAILS = { - 'thickness': { - 'description': 'The thickness of the layer in angstroms', - 'value': 10.0, - 'units': 'angstrom', - 'min': 0, - 'max': np.inf, - 'fixed': True - }, - 'chemical_structure': 'C10H18NO8P', - 'roughness': { - 'description': 'Conformal roughness', - 'value': 3.0, - 'units': 'angstrom', - 'min': 0, - 'max': np.inf, - 'fixed': True - }, - 'area_per_molecule': { - 'description': 'Surface coverage', - 'value': 48.2, - 'units': 'angstrom ** 2', - 'min': 0, - 'max': np.inf, - 'fixed': True - }, - 'solvation': { - 'description': 'Fraction of solvent present', - 'value': 0.2, - 'units': 'dimensionless', - 'min': 0, - 'max': 1, - 'fixed': True - }, - 'sl': { - 'description': 'The real scattering length for a chemical formula in angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 4.186, - 'units': 'angstrom', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - }, - 'isl': { - 'description': 'The real scattering length for a chemical formula in angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 0.0, - 'units': 'angstrom', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - } -} - - -class Layer(BaseObj): - - def __init__(self, - material: Material, - thickness: Parameter, - roughness: Parameter, - name: str = 'EasyLayer', - interface=None): - super().__init__(name, - material=material, - thickness=thickness, - roughness=roughness) - self.interface = interface - - # Class constructors - @classmethod - def default(cls, interface=None) -> "Layer": - """ - Default constructor for the reflectometry layer. - - :return: Default layer container - :rtype: Layer - """ - material = Material.default() - thickness = Parameter('thickness', **LAYER_DETAILS['thickness']) - roughness = Parameter('roughness', **LAYER_DETAILS['roughness']) - return cls(material, thickness, roughness, interface=interface) - - @classmethod - def from_pars(cls, - material: Material, - thickness: float, - roughness: float, - name: str = 'EasyLayer', - interface=None) -> "Layer": - """ - Constructor of a reflectometry layer where the parameters are known. - - :param material: The material that makes up the layer - :type material: EasyReflectometry.material.Material - :param thickness: Layer thickness in angstrom - :type thickness: float - :param roughness: Layer roughness in angstrom - :type roughness: float - :return: Layer container - :rtype: Layer - """ - default_options = deepcopy(LAYER_DETAILS) - del default_options['thickness']['value'] - del default_options['roughness']['value'] - - thickness = Parameter('thickness', thickness, **default_options['thickness']) - roughness = Parameter('roughness', roughness, **default_options['roughness']) - - return cls(material=material, - thickness=thickness, - roughness=roughness, - name=name, - interface=interface) - - def assign_material(self, material): - """ - Assign a material to the layer interface - """ - self.material = material - if self.interface is not None: - self.interface().assign_material_to_layer(self.material.uid, self.uid) - - @property - def uid(self) -> int: - """ - :return: UID from the borg map - """ - return self._borg.map.convert_id_to_key(self) - - # Representation - @property - def _dict_repr(self) -> dict: - """ - A simplified dict representation. - - :return: Simple dictionary - """ - return { - self.name: { - 'material': self.material._dict_repr, - 'thickness': f'{self.thickness.raw_value:.3f} {self.thickness.unit}', - 'roughness': f'{self.roughness.raw_value:.3f} {self.roughness.unit}' - } - } - - def __repr__(self) -> str: - """ - String representation of the layer. - - :return: a string representation of the layer - :rtype: str - """ - return yaml.dump(self._dict_repr, sort_keys=False) - - -class LayerApm(Layer): - """ - The :py:class:`LayerApm` class allows a layer to be defined in terms of some - chemical structure (given as a chemical formula) and area per molecule. - - """ - area_per_molecule: ClassVar[Parameter] - solvation: ClassVar[Parameter] - - def __init__(self, - chemical_structure: str, - thickness: Parameter, - solvent: Material, - solvation: Parameter, - area_per_molecule: Parameter, - roughness: Parameter, - name: str = 'EasyLayerApm', - interface=None): - """ - :param chemical_structure: Chemical formula for the material in the layer - :param thickness: Layer thickness - :param solvent: Solvent present in material - :param solvation: Fraction of solvent present - :param area_per_molecule: Area per molecule for the chemical material - :param roughness: Upper roughness on the layer - :param name: Identifier, defaults to :py:attr:`EasyLayerApm` - :param interface: Interface object, defaults to :py:attr:`None` - """ - scattering_length = neutron_scattering_length(chemical_structure) - default_options = deepcopy(LAYERAPM_DETAILS) - del default_options['sl']['value'] - del default_options['isl']['value'] - scattering_length_real = Parameter('scattering_length_real', - scattering_length.real, - **default_options['sl']) - scattering_length_imag = Parameter('scattering_length_imag', - scattering_length.imag, - **default_options['isl']) - sld = apm_to_sld(scattering_length_real.raw_value, thickness.raw_value, - area_per_molecule.raw_value) - isld = apm_to_sld(scattering_length_imag.raw_value, thickness.raw_value, - area_per_molecule.raw_value) - - material = Material.from_pars(sld, - isld, - name=chemical_structure, - interface=interface) - - constraint = FunctionalConstraint( - material.sld, apm_to_sld, - [scattering_length_real, thickness, area_per_molecule]) - thickness.user_constraints['apm'] = constraint - area_per_molecule.user_constraints['apm'] = constraint - scattering_length_real.user_constraints['apm'] = constraint - - iconstraint = FunctionalConstraint( - material.isld, apm_to_sld, - [scattering_length_imag, thickness, area_per_molecule]) - thickness.user_constraints['iapm'] = iconstraint - area_per_molecule.user_constraints['iapm'] = iconstraint - scattering_length_imag.user_constraints['iapm'] = iconstraint - - solvated_material = MaterialMixture(material, - solvent, - solvation, - name=chemical_structure + '/' + - solvent.name, - interface=interface) - super().__init__(material=solvated_material, - thickness=thickness, - roughness=roughness, - name=name, - interface=interface) - self._add_component('scattering_length_real', scattering_length_real) - self._add_component('scattering_length_imag', scattering_length_imag) - self._add_component('area_per_molecule', area_per_molecule) - self._add_component('roughness', roughness) - self._chemical_structure = chemical_structure - self.interface = interface - - @property - def solvent(self) -> Material: - """ - :return: Solvent material - """ - return self.material.material_b - - @solvent.setter - def solvent(self, new_solvent): - """ - :param new_solvent: New solvent material. - """ - self.material.material_b = new_solvent - - @property - def solvation(self) -> Parameter: - """ - :return: Solvation fraction. - """ - return self.material.fraction - - @solvation.setter - def solvation(self, fraction: float): - """ - :param fraction: Fraction of solvent. - """ - self.material.fraction = fraction - - # Class constructors - @classmethod - def default(cls, interface=None) -> "LayerApm": - """ - Default constructor for layer defined from chemical structure - and area per molecule. - - :param interface: Interface object, defaults to :py:attr:`None` - :return: Layer with correct structure - """ - area_per_molecule = Parameter('area_per_molecule', - **LAYERAPM_DETAILS['area_per_molecule']) - thickness = Parameter('thickness', **LAYERAPM_DETAILS['thickness']) - roughness = Parameter('roughness', **LAYERAPM_DETAILS['roughness']) - solvent = Material.from_pars(6.36, 0, 'D2O', interface=interface) - solvation = Parameter('solvation', **LAYERAPM_DETAILS['solvation']) - return cls(LAYERAPM_DETAILS['chemical_structure'], - thickness, - solvent, - solvation, - area_per_molecule, - roughness, - interface=interface) - - @classmethod - def from_pars(cls, - chemical_structure: str, - thickness: float, - solvent: Material, - solvation: float, - area_per_molecule: float, - roughness: float, - name: str = 'EasyLayerApm', - interface=None) -> "LayerApm": - """ - Constructor for a layer described with the area per molecule, - where the parameters are known. - - :param chemical_structure: Chemical formula for the material in the layer - :param thickness: Layer thickness - :param solvent: Solvent present in material - :param solvation: Fraction of solvent present - :param area_per_molecule: Area per molecule for the chemical material - :param roughness: Upper roughness on the layer - :param name: Identifier, defaults to :py:attr:`EasyLayerApm` - :param interface: Interface object, defaults to :py:attr:`None` - :return: Layer with correct structure - """ - default_options = deepcopy(LAYERAPM_DETAILS) - del default_options['area_per_molecule']['value'] - del default_options['thickness']['value'] - del default_options['roughness']['value'] - del default_options['solvation']['value'] - del default_options['chemical_structure'] - - area_per_molecule = Parameter('area_per_molecule', area_per_molecule, - **default_options['area_per_molecule']) - thickness = Parameter('thickness', thickness, **default_options['thickness']) - roughness = Parameter('roughness', roughness, **default_options['roughness']) - solvation = Parameter('solvation', solvation, **default_options['solvation']) - - return cls(chemical_structure, - thickness, - solvent, - solvation, - area_per_molecule, - roughness, - name=name, - interface=interface) - - @property - def chemical_structure(self) -> str: - """ - :return: Chemical structure - """ - return self._chemical_structure - - @chemical_structure.setter - def chemical_structure(self, structure_string: str): - """ - :param structure_string: String that defines the chemical structure. - """ - self._chemical_structure = structure_string - scattering_length = neutron_scattering_length(structure_string) - self.scattering_length_real.value = scattering_length.real - self.scattering_length_imag.value = scattering_length.imag - self.material.name = structure_string + '/' + self.material._material_b.name - - @property - def _dict_repr(self) -> dict: - """ - Dictionary representation of the :py:class:`LayerApm` object. - - :return: Simple dictionary - """ - layerapm_dict = super()._dict_repr - layerapm_dict['chemical_structure'] = self._chemical_structure - layerapm_dict[ - 'area_per_molecule'] = f'{self.area_per_molecule.raw_value:.1f} ' \ - f'{self.area_per_molecule.unit}' - return layerapm_dict - - def as_dict(self, skip: list = None) -> dict: - """ - Custom as_dict method to skip necessary things. - - :return: Cleaned dictionary. - """ - if skip is None: - skip = [] - this_dict = super().as_dict(skip=skip) - del this_dict['material'], this_dict['scattering_length_real'] - del this_dict['scattering_length_imag'] - return this_dict diff --git a/EasyReflectometry/sample/layers.py b/EasyReflectometry/sample/layers.py deleted file mode 100644 index 84f50187..00000000 --- a/EasyReflectometry/sample/layers.py +++ /dev/null @@ -1,69 +0,0 @@ -__author__ = 'github.com/arm61' - -from typing import List - -import yaml -from easyCore.Objects.Groups import BaseCollection - -from EasyReflectometry.sample.layer import Layer - - -class Layers(BaseCollection): - - def __init__(self, - *args: List[Layer], - name: str = 'EasyLayers', - interface=None, - **kwargs): - super().__init__(name, *args, **kwargs) - self.interface = interface - - # Class constructors - @classmethod - def default(cls, interface=None) -> 'Layers': - """ - Default constructor for the reflectometry layers. - - :return: Default layers container - """ - layer1 = Layer.default() - layer2 = Layer.default() - return cls(layer1, layer2, interface=interface) - - @classmethod - def from_pars(cls, - *args: List[Layer], - name: str = 'EasyLayer', - interface=None) -> 'Layer': - """ - Constructor of a reflectometry layers where the parameters are known. - - :param args: The series of layers - :return: Layers container - """ - return cls(*args, name=name, interface=interface) - - @property - def uid(self) -> int: - """ - :return: UID from the borg map - """ - return self._borg.map.convert_id_to_key(self) - - # Representation - @property - def _dict_repr(self) -> dict: - """ - A simplified dict representation. - - :return: Simple dictionary - """ - return {self.name: [i._dict_repr for i in self]} - - def __repr__(self) -> str: - """ - String representation of the layer. - - :return: String representation of the layer - """ - return yaml.dump(self._dict_repr, sort_keys=False) diff --git a/EasyReflectometry/sample/material.py b/EasyReflectometry/sample/material.py deleted file mode 100644 index 332a6799..00000000 --- a/EasyReflectometry/sample/material.py +++ /dev/null @@ -1,494 +0,0 @@ -__author__ = 'github.com/arm61' - -from copy import deepcopy -from typing import ClassVar - -import yaml -from easyCore import np -from easyCore.Fitting.Constraints import FunctionalConstraint -from easyCore.Objects.ObjectClasses import BaseObj -from easyCore.Objects.ObjectClasses import Parameter - -from EasyReflectometry.special.calculations import density_to_sld -from EasyReflectometry.special.calculations import molecular_weight -from EasyReflectometry.special.calculations import neutron_scattering_length -from EasyReflectometry.special.calculations import weighted_average_sld - -MATERIAL_DEFAULTS = { - 'sld': { - 'description': - 'The real scattering length density for a ' - 'material in e-6 per squared angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 4.186, - 'units': '1 / angstrom ** 2', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - }, - 'isld': { - 'description': - 'The imaginary scattering length density for a ' - 'material in e-6 per squared angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 0.0, - 'units': '1 / angstrom ** 2', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - } -} - -MATERIALDENSITY_DEFAULTS = { - 'chemical_structure': 'Si', - 'sl': { - 'description': 'The real scattering length for a chemical formula in angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 4.1491, - 'units': 'angstrom', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - }, - 'isl': { - 'description': 'The real scattering length for a chemical formula in angstrom.', - 'url': 'https://www.ncnr.nist.gov/resources/activation/', - 'value': 0.0, - 'units': 'angstrom', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - }, - 'density': { - 'description': 'The mass density of the material.', - 'url': 'https://en.wikipedia.org/wiki/Density', - 'value': 2.33, - 'units': 'gram / centimeter ** 3', - 'min': 0, - 'max': np.Inf, - 'fixed': True - }, - 'molecular_weight': { - 'description': 'The molecular weight of a material.', - 'url': 'https://en.wikipedia.org/wiki/Molecular_mass', - 'value': 28.02, - 'units': 'g / mole', - 'min': -np.Inf, - 'max': np.Inf, - 'fixed': True - } -} - -MATERIALMIXTURE_DEFAULTS = { - 'fraction': { - 'description': 'The fraction of material b in material a', - 'value': 0.5, - 'units': 'dimensionless', - 'min': 0, - 'max': 1, - 'fixed': True - } -} - - -class Material(BaseObj): - - sld: ClassVar[Parameter] - isld: ClassVar[Parameter] - - def __init__(self, - sld: Parameter, - isld: Parameter, - name: str = 'EasyMaterial', - interface=None): - super().__init__(name, sld=sld, isld=isld) - self.interface = interface - - # Class constructors - @classmethod - def default(cls, interface=None) -> "Material": - """ - Default constructor for the reflectometry material. - - :return: Default material container - """ - sld = Parameter('sld', **MATERIAL_DEFAULTS['sld']) - isld = Parameter('isld', **MATERIAL_DEFAULTS['isld']) - return cls(sld, isld, interface=interface) - - @classmethod - def from_pars(cls, - sld: float, - isld: float, - name: str = 'EasyMaterial', - interface=None) -> "Material": - """ - Constructor of a reflectometry material where the parameters are known. - - :param sld: Real scattering length density - :param isld: Imaginary scattering length density - :return: Material container - """ - default_options = deepcopy(MATERIAL_DEFAULTS) - del default_options['sld']['value'] - del default_options['isld']['value'] - - sld = Parameter('sld', sld, **default_options['sld']) - isld = Parameter('isld', isld, **default_options['isld']) - - return cls(sld=sld, isld=isld, name=name, interface=interface) - - @property - def uid(self) -> int: - """ - Return a UID from the borg map. - - :return: Unique id - """ - return self._borg.map.convert_id_to_key(self) - - # Representation - @property - def _dict_repr(self) -> dict: - """ - A simplified dict representation. - - :return: Simple dictionary - """ - return { - self.name: { - 'sld': f'{self.sld.raw_value:.3f}e-6 {self.sld.unit}', - 'isld': f'{self.isld.raw_value:.3f}e-6 {self.isld.unit}' - } - } - - def __repr__(self) -> str: - """ - Representation of the material. - - :return: Dictionary representation of the material - """ - return yaml.dump(self._dict_repr, sort_keys=False) - - -class MaterialDensity(Material): - - density: ClassVar[Parameter] - - def __init__(self, - chemical_structure: str, - density: Parameter, - name: str = 'EasyMaterialDensity', - interface=None) -> 'MaterialDensity': - """ - :param chemical_structure: Chemical formula for the material - :param density: Mass density for the material - :param name: Identifier, defaults to :py:attr:`EasyMaterialDensity` - :param interface: Interface object, defaults to :py:attr:`None` - """ - scattering_length = neutron_scattering_length(chemical_structure) - default_options = deepcopy(MATERIALDENSITY_DEFAULTS) - del default_options['molecular_weight']['value'] - del default_options['sl']['value'] - del default_options['isl']['value'] - mw = Parameter('molecular_weight', molecular_weight(chemical_structure), - **default_options['molecular_weight']) - scattering_length_real = Parameter('scattering_length_real', - scattering_length.real, - **default_options['sl']) - scattering_length_imag = Parameter('scattering_length_imag', - scattering_length.imag, - **default_options['isl']) - default_options = deepcopy(MATERIAL_DEFAULTS) - del default_options['sld']['value'] - del default_options['isld']['value'] - sld = Parameter( - 'sld', - density_to_sld(scattering_length_real.raw_value, mw.raw_value, - density.raw_value), **default_options['sld']) - isld = Parameter( - 'isld', - density_to_sld(scattering_length_imag.raw_value, mw.raw_value, - density.raw_value), **default_options['isld']) - - constraint = FunctionalConstraint(sld, density_to_sld, - [scattering_length_real, mw, density]) - scattering_length_real.user_constraints['sld'] = constraint - mw.user_constraints['sld'] = constraint - density.user_constraints['sld'] = constraint - iconstraint = FunctionalConstraint(isld, density_to_sld, - [scattering_length_imag, mw, density]) - scattering_length_imag.user_constraints['isld'] = iconstraint - mw.user_constraints['isld'] = iconstraint - density.user_constraints['isld'] = iconstraint - - super().__init__(sld, isld, name=name, interface=interface) - - self._add_component('scattering_length_real', scattering_length_real) - self._add_component('scattering_length_imag', scattering_length_imag) - self._add_component('molecular_weight', mw) - self._add_component('density', density) - self._chemical_structure = chemical_structure - self.interface = interface - - @property - def chemical_structure(self) -> str: - """ - :returns: Chemical structure string - """ - return self._chemical_structure - - @chemical_structure.setter - def chemical_structure(self, structure_string: str): - """ - :param structure_string: String that defines the chemical structure. - """ - self._chemical_structure = structure_string - scattering_length = neutron_scattering_length(structure_string) - self.scattering_length_real.value = scattering_length.real - self.scattering_length_imag.value = scattering_length.imag - - # Class constructors - @classmethod - def default(cls, interface=None) -> 'MaterialDensity': - """ - Default constructor for the material defined by density and chemical structure. - - :param interface: Interface object, defaults to :py:attr:`None` - :return: Material container - """ - density = Parameter('density', **MATERIALDENSITY_DEFAULTS['density']) - return cls(MATERIALDENSITY_DEFAULTS['chemical_structure'], - density, - interface=interface) - - @classmethod - def from_pars(cls, - chemical_structure: str, - density: float, - name: str = 'EasyMaterialDensity', - interface=None) -> 'MaterialDensity': - """ - Constructor for a material based on the mass density and chemical structure, - where these are known. - :param chemical_structure: Chemical formula for the material - :param density: Mass density for the material - :param name: Identifier, defaults to :py:attr:`EasyMaterialDensity` - :param interface: Interface object, defaults to :py:attr:`None` - :return: Material container - """ - default_options = deepcopy(MATERIALDENSITY_DEFAULTS) - del default_options['density']['value'] - - density = Parameter('density', density, **default_options['density']) - - return cls(chemical_structure, density, name=name, interface=interface) - - @property - def _dict_repr(self) -> dict: - """ - Dictionary representation of the :py:class:`MaterialDensity` object. - - :return: Simple dictionary - """ - mat_dict = super()._dict_repr - mat_dict['chemical_structure'] = self._chemical_structure - mat_dict['density'] = f'{self.density.raw_value:.2e} {self.density.unit}' - return mat_dict - - def as_dict(self, skip: list = []) -> dict: - """ - Custom as_dict method to skip necessary things. - - :return: Cleaned dictionary. - """ - this_dict = super().as_dict(skip=skip) - del this_dict['sld'], this_dict['isld'], this_dict['scattering_length_real'] - del this_dict['scattering_length_imag'], this_dict['molecular_weight'] - return this_dict - - -class MaterialMixture(BaseObj): - - fraction: ClassVar[Parameter] - - def __init__(self, - material_a: Material, - material_b: Material, - fraction: Parameter, - name=None, - interface=None): - if name is None: - name = material_a.name + '/' + material_b.name - super().__init__(name, - _material_a=material_a, - _material_b=material_b, - fraction=fraction) - sld = weighted_average_sld(self._material_a.sld.raw_value, - self._material_b.sld.raw_value, - self.fraction.raw_value) - isld = weighted_average_sld(self._material_a.isld.raw_value, - self._material_b.isld.raw_value, - self.fraction.raw_value) - default_options = deepcopy(MATERIAL_DEFAULTS) - del default_options['sld']['value'] - del default_options['isld']['value'] - self._slds = [ - Parameter('sld', sld, **default_options['sld']), - Parameter('isld', isld, **default_options['isld']), - ] - self._materials_constraints() - self.interface = interface - - def _get_linkable_attributes(self): - return self._slds - - @property - def sld(self): - return self._slds[0] - - @property - def isld(self): - return self._slds[1] - - def _materials_constraints(self): - self._slds[0].enabled = True - self._slds[1].enabled = True - constraint = FunctionalConstraint( - self._slds[0], weighted_average_sld, - [self._material_a.sld, self._material_b.sld, self.fraction]) - self._material_a.sld.user_constraints['sld'] = constraint - self._material_b.sld.user_constraints['sld'] = constraint - self.fraction.user_constraints['sld'] = constraint - constraint() - iconstraint = FunctionalConstraint( - self._slds[1], weighted_average_sld, - [self._material_a.isld, self._material_b.isld, self.fraction]) - self._material_a.isld.user_constraints['isld'] = iconstraint - self._material_b.isld.user_constraints['isld'] = iconstraint - self.fraction.user_constraints['isld'] = iconstraint - iconstraint() - - @property - def material_a(self) -> Material: - """ - :return: the first material. - """ - return self._material_a - - @material_a.setter - def material_a(self, new_material_a: Material): - """ - Setter for material_a - - :param new_material_a: New material_a - """ - self.name = new_material_a.name + '/' + self._material_b.name - self._material_a = new_material_a - self._materials_constraints() - if self.interface is not None: - self.interface.generate_bindings(self) - - @property - def material_b(self) -> Material: - """ - :return: the second material. - """ - return self._material_b - - @material_b.setter - def material_b(self, new_material_b: Material): - """ - Setter for material_b - - :param new_material_b: New material_b - """ - self.name = self._material_a.name + '/' + new_material_b.name - self._material_b = new_material_b - self._materials_constraints() - if self.interface is not None: - self.interface.generate_bindings(self) - - # Class constructors - @classmethod - def default(cls, interface=None) -> "MaterialMixture": - """ - Default constructor for a mixture of two materials. - - :return: Default material mixture container. - """ - material_a = Material.default() - material_b = Material.default() - fraction = Parameter('fraction', **MATERIALMIXTURE_DEFAULTS['fraction']) - return cls(material_a, material_b, fraction, interface=interface) - - @classmethod - def from_pars(cls, - material_a: Material, - material_b: Material, - fraction: float, - name=None, - interface=None) -> "MaterialMixture": - """ - Constructor of a mixture of two materials where the parameters are known. - - :param material_a: The first material - :param material_b: The second material - :param fraction: The fraction of material_b in material_a - :return: MaterialMixture container. - """ - default_options = deepcopy(MATERIALMIXTURE_DEFAULTS) - del default_options['fraction']['value'] - fraction = Parameter('fraction', fraction, **default_options['fraction']) - - return cls(material_a=material_a, - material_b=material_b, - fraction=fraction, - name=name, - interface=interface) - - @property - def uid(self) -> int: - """ - Return a UID from the borg map. - - :return: Unique id - """ - return self._borg.map.convert_id_to_key(self) - - # Representation - @property - def _dict_repr(self) -> dict: - """ - A simplified dict representation. - - :return: Simple dictionary - """ - return { - self.name: { - 'fraction': self.fraction.raw_value, - 'sld': f'{self.sld.raw_value:.3f}e-6 {self.sld.unit}', - 'isld': f'{self.isld.raw_value:.3f}e-6 {self.isld.unit}', - 'material1': self.material_a._dict_repr, - 'material2': self.material_b._dict_repr - } - } - - def __repr__(self) -> str: - """ - :return: Custom repr - """ - return yaml.dump(self._dict_repr, sort_keys=False) - - def as_dict(self, skip: list = None) -> dict: - """ - Custom as_dict method to skip necessary things. - - :return: Cleaned dictionary. - """ - if skip is None: - skip = [] - this_dict = super().as_dict(skip=skip) - this_dict['material_a'] = self._material_a - this_dict['material_b'] = self._material_b - return this_dict diff --git a/EasyReflectometry/sample/materials.py b/EasyReflectometry/sample/materials.py deleted file mode 100644 index 64d7f305..00000000 --- a/EasyReflectometry/sample/materials.py +++ /dev/null @@ -1,74 +0,0 @@ -__author__ = 'github.com/arm61' - -from typing import List -from typing import Union - -import yaml -from easyCore.Objects.Groups import BaseCollection - -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.material import MaterialMixture - - -class Materials(BaseCollection): - - def __init__(self, - *args: List[Union[Material, MaterialMixture]], - name: str = 'EasyMaterials', - interface=None, - **kwargs): - super().__init__(name, *args, **kwargs) - self.interface = interface - - # Class constructors - @classmethod - def default(cls, interface=None) -> "Materials": - """ - Default constructor for materials. - - :return: Default materials container - :rtype: Materials - """ - material1 = Material.default() - material2 = Material.default() - return cls(material1, material2, interface=interface) - - @classmethod - def from_pars(cls, - *args: List[Union[Material, MaterialMixture]], - name: str = 'EasyMaterials', - interface=None) -> "Materials": - """ - Constructor of materials where the parameters are known. - - :param args: The series of material - :type args: List[Union[EasyReflectometry.material.Material]] - :return: Materials container - :rtype: Materials - """ - return cls(*args, name=name, interface=interface) - - @property - def names(self) -> List: - """ - :returns: List of names for the materials. - """ - return [i.name for i in self] - - # Representation - @property - def _dict_repr(self) -> dict: - """ - A simplified dict representation. - - :return: Simple dictionary - """ - return {self.name: [i._dict_repr for i in self]} - - def __repr__(self) -> str: - """ - String representation of the materials. - - :return: String representation of the materials - """ - return yaml.dump(self._dict_repr, sort_keys=False) diff --git a/EasyReflectometry/sample/structure.py b/EasyReflectometry/sample/sample.py similarity index 58% rename from EasyReflectometry/sample/structure.py rename to EasyReflectometry/sample/sample.py index 1e3a2edd..ec033db0 100644 --- a/EasyReflectometry/sample/structure.py +++ b/EasyReflectometry/sample/sample.py @@ -1,61 +1,58 @@ from __future__ import annotations -__author__ = "github.com/arm61" - -from typing import List -from typing import Union +__author__ = 'github.com/arm61' import yaml from easyCore.Objects.Groups import BaseCollection -from EasyReflectometry.sample.items import MultiLayer -from EasyReflectometry.sample.layer import Layer +from . import Layer +from . import Multilayer -class Structure(BaseCollection): +class Sample(BaseCollection): def __init__( self, - *args: List[Union[Layer, MultiLayer]], - name: str = "EasyStructure", + *args: list[Layer | Multilayer], + name: str = 'EasySample', interface=None, **kwargs, ): new_items = [] for i in args: if issubclass(type(i), Layer): - new_items.append(MultiLayer.from_pars(i, name=i.name)) - elif issubclass(type(i), MultiLayer): + new_items.append(Multilayer.from_pars(i, name=i.name)) + elif issubclass(type(i), Multilayer): new_items.append(i) else: - raise ValueError("The items must be either a Layer or an Item") + raise ValueError('The items must be either a Layer or an Assembly.') super().__init__(name, *new_items, **kwargs) self.interface = interface # Class constructors @classmethod - def default(cls, interface=None) -> Structure: + def default(cls, interface=None) -> Sample: """ - Default constructor for the reflectometry structure. + Default constructor for the reflectometry sample. - :return: Default structure container + :return: Default sample container :rtype: Structure """ - item1 = MultiLayer.default() - item2 = MultiLayer.default() + item1 = Multilayer.default() + item2 = Multilayer.default() return cls(item1, item2, interface=interface) @classmethod def from_pars( cls, - *args: List[Union[MultiLayer]], - name: str = "EasyStructure", + *args: list[Multilayer], + name: str = 'EasyStructure', interface=None, - ) -> "Structure": + ) -> Sample: """ - Constructor of a reflectometry structure where the parameters are known. + Constructor of a reflectometry sample where the parameters are known. - :param args: The items in the structure - :type args: List[EasyReflectometry.item.Item] + :param args: The items in the sample + :type args: list[EasyReflectometry.item.Item] :return: Structure container :rtype: Structure """ diff --git a/docs/item_library.rst b/docs/item_library.rst index 6e220db2..19f15082 100644 --- a/docs/item_library.rst +++ b/docs/item_library.rst @@ -6,53 +6,55 @@ These items offer flexibility for the user and enable more powerful analysis by In this page, we will document the items that are available with simple examples of the constructors that exist. Full API documentation is also available for the :py:mod:`EasyReflectometry.sample.item` module. -:py:class:`MultiLayer` +:py:class:`Multilayer` ---------------------- This item should be used for a series of layers that, for whatever reason, should be thought of as a single object. For example, in the `simple fitting tutorial`_ this item type is used to combine the silicon and silicon dioxide layer that as formed into a single object. All of the separate layers in these objects will be fitted individually, i.e. there is no constraints present, however, there is some cognative benefit to grouping layers together. -To create a :py:class:`MultiLayer` object, we use the following construction. +To create a :py:class:`Multilayer` object, we use the following construction. .. code-block:: python - from EasyReflectometry.sample import Layer, Material - from EasyReflectometry.sample.item import MultiLayer + from EasyReflectometry.sample import Layer + from EasyReflectometry.sample import Material + from EasyReflectometry.sample import Multilayer si = Material.from_pars(2.07, 0, 'Si') sio2 = Material.from_pars(3.47, 0, 'SiO2') si_layer = Layer.from_pars(si, 0, 0, 'Si layer') sio2_layer = Layer.from_pars(sio2, 30, 3, 'SiO2 layer') - subphase = MultiLayer.from_pars([si_layer, sio2_layer], + subphase = Multilayer.from_pars([si_layer, sio2_layer], name='Si/SiO2 subphase') -This will create a :py:class:`MultiLayer` object named :code:`subphase` which we can use in some :py:class:`Structure` for our analysis. +This will create a :py:class:`Multilayer` object named :code:`subphase` which we can use in some :py:class:`Structure` for our analysis. -:py:class:`RepeatingMultiLayer` +:py:class:`RepeatingMultilayer` ------------------------------- -The :py:class:`RepeatingMultiLayer` item type is an extension of the :py:class:`MultiLayer` for the analysis of systems with a multilayer that has some number of repeats. +The :py:class:`RepeatingMultilayer` item type is an extension of the :py:class:`Multilayer` for the analysis of systems with a multilayer that has some number of repeats. This item type imposes some constraints, specifically that all of the repeats have the exact same structure (i.e. thicknesses, roughnesses, and scattering length densities), which brings with it some computational saving as the reflectometry coefficients only needs to be calculated once for this structure and propagated for the correct number of repeats. There is a `tutorial`_ that discusses the utilisation of this item type for a nickel-titanium multilayer system. -The creation of a :py:class:`RepeatingMultiLayer` object is very similar to that for the :py:class:`MultiLayer`, with the addition of a number of repetitions. +The creation of a :py:class:`RepeatingMultilayer` object is very similar to that for the :py:class:`Multilayer`, with the addition of a number of repetitions. .. code-block:: python - from EasyReflectometry.sample import Layer, Material - from EasyReflectometry.sample.item import RepeatingMultiLayer + from EasyReflectometry.sample import Layer + from EasyReflectometry.sample import Material + from EasyReflectometry.sample import RepeatingMultilayer ti = Material.from_pars(-1.9493, 0, 'Ti') ni = Material.from_pars(9.4245, 0, 'Ni') ti_layer = Layer.from_pars(ti, 40, 0, 'Ti Layer') ni_layer = Layer.from_pars(ni, 70, 0, 'Ni Layer') - ni_ti = RepeatingMultiLayer.from_pars([ti_layer, ni_layer], + ni_ti = RepeatingMultilayer.from_pars([ti_layer, ni_layer], repetitions=10, - name='Ni/Ti MultiLayer') + name='Ni/Ti Multilayer') The number of repeats is a parameter that can be varied in the optimisation process, however given this is a value that depends on the synthesis of the sample this is unlikely to be necessary. diff --git a/docs/monolayer.ipynb b/docs/monolayer.ipynb index ef705925..f692e0c0 100644 --- a/docs/monolayer.ipynb +++ b/docs/monolayer.ipynb @@ -34,13 +34,15 @@ "import EasyReflectometry\n", "from EasyReflectometry.calculators import CalculatorFactory\n", "from EasyReflectometry.data import load\n", + "from EasyReflectometry.plot import plot\n", + "from EasyReflectometry.sample import Material\n", + "from EasyReflectometry.sample import SurfactantLayer\n", + "from EasyReflectometry.sample import Layer\n", + "from EasyReflectometry.sample import Sample\n", "from EasyReflectometry.experiment.model import Model\n", "from EasyReflectometry.fitting import Fitter\n", "from EasyReflectometry.plot import plot\n", - "from EasyReflectometry.sample import Layer\n", - "from EasyReflectometry.sample import Structure\n", - "from EasyReflectometry.sample.items import SurfactantLayer\n", - "from EasyReflectometry.sample.material import Material\n" + "\n" ] }, { @@ -287,7 +289,7 @@ "id": "9adc7c34-958e-4f45-9d63-876a83b64961", "metadata": {}, "source": [ - "Now that the surfactant layer and sub- and super-phases are available and the necessary constraints present, we construct our `Structure` and `Model` objects. " + "Now that the surfactant layer and sub- and super-phases are available and the necessary constraints present, we construct our `Sample` and `Model` objects. " ] }, { @@ -297,8 +299,8 @@ "metadata": {}, "outputs": [], "source": [ - "structure = Structure.from_pars(air_layer, dspc, d2o_layer)\n", - "model = Model.from_pars(structure, 1, data['data']['R_0'].values.min(), 5)" + "sample = Sample.from_pars(air_layer, dspc, d2o_layer)\n", + "model = Model.from_pars(sample, 1, data['data']['R_0'].values.min(), 5)" ] }, { diff --git a/docs/multi_contrast.ipynb b/docs/multi_contrast.ipynb index bc088207..cb1636a4 100644 --- a/docs/multi_contrast.ipynb +++ b/docs/multi_contrast.ipynb @@ -8,7 +8,7 @@ "# Analysis of multiple isotopic contrasts\n", "\n", "In the use of neutron reflectometry, it is common to use multiple isotopic contrasts of experimental data to analyse a system in a constrained fashion. \n", - "That is to say, that we have data from multiple different species (where isotopic substitution has been used to produce data with different scattering length density) that shar ethe same structure. \n", + "That is to say, that we have data from multiple different species (where isotopic substitution has been used to produce data with different scattering length density) that share the same structure. \n", "In this tutorial we will look at how `EasyReflectometry` can be used to fit multiple contrasts of data from a [surfactant monolayer](./monolayer.html) system, if you haven't looked at the tutorial for a single contrast of surfactant monolayer data it is suggested that you as this tutorial will build on it." ] }, @@ -33,9 +33,10 @@ "import refnx\n", "from EasyReflectometry.data import load\n", "from EasyReflectometry.plot import plot\n", - "from EasyReflectometry.sample.material import Material\n", - "from EasyReflectometry.sample.items import SurfactantLayer\n", - "from EasyReflectometry.sample import Layer, Structure\n", + "from EasyReflectometry.sample import Material\n", + "from EasyReflectometry.sample import SurfactantLayer\n", + "from EasyReflectometry.sample import Layer\n", + "from EasyReflectometry.sample import Sample\n", "from EasyReflectometry.experiment.model import Model\n", "from EasyReflectometry.calculators import CalculatorFactory\n", "from EasyReflectometry.fitting import Fitter\n", @@ -302,7 +303,7 @@ "source": [ "Even through only as single value (that for the d13-DSPC head thickness) was changed, all three values changed. \n", "\n", - "Having constructed each of the surfactant layer object and implemented the constraints, we can now build structures and models. " + "Having constructed each of the surfactant layer object and implemented the constraints, we can now build Samples and models. " ] }, { @@ -312,9 +313,9 @@ "metadata": {}, "outputs": [], "source": [ - "d13d2o_structure = Structure.from_pars(air_layer, d13d2o, d2o_layer)\n", - "d70d2o_structure = Structure.from_pars(air_layer, d70d2o, d2o_layer)\n", - "d83acmw_structure = Structure.from_pars(air_layer, d83acmw, acmw_layer)\n", + "d13d2o_structure = Sample.from_pars(air_layer, d13d2o, d2o_layer)\n", + "d70d2o_structure = Sample.from_pars(air_layer, d70d2o, d2o_layer)\n", + "d83acmw_structure = Sample.from_pars(air_layer, d83acmw, acmw_layer)\n", "d13d2o_model = Model.from_pars(d13d2o_structure, 0.1, data['data']['R_d13DSPC-D2O'].values.min(), 5)\n", "d70d2o_model = Model.from_pars(d70d2o_structure, 0.1, data['data']['R_d70DSPC-D2O'].values.min(), 5)\n", "d83acmw_model = Model.from_pars(d83acmw_structure, 0.1, data['data']['R_d83DSPC-ACMW'].values.min(), 5)" diff --git a/docs/repeating.ipynb b/docs/repeating.ipynb index 1f848bc0..480f26a5 100644 --- a/docs/repeating.ipynb +++ b/docs/repeating.ipynb @@ -11,7 +11,7 @@ "This allows the user to define their model, using specific parameters for their system of interest (if it is included in the item library). \n", "These items will impose necessary constraints and computational efficiencies based on the item that is used. \n", "\n", - "In this tutorial, we will look at one of these items, that of a `RepeatingMultiLayer` ([documented here](https://easyscience.github.io/EasyReflectometryLib/item_library.html#repeatingmultiLayer)). \n", + "In this tutorial, we will look at one of these items, that of a `RepeatingMultilayer` ([documented here](https://easyscience.github.io/EasyReflectometryLib/item_library.html#repeatingmultiLayer)). \n", "This tutorial is based on an example from the [BornAgain](https://www.bornagainproject.org) documentation looking at [specular reflectivity analysis](https://www.bornagainproject.org/m/py/fitting/extended/fit-specular-data/). \n", "Before performing analysis, we should import the packages that we need." ] @@ -38,9 +38,10 @@ "import EasyReflectometry\n", "import refl1d\n", "from EasyReflectometry.data import load\n", - "from EasyReflectometry.sample import Layer, Structure\n", - "from EasyReflectometry.sample.material import Material\n", - "from EasyReflectometry.sample.items import RepeatingMultiLayer\n", + "from EasyReflectometry.sample import Layer\n", + "from EasyReflectometry.sample import Sample\n", + "from EasyReflectometry.sample import Material\n", + "from EasyReflectometry.sample import RepeatingMultilayer\n", "from EasyReflectometry.experiment.model import Model\n", "from EasyReflectometry.calculators import CalculatorFactory\n", "from EasyReflectometry.fitting import Fitter\n", @@ -159,7 +160,7 @@ "id": "f63ec440-089f-46cf-8ff5-be5012ad8dc8", "metadata": {}, "source": [ - "Then, to produce the repeating multilayer, we use the `RepeatingMultilayer` [item type](https://easyscience.github.io/EasyReflectometryLib/item.html#EasyReflectometry.sample.item.RepeatingMultiLayer). \n", + "Then, to produce the repeating multilayer, we use the `RepeatingMultilayer` [item type](https://easyscience.github.io/EasyReflectometryLib/item.html#EasyReflectometry.sample.item.RepeatingMultilayer). \n", "This can be constructed in a range of different ways, however here we pass a list of `Layer` type objects and a number of repetitions. " ] }, @@ -170,7 +171,7 @@ "metadata": {}, "outputs": [], "source": [ - "rep_multilayer = RepeatingMultiLayer.from_pars([ti_layer, ni_layer], repetitions=10, name='NiTi Multilayer')\n", + "rep_multilayer = RepeatingMultilayer.from_pars([ti_layer, ni_layer], repetitions=10, name='NiTi Multilayer')\n", "rep_multilayer" ] }, @@ -189,8 +190,8 @@ "metadata": {}, "outputs": [], "source": [ - "structure = Structure.from_pars(superphase, rep_multilayer, subphase, name='Multilayer Structure')\n", - "model = Model.from_pars(structure, 1, 0, 0, 'Multilayer Model')" + "sample = Sample.from_pars(superphase, rep_multilayer, subphase, name='Multilayer Structure')\n", + "model = Model.from_pars(sample, 1, 0, 0, 'Multilayer Model')" ] }, { diff --git a/docs/simple_fitting.ipynb b/docs/simple_fitting.ipynb index 842b977c..093f5b29 100644 --- a/docs/simple_fitting.ipynb +++ b/docs/simple_fitting.ipynb @@ -32,9 +32,10 @@ "import EasyReflectometry\n", "import refnx\n", "from EasyReflectometry.data import load\n", - "from EasyReflectometry.sample import Layer, Structure\n", - "from EasyReflectometry.sample.material import Material\n", - "from EasyReflectometry.sample.items import MultiLayer\n", + "from EasyReflectometry.sample import Layer\n", + "from EasyReflectometry.sample import Sample\n", + "from EasyReflectometry.sample import Material\n", + "from EasyReflectometry.sample import Multilayer\n", "from EasyReflectometry.experiment.model import Model\n", "from EasyReflectometry.calculators import CalculatorFactory\n", "from EasyReflectometry.fitting import Fitter\n", @@ -229,7 +230,7 @@ "metadata": {}, "outputs": [], "source": [ - "subphase = MultiLayer.from_pars([si_layer, sio2_layer], name='Si/SiO2 Superphase')" + "subphase = Multilayer.from_pars([si_layer, sio2_layer], name='Si/SiO2 Superphase')" ] }, { @@ -237,7 +238,7 @@ "id": "a3a2df9b-8ddb-4b3e-a5c3-55b72280b651", "metadata": {}, "source": [ - "These objects are then combined as a `Structure`, where the constructor takes a series of layers (or some more complex `EasyReflectometry` [item](./item_library.html)) and, optionally, some name for the structure." + "These objects are then combined as a `Sample`, where the constructor takes a series of layers (or some more complex `EasyReflectometry` [item](./item_library.html)) and, optionally, some name for the sample." ] }, { @@ -247,7 +248,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure = Structure.from_pars(subphase, film_layer, superphase, name='Film Structure')" + "sample = Sample.from_pars(subphase, film_layer, superphase, name='Film Structure')" ] }, { @@ -255,7 +256,7 @@ "id": "b3e5d88e-3da4-4a3e-8067-80e9cd35be8f", "metadata": {}, "source": [ - "This structure can be investigated from the string representation like the other objects. " + "This sample can be investigated from the string representation like the other objects. " ] }, { @@ -265,7 +266,7 @@ "metadata": {}, "outputs": [], "source": [ - "structure" + "sample" ] }, { @@ -285,7 +286,7 @@ "\n", "\n", "\n", - "the `Model` constructor takes our structure, a scale factor, a uniform background level and a resolution width. " + "the `Model` constructor takes our smple, a scale factor, a uniform background level and a resolution width. " ] }, { @@ -295,7 +296,7 @@ "metadata": {}, "outputs": [], "source": [ - "model = Model.from_pars(structure, 1, 1e-6, 0.02, 'Film Model')" + "model = Model.from_pars(sample, 1, 1e-6, 0.02, 'Film Model')" ] }, { diff --git a/docs/solvation.ipynb b/docs/solvation.ipynb index fca83df7..fcee3e74 100644 --- a/docs/solvation.ipynb +++ b/docs/solvation.ipynb @@ -37,9 +37,11 @@ "import EasyReflectometry\n", "import refnx\n", "from EasyReflectometry.data import load\n", - "from EasyReflectometry.sample import Layer, Structure\n", - "from EasyReflectometry.sample.material import Material, MaterialMixture\n", - "from EasyReflectometry.sample.items import MultiLayer\n", + "from EasyReflectometry.sample import Layer\n", + "from EasyReflectometry.sample import Sample\n", + "from EasyReflectometry.sample import Material\n", + "from EasyReflectometry.sample import MaterialMixture\n", + "from EasyReflectometry.sample import Multilayer\n", "from EasyReflectometry.experiment.model import Model\n", "from EasyReflectometry.calculators import CalculatorFactory\n", "from EasyReflectometry.fitting import Fitter\n", @@ -156,7 +158,7 @@ "id": "0db4114b-3cfb-4d8e-a40e-3d86f869ab5f", "metadata": {}, "source": [ - "Now, we can construct our layers, structure and model. " + "Now, we can construct our layers, sample and model. " ] }, { @@ -171,9 +173,9 @@ "solvated_film_layer = Layer.from_pars(solvated_film, 250, 3, 'Film Layer')\n", "superphase = Layer.from_pars(d2o, 0, 3, 'D2O Subphase')\n", "\n", - "subphase = MultiLayer.from_pars([si_layer, sio2_layer], name='Si/SiO2 Superphase')\n", - "structure = Structure.from_pars(subphase, solvated_film_layer, superphase, name='Film Structure')\n", - "model = Model.from_pars(structure, 1, 1e-6, 0.02, 'Film Model')" + "subphase = Multilayer.from_pars([si_layer, sio2_layer], name='Si/SiO2 Superphase')\n", + "sample = Sample.from_pars(subphase, solvated_film_layer, superphase, name='Film Structure')\n", + "model = Model.from_pars(sample, 1, 1e-6, 0.02, 'Film Model')" ] }, { diff --git a/tests/__init__.py b/tests/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/calculators/bornagain/test_bornagain_wrapper.py b/tests/calculators/bornagain/test_bornagain_wrapper.py index c5cc78cc..d4b5605f 100644 --- a/tests/calculators/bornagain/test_bornagain_wrapper.py +++ b/tests/calculators/bornagain/test_bornagain_wrapper.py @@ -95,7 +95,7 @@ # def test_create_model(self): # p = BornAgain() # p.create_model() -# assert_equal(isinstance(p.storage['model'], ba.MultiLayer), True) +# assert_equal(isinstance(p.storage['model'], ba.Multilayer), True) # assert_equal(p.storage['model'].roughnessModel(), 2) # assert_equal(list(p.storage['model_parameters'].keys()), ['scale', 'background', 'resolution']) diff --git a/tests/calculators/refl1d/test_refl1d_calculator.py b/tests/calculators/refl1d/test_refl1d_calculator.py index 9c3e9a4a..e950cd7d 100644 --- a/tests/calculators/refl1d/test_refl1d_calculator.py +++ b/tests/calculators/refl1d/test_refl1d_calculator.py @@ -8,7 +8,7 @@ import numpy as np from numpy.testing import assert_almost_equal, assert_equal from EasyReflectometry.calculators.refl1d.calculator import Refl1d -from EasyReflectometry.sample.material import Material +from EasyReflectometry.sample import Material class TestRefl1d(unittest.TestCase): diff --git a/tests/experiment/test_model.py b/tests/experiment/test_model.py index 653cafd5..73be612b 100644 --- a/tests/experiment/test_model.py +++ b/tests/experiment/test_model.py @@ -12,11 +12,11 @@ from EasyReflectometry.experiment.model import Model from EasyReflectometry.calculators import CalculatorFactory -from EasyReflectometry.sample.items import RepeatingMultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.structure import Structure +from EasyReflectometry.sample import RepeatingMultilayer +from EasyReflectometry.sample import Layer +from EasyReflectometry.sample import LayerCollection +from EasyReflectometry.sample import Material +from EasyReflectometry.sample import Sample class TestModel(unittest.TestCase): @@ -25,7 +25,7 @@ def test_default(self): p = Model.default() assert_equal(p.name, 'EasyModel') assert_equal(p.interface, None) - assert_equal(p.structure.name, 'EasyStructure') + assert_equal(p.sample.name, 'EasySample') assert_equal(p.scale.display_name, 'scale') assert_equal(str(p.scale.unit), 'dimensionless') assert_equal(p.scale.value.n, 1.0) @@ -50,15 +50,15 @@ def test_from_pars(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, o2, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, o2, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel') assert_equal(mod.name, 'newModel') assert_equal(mod.interface, None) - assert_equal(mod.structure.name, 'myModel') + assert_equal(mod.sample.name, 'myModel') assert_equal(mod.scale.display_name, 'scale') assert_equal(str(mod.scale.unit), 'dimensionless') assert_equal(mod.scale.value.n, 2.0) @@ -83,17 +83,17 @@ def test_add_item(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel') - assert_equal(len(mod.structure), 1) + assert_equal(len(mod.sample), 1) mod.add_item(o2) - assert_equal(len(mod.structure), 2) - assert_equal(mod.structure[1].name, 'oneLayerItem2') - assert_equal(issubclass(mod.structure[1].__class__, RepeatingMultiLayer), True) + assert_equal(len(mod.sample), 2) + assert_equal(mod.sample[1].name, 'oneLayerItem2') + assert_equal(issubclass(mod.sample[1].__class__, RepeatingMultilayer), True) def test_add_item_with_interface_refnx(self): interface = CalculatorFactory() @@ -101,11 +101,11 @@ def test_add_item_with_interface_refnx(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) assert_equal(len(mod.interface()._wrapper.storage['item']), 1) assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) @@ -120,11 +120,11 @@ def test_add_item_with_interface_refl1d(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) assert_equal(len(mod.interface()._wrapper.storage['item']), 1) assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) @@ -141,9 +141,9 @@ def test_add_item_with_interface_refl1d(self): # l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') # ls1 = Layers.from_pars(l1, l2, name='twoLayer1') # ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - # o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - # o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - # d = Structure.from_pars(o1, name='myModel') + # o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + # o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + # d = Sample.from_pars(o1, name='myModel') # mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) # assert_equal(len(mod.interface()._wrapper.storage['item']), 1) # assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) @@ -156,19 +156,19 @@ def test_duplicate_item(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel') - assert_equal(len(mod.structure), 1) + assert_equal(len(mod.sample), 1) mod.add_item(o2) - assert_equal(len(mod.structure), 2) + assert_equal(len(mod.sample), 2) mod.duplicate_item(1) - assert_equal(len(mod.structure), 3) - assert_equal(mod.structure[2].name, 'oneLayerItem2 duplicate') - assert_equal(issubclass(mod.structure[2].__class__, RepeatingMultiLayer), True) + assert_equal(len(mod.sample), 3) + assert_equal(mod.sample[2].name, 'oneLayerItem2 duplicate') + assert_equal(issubclass(mod.sample[2].__class__, RepeatingMultilayer), True) def test_duplicate_item_with_interface_refnx(self): interface = CalculatorFactory() @@ -176,11 +176,11 @@ def test_duplicate_item_with_interface_refnx(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) assert_equal(len(mod.interface()._wrapper.storage['item']), 1) mod.add_item(o2) @@ -195,11 +195,11 @@ def test_duplicate_item_with_interface_refl1d(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) assert_equal(len(mod.interface()._wrapper.storage['item']), 1) mod.add_item(o2) @@ -216,9 +216,9 @@ def test_duplicate_item_with_interface_refl1d(self): # l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') # ls1 = Layers.from_pars(l1, l2, name='twoLayer1') # ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - # o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - # o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - # d = Structure.from_pars(o1, name='myModel') + # o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + # o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + # d = Sample.from_pars(o1, name='myModel') # mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) # assert_equal(len(mod.interface()._wrapper.storage['item']), 1) # mod.add_item(o2) @@ -231,17 +231,17 @@ def test_remove_item(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel') - assert_equal(len(mod.structure), 1) + assert_equal(len(mod.sample), 1) mod.add_item(o2) - assert_equal(len(mod.structure), 2) + assert_equal(len(mod.sample), 2) mod.remove_item(0) - assert_equal(len(mod.structure), 1) + assert_equal(len(mod.sample), 1) def test_remove_item_with_interface_refnx(self): interface = CalculatorFactory() @@ -249,11 +249,11 @@ def test_remove_item_with_interface_refnx(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) assert_equal(len(mod.interface()._wrapper.storage['item']), 1) assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) @@ -271,11 +271,11 @@ def test_remove_item_with_interface_refl1d(self): m2 = Material.from_pars(0.487, 0.000, 'Potassium') l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, name='myModel') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, name='myModel') mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) assert_equal(len(mod.interface()._wrapper.storage['item']), 1) assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) @@ -295,9 +295,9 @@ def test_remove_item_with_interface_refl1d(self): # l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') # ls1 = Layers.from_pars(l1, l2, name='twoLayer1') # ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - # o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - # o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - # d = Structure.from_pars(o1, name='myModel') + # o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + # o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + # d = Sample.from_pars(o1, name='myModel') # mod = Model.from_pars(d, 2, 1e-5, 2.0, 'newModel', interface=interface) # assert_equal(len(mod.interface()._wrapper.storage['item']), 1) # assert_equal(len(mod.interface()._wrapper.storage['layer']), 2) @@ -315,7 +315,7 @@ def test_uid(self): def test_repr(self): p = Model.default() assert p.__repr__( - ) == "EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: 5.0 %\n structure:\n EasyStructure:\n - EasyMultiLayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyMultiLayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n" + ) == "EasyModel:\n scale: 1.0\n background: 1.0e-08\n resolution: 5.0 %\n sample:\n EasySample:\n - EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n" def test_dict_round_trip(self): p = Model.default() diff --git a/tests/sample/__init__.py b/tests/sample/__init__.py deleted file mode 100644 index e69de29b..00000000 diff --git a/tests/sample/items/test_gradient_layer.py b/tests/sample/assemblies/test_gradient_layer.py similarity index 91% rename from tests/sample/items/test_gradient_layer.py rename to tests/sample/assemblies/test_gradient_layer.py index 259b6451..3ad77f71 100644 --- a/tests/sample/items/test_gradient_layer.py +++ b/tests/sample/assemblies/test_gradient_layer.py @@ -3,15 +3,15 @@ import pytest from numpy.testing import assert_almost_equal -import EasyReflectometry.sample.items.gradient_layer -from EasyReflectometry.sample.items import GradientLayer -from EasyReflectometry.sample.items.gradient_layer import ( +import EasyReflectometry.sample.assemblies.gradient_layer +from EasyReflectometry.sample.assemblies.gradient_layer import GradientLayer +from EasyReflectometry.sample.assemblies.gradient_layer import ( _apply_thickness_constraints, _linear_gradient, _prepare_gradient_layers, ) -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.material import Material +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.materials.material import Material class TestGradientLayer(): @@ -177,9 +177,9 @@ def test_prepare_gradient_layers(monkeypatch): mock_Material = MagicMock() mock_Material.from_pars = MagicMock(return_value='Material_from_pars') mock_linear_gradient = MagicMock(return_value=[1.0, 2.0, 3.0]) - monkeypatch.setattr(EasyReflectometry.sample.items.gradient_layer, '_linear_gradient', mock_linear_gradient) - monkeypatch.setattr(EasyReflectometry.sample.items.gradient_layer, 'Layer', mock_Layer) - monkeypatch.setattr(EasyReflectometry.sample.items.gradient_layer, 'Material', mock_Material) + monkeypatch.setattr(EasyReflectometry.sample.assemblies.gradient_layer, '_linear_gradient', mock_linear_gradient) + monkeypatch.setattr(EasyReflectometry.sample.assemblies.gradient_layer, 'Layer', mock_Layer) + monkeypatch.setattr(EasyReflectometry.sample.assemblies.gradient_layer, 'Material', mock_Material) # Then result = _prepare_gradient_layers(mock_material_1, mock_material_2, 3, None) @@ -205,7 +205,7 @@ def test_apply_thickness_constraints(monkeypatch): mock_layer_1.thickness = MagicMock() mock_obj_constraint = MagicMock() mock_ObjConstraint = MagicMock(return_value=mock_obj_constraint) - monkeypatch.setattr(EasyReflectometry.sample.items.gradient_layer, 'ObjConstraint', mock_ObjConstraint) + monkeypatch.setattr(EasyReflectometry.sample.assemblies.gradient_layer, 'ObjConstraint', mock_ObjConstraint) #Then _apply_thickness_constraints(layers) diff --git a/tests/sample/items/test_multilayer.py b/tests/sample/assemblies/test_multilayer.py similarity index 84% rename from tests/sample/items/test_multilayer.py rename to tests/sample/assemblies/test_multilayer.py index 1b7b0571..011626c1 100644 --- a/tests/sample/items/test_multilayer.py +++ b/tests/sample/assemblies/test_multilayer.py @@ -4,25 +4,23 @@ Tests for Item class module """ -import os import unittest -import numpy as np from numpy.testing import assert_equal from numpy.testing import assert_raises -from EasyReflectometry.calculators import CalculatorFactory -from EasyReflectometry.sample.items import MultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material +from EasyReflectometry.calculators.factory import CalculatorFactory +from EasyReflectometry.sample.assemblies.multilayer import Multilayer +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.layer_collection import LayerCollection +from EasyReflectometry.sample.elements.materials.material import Material -class TestMultiLayer(unittest.TestCase): +class TestMultilayer(unittest.TestCase): def test_default(self): - p = MultiLayer.default() - assert_equal(p.name, 'EasyMultiLayer') + p = Multilayer.default() + assert_equal(p.name, 'EasyMultilayer') assert_equal(p.type, 'Multi-layer') assert_equal(p.interface, None) assert_equal(len(p.layers), 2) @@ -33,8 +31,8 @@ def test_from_pars(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - l = Layers.from_pars(p, q, name='twoLayer') - o = MultiLayer.from_pars(l, 'twoLayerItem') + l = LayerCollection.from_pars(p, q, name='twoLayer') + o = Multilayer.from_pars(l, 'twoLayerItem') assert_equal(o.name, 'twoLayerItem') assert_equal(o.type, 'Multi-layer') assert_equal(o.interface, None) @@ -43,7 +41,7 @@ def test_from_pars(self): def test_from_pars_layer(self): m = Material.from_pars(6.908, -0.278, 'Boron') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') - o = MultiLayer.from_pars(p, 'twoLayerItem') + o = Multilayer.from_pars(p, 'twoLayerItem') assert_equal(o.name, 'twoLayerItem') assert_equal(o.interface, None) assert_equal(o.layers.name, 'thinBoron') @@ -53,7 +51,7 @@ def test_from_pars_layer_list(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 15.0, 2.0, 'layerPotassium') - o = MultiLayer.from_pars([p, q], 'twoLayerItem') + o = Multilayer.from_pars([p, q], 'twoLayerItem') assert_equal(o.name, 'twoLayerItem') assert_equal(o.interface, None) assert_equal(o.layers.name, 'thinBoron/layerPotassium') @@ -63,7 +61,7 @@ def test_add_layer(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - o = MultiLayer.from_pars(p, 'twoLayerItem') + o = Multilayer.from_pars(p, 'twoLayerItem') assert_equal(len(o.layers), 1) o.add_layer(q) assert_equal(len(o.layers), 2) @@ -76,7 +74,7 @@ def test_add_layer_with_interface_refnx(self): k = Material.from_pars(0.487, 0.000, 'Potassium', interface=interface) p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium', interface=interface) - o = MultiLayer.from_pars(p, 'twoLayerItem', interface=interface) + o = Multilayer.from_pars(p, 'twoLayerItem', interface=interface) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) o.add_layer(q) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) @@ -89,7 +87,7 @@ def test_duplicate_layer(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - o = MultiLayer.from_pars(p, 'twoLayerItem') + o = Multilayer.from_pars(p, 'twoLayerItem') assert_equal(len(o.layers), 1) o.add_layer(q) assert_equal(len(o.layers), 2) @@ -105,7 +103,7 @@ def test_duplicate_layer_with_interface_refnx(self): k = Material.from_pars(0.487, 0.000, 'Potassium', interface=interface) p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium', interface=interface) - o = MultiLayer.from_pars(p, 'twoLayerItem', interface=interface) + o = Multilayer.from_pars(p, 'twoLayerItem', interface=interface) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) o.add_layer(q) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) @@ -127,7 +125,7 @@ def test_remove_layer(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - o = MultiLayer.from_pars(p, 'twoLayerItem') + o = Multilayer.from_pars(p, 'twoLayerItem') assert_equal(len(o.layers), 1) o.add_layer(q) assert_equal(len(o.layers), 2) @@ -143,7 +141,7 @@ def test_remove_layer_with_interface_refnx(self): k = Material.from_pars(0.487, 0.000, 'Potassium', interface=interface) p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium', interface=interface) - o = MultiLayer.from_pars(p, name='twoLayerItem', interface=interface) + o = Multilayer.from_pars(p, name='twoLayerItem', interface=interface) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) o.add_layer(q) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) @@ -153,11 +151,11 @@ def test_remove_layer_with_interface_refnx(self): assert_equal(o.layers[0].name, 'thinBoron') def test_repr(self): - p = MultiLayer.default() + p = Multilayer.default() assert p.__repr__( - ) == 'EasyMultiLayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' + ) == 'EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' def test_dict_round_trip(self): - p = MultiLayer.default() - q = MultiLayer.from_dict(p.as_dict()) + p = Multilayer.default() + q = Multilayer.from_dict(p.as_dict()) assert p.as_data_dict() == q.as_data_dict() \ No newline at end of file diff --git a/tests/sample/items/test_repeating_multilayer.py b/tests/sample/assemblies/test_repeating_multilayer.py similarity index 86% rename from tests/sample/items/test_repeating_multilayer.py rename to tests/sample/assemblies/test_repeating_multilayer.py index dcea3161..b2383fd1 100644 --- a/tests/sample/items/test_repeating_multilayer.py +++ b/tests/sample/assemblies/test_repeating_multilayer.py @@ -4,25 +4,23 @@ Tests for Item class module """ -import os import unittest -import numpy as np from numpy.testing import assert_equal from numpy.testing import assert_raises from EasyReflectometry.calculators import CalculatorFactory -from EasyReflectometry.sample.items import RepeatingMultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material +from EasyReflectometry.sample.assemblies.repeating_multilayer import RepeatingMultilayer +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.layer_collection import LayerCollection +from EasyReflectometry.sample.elements.materials.material import Material -class TestRepeatingMultiLayer(unittest.TestCase): +class TestRepeatingMultilayer(unittest.TestCase): def test_default(self): - p = RepeatingMultiLayer.default() - assert_equal(p.name, 'EasyRepeatingMultiLayer') + p = RepeatingMultilayer.default() + assert_equal(p.name, 'EasyRepeatingMultilayer') assert_equal(p.type, 'Repeating Multi-layer') assert_equal(p.interface, None) assert_equal(len(p.layers), 2) @@ -39,8 +37,8 @@ def test_from_pars(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - l = Layers.from_pars(p, q, name='twoLayer') - o = RepeatingMultiLayer.from_pars(l, 2.0, 'twoLayerItem') + l = LayerCollection.from_pars(p, q, name='twoLayer') + o = RepeatingMultilayer.from_pars(l, 2.0, 'twoLayerItem') assert_equal(o.name, 'twoLayerItem') assert_equal(o.type, 'Repeating Multi-layer') assert_equal(o.interface, None) @@ -55,7 +53,7 @@ def test_from_pars(self): def test_from_pars_layer(self): m = Material.from_pars(6.908, -0.278, 'Boron') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') - o = RepeatingMultiLayer.from_pars(p, 2.0, 'twoLayerItem') + o = RepeatingMultilayer.from_pars(p, 2.0, 'twoLayerItem') assert_equal(o.name, 'twoLayerItem') assert_equal(o.interface, None) assert_equal(o.repetitions.display_name, 'repetitions') @@ -71,7 +69,7 @@ def test_from_pars_layer_list(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 15.0, 2.0, 'layerPotassium') - o = RepeatingMultiLayer.from_pars([p, q], 10, 'twoLayerItem') + o = RepeatingMultilayer.from_pars([p, q], 10, 'twoLayerItem') assert_equal(o.name, 'twoLayerItem') assert_equal(o.interface, None) assert_equal(o.layers.name, 'thinBoron/layerPotassium') @@ -84,7 +82,7 @@ def test_add_layer(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - o = RepeatingMultiLayer.from_pars(p, 2.0, 'twoLayerItem') + o = RepeatingMultilayer.from_pars(p, 2.0, 'twoLayerItem') assert_equal(len(o.layers), 1) o.add_layer(q) assert_equal(len(o.layers), 2) @@ -97,7 +95,7 @@ def test_add_layer_with_interface_refnx(self): k = Material.from_pars(0.487, 0.000, 'Potassium', interface=interface) p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium', interface=interface) - o = RepeatingMultiLayer.from_pars(p, 2.0, 'twoLayerItem', interface=interface) + o = RepeatingMultilayer.from_pars(p, 2.0, 'twoLayerItem', interface=interface) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) o.add_layer(q) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) @@ -110,7 +108,7 @@ def test_duplicate_layer(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - o = RepeatingMultiLayer.from_pars(p, 2.0, 'twoLayerItem') + o = RepeatingMultilayer.from_pars(p, 2.0, 'twoLayerItem') assert_equal(len(o.layers), 1) o.add_layer(q) assert_equal(len(o.layers), 2) @@ -126,7 +124,7 @@ def test_duplicate_layer_with_interface_refnx(self): k = Material.from_pars(0.487, 0.000, 'Potassium', interface=interface) p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium', interface=interface) - o = RepeatingMultiLayer.from_pars(p, 2.0, 'twoLayerItem', interface=interface) + o = RepeatingMultilayer.from_pars(p, 2.0, 'twoLayerItem', interface=interface) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 1) o.add_layer(q) assert_equal(len(o.interface()._wrapper.storage['item'][o.uid].components), 2) @@ -148,7 +146,7 @@ def test_remove_layer(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - o = RepeatingMultiLayer.from_pars(p, 2.0, 'twoLayerItem') + o = RepeatingMultilayer.from_pars(p, 2.0, 'twoLayerItem') assert_equal(len(o.layers), 1) o.add_layer(q) assert_equal(len(o.layers), 2) @@ -164,7 +162,7 @@ def test_remove_layer_with_interface_refnx(self): k = Material.from_pars(0.487, 0.000, 'Potassium', interface=interface) p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium', interface=interface) - o = RepeatingMultiLayer.from_pars(p, + o = RepeatingMultilayer.from_pars(p, repetitions=2.0, name='twoLayerItem', interface=interface) @@ -177,11 +175,11 @@ def test_remove_layer_with_interface_refnx(self): assert_equal(o.layers[0].name, 'thinBoron') def test_repr(self): - p = RepeatingMultiLayer.default() + p = RepeatingMultilayer.default() assert p.__repr__( - ) == "EasyRepeatingMultiLayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n repetitions: 1.0\n" + ) == "EasyRepeatingMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n repetitions: 1.0\n" def test_dict_round_trip(self): - p = RepeatingMultiLayer.default() - q = RepeatingMultiLayer.from_dict(p.as_dict()) + p = RepeatingMultilayer.default() + q = RepeatingMultilayer.from_dict(p.as_dict()) assert p.as_data_dict() == q.as_data_dict() \ No newline at end of file diff --git a/tests/sample/items/test_surfactant_layer.py b/tests/sample/assemblies/test_surfactant_layer.py similarity index 93% rename from tests/sample/items/test_surfactant_layer.py rename to tests/sample/assemblies/test_surfactant_layer.py index 3effd394..87a1a5a3 100644 --- a/tests/sample/items/test_surfactant_layer.py +++ b/tests/sample/assemblies/test_surfactant_layer.py @@ -4,21 +4,11 @@ Tests for Item class module """ -import os import unittest -import numpy as np -from numpy.testing import assert_almost_equal -from numpy.testing import assert_equal -from numpy.testing import assert_raises - -from EasyReflectometry.calculators import CalculatorFactory -from EasyReflectometry.sample.items import MultiLayer -from EasyReflectometry.sample.items import RepeatingMultiLayer -from EasyReflectometry.sample.items import SurfactantLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material +from EasyReflectometry.sample.assemblies.surfactant_layer import SurfactantLayer +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.materials.material import Material class TestSurfactantLayer(unittest.TestCase): diff --git a/tests/sample/elements/layers/test_layer.py b/tests/sample/elements/layers/test_layer.py new file mode 100644 index 00000000..915d770d --- /dev/null +++ b/tests/sample/elements/layers/test_layer.py @@ -0,0 +1,107 @@ +__author__ = 'github.com/arm61' +__version__ = '0.0.1' +""" +Tests for Layer class module +""" + +import unittest + +import numpy as np +from numpy.testing import assert_almost_equal +from numpy.testing import assert_equal + +from EasyReflectometry.calculators.factory import CalculatorFactory +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.materials.material import Material + + +class TestLayer(unittest.TestCase): + + def test_default(self): + p = Layer.default() + assert_equal(p.name, 'EasyLayer') + assert_equal(p.interface, None) + assert_equal(p.material.name, 'EasyMaterial') + assert_equal(p.thickness.display_name, 'thickness') + assert_equal(str(p.thickness.unit), 'angstrom') + assert_equal(p.thickness.value.n, 10.0) + assert_equal(p.thickness.min, 0.0) + assert_equal(p.thickness.max, np.Inf) + assert_equal(p.thickness.fixed, True) + assert_equal(p.roughness.display_name, 'roughness') + assert_equal(str(p.roughness.unit), 'angstrom') + assert_equal(p.roughness.value.n, 3.3) + assert_equal(p.roughness.min, 0.0) + assert_equal(p.roughness.max, np.Inf) + assert_equal(p.roughness.fixed, True) + + def test_from_pars(self): + m = Material.from_pars(6.908, -0.278, 'Boron') + p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') + assert_equal(p.name, 'thinBoron') + assert_equal(p.interface, None) + assert_equal(p.material.name, 'Boron') + assert_equal(p.thickness.display_name, 'thickness') + assert_equal(str(p.thickness.unit), 'angstrom') + assert_equal(p.thickness.value.n, 5.0) + assert_equal(p.thickness.min, 0.0) + assert_equal(p.thickness.max, np.Inf) + assert_equal(p.thickness.fixed, True) + assert_equal(p.roughness.display_name, 'roughness') + assert_equal(str(p.roughness.unit), 'angstrom') + assert_equal(p.roughness.value.n, 2.0) + assert_equal(p.roughness.min, 0.0) + assert_equal(p.roughness.max, np.Inf) + assert_equal(p.roughness.fixed, True) + + def test_assign_material(self): + m = Material.from_pars(6.908, -0.278, 'Boron') + p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') + k = Material.from_pars(2.074, 0.0, 'Silicon') + assert_almost_equal(p.material.sld.raw_value, 6.908) + assert_almost_equal(p.material.isld.raw_value, -0.278) + p.assign_material(k) + assert_almost_equal(p.material.sld.raw_value, 2.074) + assert_almost_equal(p.material.isld.raw_value, 0.0) + + def test_assign_material_with_interface_refnx(self): + interface = CalculatorFactory() + m = Material.from_pars(6.908, -0.278, 'Boron', interface=interface) + p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) + k = Material.from_pars(2.074, 0.0, 'Silicon', interface=interface) + assert_almost_equal( + p.interface()._wrapper.storage['layer'][p.uid].sld.real.value, 6.908) + assert_almost_equal( + p.interface()._wrapper.storage['layer'][p.uid].sld.imag.value, -0.278) + p.assign_material(k) + assert_almost_equal( + p.interface()._wrapper.storage['layer'][p.uid].sld.real.value, 2.074) + assert_almost_equal( + p.interface()._wrapper.storage['layer'][p.uid].sld.imag.value, 0.0) + + def test_dict_repr(self): + p = Layer.default() + assert p._dict_repr == { + 'EasyLayer': { + 'material': { + 'EasyMaterial': { + 'isld': '0.000e-6 1 / angstrom ** 2', + 'sld': '4.186e-6 1 / angstrom ** 2' + } + }, + 'roughness': '3.300 angstrom', + 'thickness': '10.000 angstrom' + } + } + + def test_repr(self): + p = Layer.default() + assert p.__repr__( + ) == 'EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' + + def test_dict_round_trip(self): + p = Layer.default() + q = Layer.from_dict(p.as_dict()) + assert p.as_data_dict() == q.as_data_dict() + + diff --git a/tests/sample/test_layer.py b/tests/sample/elements/layers/test_layer_apm.py similarity index 57% rename from tests/sample/test_layer.py rename to tests/sample/elements/layers/test_layer_apm.py index 543f9f5d..d261bace 100644 --- a/tests/sample/test_layer.py +++ b/tests/sample/elements/layers/test_layer_apm.py @@ -1,113 +1,9 @@ -__author__ = 'github.com/arm61' -__version__ = '0.0.1' -""" -Tests for Layer class module -""" - -import os import unittest -import numpy as np from numpy.testing import assert_almost_equal -from numpy.testing import assert_equal - -from EasyReflectometry.calculators import CalculatorFactory -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layer import LayerApm -from EasyReflectometry.sample.material import Material -from EasyReflectometry.special.calculations import apm_to_sld -from EasyReflectometry.special.calculations import neutron_scattering_length - - -class TestLayer(unittest.TestCase): - - def test_default(self): - p = Layer.default() - assert_equal(p.name, 'EasyLayer') - assert_equal(p.interface, None) - assert_equal(p.material.name, 'EasyMaterial') - assert_equal(p.thickness.display_name, 'thickness') - assert_equal(str(p.thickness.unit), 'angstrom') - assert_equal(p.thickness.value.n, 10.0) - assert_equal(p.thickness.min, 0.0) - assert_equal(p.thickness.max, np.Inf) - assert_equal(p.thickness.fixed, True) - assert_equal(p.roughness.display_name, 'roughness') - assert_equal(str(p.roughness.unit), 'angstrom') - assert_equal(p.roughness.value.n, 3.3) - assert_equal(p.roughness.min, 0.0) - assert_equal(p.roughness.max, np.Inf) - assert_equal(p.roughness.fixed, True) - - def test_from_pars(self): - m = Material.from_pars(6.908, -0.278, 'Boron') - p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') - assert_equal(p.name, 'thinBoron') - assert_equal(p.interface, None) - assert_equal(p.material.name, 'Boron') - assert_equal(p.thickness.display_name, 'thickness') - assert_equal(str(p.thickness.unit), 'angstrom') - assert_equal(p.thickness.value.n, 5.0) - assert_equal(p.thickness.min, 0.0) - assert_equal(p.thickness.max, np.Inf) - assert_equal(p.thickness.fixed, True) - assert_equal(p.roughness.display_name, 'roughness') - assert_equal(str(p.roughness.unit), 'angstrom') - assert_equal(p.roughness.value.n, 2.0) - assert_equal(p.roughness.min, 0.0) - assert_equal(p.roughness.max, np.Inf) - assert_equal(p.roughness.fixed, True) - - def test_assign_material(self): - m = Material.from_pars(6.908, -0.278, 'Boron') - p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') - k = Material.from_pars(2.074, 0.0, 'Silicon') - assert_almost_equal(p.material.sld.raw_value, 6.908) - assert_almost_equal(p.material.isld.raw_value, -0.278) - p.assign_material(k) - assert_almost_equal(p.material.sld.raw_value, 2.074) - assert_almost_equal(p.material.isld.raw_value, 0.0) - - def test_assign_material_with_interface_refnx(self): - interface = CalculatorFactory() - m = Material.from_pars(6.908, -0.278, 'Boron', interface=interface) - p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron', interface=interface) - k = Material.from_pars(2.074, 0.0, 'Silicon', interface=interface) - assert_almost_equal( - p.interface()._wrapper.storage['layer'][p.uid].sld.real.value, 6.908) - assert_almost_equal( - p.interface()._wrapper.storage['layer'][p.uid].sld.imag.value, -0.278) - p.assign_material(k) - assert_almost_equal( - p.interface()._wrapper.storage['layer'][p.uid].sld.real.value, 2.074) - assert_almost_equal( - p.interface()._wrapper.storage['layer'][p.uid].sld.imag.value, 0.0) - - def test_dict_repr(self): - p = Layer.default() - assert p._dict_repr == { - 'EasyLayer': { - 'material': { - 'EasyMaterial': { - 'isld': '0.000e-6 1 / angstrom ** 2', - 'sld': '4.186e-6 1 / angstrom ** 2' - } - }, - 'roughness': '3.300 angstrom', - 'thickness': '10.000 angstrom' - } - } - - def test_repr(self): - p = Layer.default() - assert p.__repr__( - ) == 'EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' - - def test_dict_round_trip(self): - p = Layer.default() - q = Layer.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() +from EasyReflectometry.sample.elements.layers.layer_apm import LayerApm +from EasyReflectometry.sample.elements.materials.material import Material class TestLayerApm(unittest.TestCase): diff --git a/tests/sample/elements/materials/test_material.py b/tests/sample/elements/materials/test_material.py new file mode 100644 index 00000000..c974cb85 --- /dev/null +++ b/tests/sample/elements/materials/test_material.py @@ -0,0 +1,68 @@ +__author__ = 'github.com/arm61' +__version__ = '0.0.1' +""" +Tests for Material class module +""" + +import unittest + +import numpy as np + +from EasyReflectometry.sample.elements.materials.material import Material + + +class TestMaterial(unittest.TestCase): + + def test_default(self): + p = Material.default() + assert p.name == 'EasyMaterial' + assert p.interface == None + assert p.sld.display_name == 'sld' + assert str(p.sld.unit) == '1 / angstrom ** 2' + assert p.sld.value.n == 4.186 + assert p.sld.min == -np.Inf + assert p.sld.max == np.Inf + assert p.sld.fixed == True + assert p.isld.display_name == 'isld' + assert str(p.isld.unit) == '1 / angstrom ** 2' + assert p.isld.value.n == 0.0 + assert p.isld.min == -np.Inf + assert p.isld.max == np.Inf + assert p.isld.fixed == True + + def test_from_pars(self): + p = Material.from_pars(6.908, -0.278, 'Boron') + assert p.name == 'Boron' + assert p.interface == None + assert p.sld.display_name == 'sld' + assert str(p.sld.unit) == '1 / angstrom ** 2' + assert p.sld.value.n == 6.908 + assert p.sld.min == -np.Inf + assert p.sld.max == np.Inf + assert p.sld.fixed == True + assert p.isld.display_name == 'isld' + assert str(p.isld.unit) == '1 / angstrom ** 2' + assert p.isld.value.n == -0.278 + assert p.isld.min == -np.Inf + assert p.isld.max == np.Inf + assert p.isld.fixed == True + + def test_dict_repr(self): + p = Material.default() + assert p._dict_repr == { + 'EasyMaterial': { + 'sld': '4.186e-6 1 / angstrom ** 2', + 'isld': '0.000e-6 1 / angstrom ** 2' + } + } + + def test_repr(self): + p = Material.default() + assert p.__repr__( + ) == 'EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n' + + def test_dict_round_trip(self): + p = Material.default() + q = Material.from_dict(p.as_dict()) + assert p.as_data_dict() == q.as_data_dict() + diff --git a/tests/sample/elements/materials/test_material_density.py b/tests/sample/elements/materials/test_material_density.py new file mode 100644 index 00000000..03566f54 --- /dev/null +++ b/tests/sample/elements/materials/test_material_density.py @@ -0,0 +1,63 @@ +import unittest + +import numpy as np +from numpy.testing import assert_almost_equal + +from EasyReflectometry.sample.elements.materials.material_density import MaterialDensity + + +class TestMaterialDensity(unittest.TestCase): + + def test_default(self): + p = MaterialDensity.default() + assert p.name == 'EasyMaterialDensity' + assert p.interface == None + assert p.density.display_name == 'density' + assert str(p.density.unit) == 'gram / centimeter ** 3' + assert p.density.value.n == 2.33 + assert p.density.min == 0 + assert p.density.max == np.Inf + assert p.density.fixed == True + + def test_default_constraint(self): + p = MaterialDensity.default() + assert p.density.value.n == 2.33 + assert_almost_equal(p.sld.value.n, 2.073705382) + p.density.value = 2 + assert_almost_equal(p.sld.value.n, 1.780004619) + + def test_from_pars(self): + p = MaterialDensity.from_pars('Co', 8.9, 'Cobalt') + assert p.density.value.n == 8.9 + assert_almost_equal(p.sld.value.n, 2.2645412328256) + assert p.chemical_structure == 'Co' + + def test_chemical_structure_change(self): + p = MaterialDensity.from_pars('Co', 8.9, 'Cobolt') + assert p.density.value.n == 8.9 + assert_almost_equal(p.sld.value.n, 2.2645412328256) + assert_almost_equal(p.isld.value.n, 0.0) + assert p.chemical_structure == 'Co' + p.chemical_structure = 'B' + assert p.density.value.n == 8.9 + assert_almost_equal(p.sld.value.n, 4.820107844970) + assert_almost_equal(p.isld.value.n, -0.19098540517806603) + assert p.chemical_structure == 'B' + + def test_dict_repr(self): + p = MaterialDensity.default() + print(p._dict_repr) + assert p._dict_repr == { + 'EasyMaterialDensity': { + 'sld': '2.074e-6 1 / angstrom ** 2', + 'isld': '0.000e-6 1 / angstrom ** 2' + }, + 'chemical_structure': 'Si', + 'density': '2.33e+00 gram / centimeter ** 3' + } + + def test_dict_round_trip(self): + p = MaterialDensity.default() + q = MaterialDensity.from_dict(p.as_dict()) + assert p.as_data_dict() == q.as_data_dict() + diff --git a/tests/sample/test_material.py b/tests/sample/elements/materials/test_material_mixture.py similarity index 55% rename from tests/sample/test_material.py rename to tests/sample/elements/materials/test_material_mixture.py index 4e6ae0a7..d635b8b8 100644 --- a/tests/sample/test_material.py +++ b/tests/sample/elements/materials/test_material_mixture.py @@ -1,130 +1,9 @@ -__author__ = 'github.com/arm61' -__version__ = '0.0.1' -""" -Tests for Material class module -""" - import unittest -import numpy as np from numpy.testing import assert_almost_equal -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.material import MaterialDensity -from EasyReflectometry.sample.material import MaterialMixture - - -class TestMaterial(unittest.TestCase): - - def test_default(self): - p = Material.default() - assert p.name == 'EasyMaterial' - assert p.interface == None - assert p.sld.display_name == 'sld' - assert str(p.sld.unit) == '1 / angstrom ** 2' - assert p.sld.value.n == 4.186 - assert p.sld.min == -np.Inf - assert p.sld.max == np.Inf - assert p.sld.fixed == True - assert p.isld.display_name == 'isld' - assert str(p.isld.unit) == '1 / angstrom ** 2' - assert p.isld.value.n == 0.0 - assert p.isld.min == -np.Inf - assert p.isld.max == np.Inf - assert p.isld.fixed == True - - def test_from_pars(self): - p = Material.from_pars(6.908, -0.278, 'Boron') - assert p.name == 'Boron' - assert p.interface == None - assert p.sld.display_name == 'sld' - assert str(p.sld.unit) == '1 / angstrom ** 2' - assert p.sld.value.n == 6.908 - assert p.sld.min == -np.Inf - assert p.sld.max == np.Inf - assert p.sld.fixed == True - assert p.isld.display_name == 'isld' - assert str(p.isld.unit) == '1 / angstrom ** 2' - assert p.isld.value.n == -0.278 - assert p.isld.min == -np.Inf - assert p.isld.max == np.Inf - assert p.isld.fixed == True - - def test_dict_repr(self): - p = Material.default() - assert p._dict_repr == { - 'EasyMaterial': { - 'sld': '4.186e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2' - } - } - - def test_repr(self): - p = Material.default() - assert p.__repr__( - ) == 'EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n' - - def test_dict_round_trip(self): - p = Material.default() - q = Material.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() - - -class TestMaterialDensity(unittest.TestCase): - - def test_default(self): - p = MaterialDensity.default() - assert p.name == 'EasyMaterialDensity' - assert p.interface == None - assert p.density.display_name == 'density' - assert str(p.density.unit) == 'gram / centimeter ** 3' - assert p.density.value.n == 2.33 - assert p.density.min == 0 - assert p.density.max == np.Inf - assert p.density.fixed == True - - def test_default_constraint(self): - p = MaterialDensity.default() - assert p.density.value.n == 2.33 - assert_almost_equal(p.sld.value.n, 2.073705382) - p.density.value = 2 - assert_almost_equal(p.sld.value.n, 1.780004619) - - def test_from_pars(self): - p = MaterialDensity.from_pars('Co', 8.9, 'Cobalt') - assert p.density.value.n == 8.9 - assert_almost_equal(p.sld.value.n, 2.2645412328256) - assert p.chemical_structure == 'Co' - - def test_chemical_structure_change(self): - p = MaterialDensity.from_pars('Co', 8.9, 'Cobolt') - assert p.density.value.n == 8.9 - assert_almost_equal(p.sld.value.n, 2.2645412328256) - assert_almost_equal(p.isld.value.n, 0.0) - assert p.chemical_structure == 'Co' - p.chemical_structure = 'B' - assert p.density.value.n == 8.9 - assert_almost_equal(p.sld.value.n, 4.820107844970) - assert_almost_equal(p.isld.value.n, -0.19098540517806603) - assert p.chemical_structure == 'B' - - def test_dict_repr(self): - p = MaterialDensity.default() - print(p._dict_repr) - assert p._dict_repr == { - 'EasyMaterialDensity': { - 'sld': '2.074e-6 1 / angstrom ** 2', - 'isld': '0.000e-6 1 / angstrom ** 2' - }, - 'chemical_structure': 'Si', - 'density': '2.33e+00 gram / centimeter ** 3' - } - - def test_dict_round_trip(self): - p = MaterialDensity.default() - q = MaterialDensity.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() - +from EasyReflectometry.sample.elements.materials.material import Material +from EasyReflectometry.sample.elements.materials.material_mixture import MaterialMixture class TestMaterialMixture(unittest.TestCase): diff --git a/tests/sample/test_layers.py b/tests/sample/elements/test_layer_collection.py similarity index 77% rename from tests/sample/test_layers.py rename to tests/sample/elements/test_layer_collection.py index 56ae4237..01b77a35 100644 --- a/tests/sample/test_layers.py +++ b/tests/sample/elements/test_layer_collection.py @@ -4,23 +4,20 @@ Tests for Layers class module """ -import os import unittest -import numpy as np -from numpy.testing import assert_almost_equal from numpy.testing import assert_equal -from EasyReflectometry.sample.items import RepeatingMultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material +from EasyReflectometry.sample.assemblies.repeating_multilayer import RepeatingMultilayer +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.layer_collection import LayerCollection +from EasyReflectometry.sample.elements.materials.material import Material -class TestLayers(unittest.TestCase): +class TestLayerCollection(unittest.TestCase): def test_default(self): - p = Layers.default() + p = LayerCollection.default() assert_equal(p.name, 'EasyLayers') assert_equal(p.interface, None) assert_equal(len(p), 2) @@ -32,7 +29,7 @@ def test_from_pars(self): k = Material.from_pars(0.487, 0.000, 'Potassium') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') q = Layer.from_pars(k, 50.0, 1.0, 'thickPotassium') - l = Layers.from_pars(p, q, name='twoLayer') + l = LayerCollection.from_pars(p, q, name='twoLayer') assert_equal(l.name, 'twoLayer') assert_equal(l.interface, None) assert_equal(len(l), 2) @@ -42,13 +39,13 @@ def test_from_pars(self): def test_from_pars_item(self): m = Material.from_pars(6.908, -0.278, 'Boron') p = Layer.from_pars(m, 5.0, 2.0, 'thinBoron') - i = RepeatingMultiLayer.from_pars(Layers.default(), 2) - l = Layers.from_pars(p, i, name='twoLayer') + i = RepeatingMultilayer.from_pars(LayerCollection.default(), 2) + l = LayerCollection.from_pars(p, i, name='twoLayer') assert_equal(l.name, 'twoLayer') assert_equal(l.interface, None) def test_dict_repr(self): - p = Layers.default() + p = LayerCollection.default() print(p._dict_repr) assert p._dict_repr == { 'EasyLayers': [{ @@ -77,11 +74,11 @@ def test_dict_repr(self): } def test_repr(self): - p = Layers.default() + p = LayerCollection.default() assert p.__repr__( ) == 'EasyLayers:\n- EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n- EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' def test_dict_round_trip(self): - p = Layers.default() - q = Layers.from_dict(p.as_dict()) + p = LayerCollection.default() + q = LayerCollection.from_dict(p.as_dict()) assert p.as_data_dict() == q.as_data_dict() \ No newline at end of file diff --git a/tests/sample/test_materials.py b/tests/sample/elements/test_material_collection.py similarity index 75% rename from tests/sample/test_materials.py rename to tests/sample/elements/test_material_collection.py index 96f5edac..a644b73c 100644 --- a/tests/sample/test_materials.py +++ b/tests/sample/elements/test_material_collection.py @@ -6,14 +6,14 @@ import unittest -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.materials import Materials +from EasyReflectometry.sample.elements.materials.material import Material +from EasyReflectometry.sample.elements.material_collection import MaterialCollection -class TestLayers(unittest.TestCase): +class TestLayerCollection(unittest.TestCase): def test_default(self): - p = Materials.default() + p = MaterialCollection.default() assert p.name == 'EasyMaterials' assert p.interface is None assert len(p) == 2 @@ -23,7 +23,7 @@ def test_default(self): def test_from_pars(self): m = Material.from_pars(6.908, -0.278, 'Boron') k = Material.from_pars(0.487, 0.000, 'Potassium') - p = Materials.from_pars(m, k, name='thinBoron') + p = MaterialCollection.from_pars(m, k, name='thinBoron') assert p.name == 'thinBoron' assert p.interface is None assert len(p) == 2 @@ -31,7 +31,7 @@ def test_from_pars(self): assert p[1].name == 'Potassium' def test_dict_repr(self): - p = Materials.default() + p = MaterialCollection.default() assert p._dict_repr == { 'EasyMaterials': [{ 'EasyMaterial': { @@ -47,11 +47,11 @@ def test_dict_repr(self): } def test_repr(self): - p = Materials.default() + p = MaterialCollection.default() assert p.__repr__( ) == 'EasyMaterials:\n- EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n- EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n' def test_dict_round_trip(self): - p = Materials.default() - q = Materials.from_dict(p.as_dict()) + p = MaterialCollection.default() + q = MaterialCollection.from_dict(p.as_dict()) assert p.as_data_dict() == q.as_data_dict() \ No newline at end of file diff --git a/tests/sample/test_sample.py b/tests/sample/test_sample.py new file mode 100644 index 00000000..81c251ef --- /dev/null +++ b/tests/sample/test_sample.py @@ -0,0 +1,63 @@ +__author__ = 'github.com/arm61' +__version__ = '0.0.1' + +import unittest + +from numpy.testing import assert_equal + +from EasyReflectometry.sample.assemblies.repeating_multilayer import RepeatingMultilayer +from EasyReflectometry.sample.elements.layers.layer import Layer +from EasyReflectometry.sample.elements.layer_collection import LayerCollection +from EasyReflectometry.sample.elements.materials.material import Material +from EasyReflectometry.sample.sample import Sample + + +class TestSample(unittest.TestCase): + + def test_default(self): + p = Sample.default() + assert_equal(p.name, 'EasySample') + assert_equal(p.interface, None) + assert_equal(p[0].name, 'EasyMultilayer') + assert_equal(p[1].name, 'EasyMultilayer') + + def test_from_pars(self): + m1 = Material.from_pars(6.908, -0.278, 'Boron') + m2 = Material.from_pars(0.487, 0.000, 'Potassium') + l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') + l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') + ls1 = LayerCollection.from_pars(l1, l2, name='twoLayer1') + ls2 = LayerCollection.from_pars(l2, l1, name='twoLayer2') + o1 = RepeatingMultilayer.from_pars(ls1, 2.0, 'twoLayerItem1') + o2 = RepeatingMultilayer.from_pars(ls2, 1.0, 'oneLayerItem2') + d = Sample.from_pars(o1, o2, name='myModel') + assert_equal(d.name, 'myModel') + assert_equal(d.interface, None) + assert_equal(d[0].name, 'twoLayerItem1') + assert_equal(d[1].name, 'oneLayerItem2') + + def test_from_pars_layers(self): + m1 = Material.from_pars(6.908, -0.278, 'Boron') + m2 = Material.from_pars(0.487, 0.000, 'Potassium') + l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') + l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') + d = Sample.from_pars(l1, l2, name='myModel') + assert_equal(d.name, 'myModel') + assert_equal(d.interface, None) + assert_equal(d[0].name, 'thinBoron') + assert_equal(d[1].name, 'thickPotassium') + + def test_from_pars_error(self): + m1 = Material.from_pars(6.908, -0.278, 'Boron') + with self.assertRaises(ValueError): + _ = Sample.from_pars(m1, name='myModel') + + def test_repr(self): + p = Sample.default() + assert p.__repr__( + ) == 'EasySample:\n- EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n- EasyMultilayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' + + def test_dict_round_trip(self): + p = Sample.default() + q = Sample.from_dict(p.as_dict()) + assert p.as_data_dict() == q.as_data_dict() \ No newline at end of file diff --git a/tests/sample/test_structure.py b/tests/sample/test_structure.py deleted file mode 100644 index b7f15919..00000000 --- a/tests/sample/test_structure.py +++ /dev/null @@ -1,69 +0,0 @@ -__author__ = 'github.com/arm61' -__version__ = '0.0.1' -""" -Tests for Model class module -""" - -import os -import unittest - -import numpy as np -from numpy.testing import assert_almost_equal -from numpy.testing import assert_equal - -from EasyReflectometry.sample.items import RepeatingMultiLayer -from EasyReflectometry.sample.layer import Layer -from EasyReflectometry.sample.layers import Layers -from EasyReflectometry.sample.material import Material -from EasyReflectometry.sample.structure import Structure - - -class TestStructure(unittest.TestCase): - - def test_default(self): - p = Structure.default() - assert_equal(p.name, 'EasyStructure') - assert_equal(p.interface, None) - assert_equal(p[0].name, 'EasyMultiLayer') - assert_equal(p[1].name, 'EasyMultiLayer') - - def test_from_pars(self): - m1 = Material.from_pars(6.908, -0.278, 'Boron') - m2 = Material.from_pars(0.487, 0.000, 'Potassium') - l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') - l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - ls1 = Layers.from_pars(l1, l2, name='twoLayer1') - ls2 = Layers.from_pars(l2, l1, name='twoLayer2') - o1 = RepeatingMultiLayer.from_pars(ls1, 2.0, 'twoLayerItem1') - o2 = RepeatingMultiLayer.from_pars(ls2, 1.0, 'oneLayerItem2') - d = Structure.from_pars(o1, o2, name='myModel') - assert_equal(d.name, 'myModel') - assert_equal(d.interface, None) - assert_equal(d[0].name, 'twoLayerItem1') - assert_equal(d[1].name, 'oneLayerItem2') - - def test_from_pars_layers(self): - m1 = Material.from_pars(6.908, -0.278, 'Boron') - m2 = Material.from_pars(0.487, 0.000, 'Potassium') - l1 = Layer.from_pars(m1, 5.0, 2.0, 'thinBoron') - l2 = Layer.from_pars(m2, 50.0, 1.0, 'thickPotassium') - d = Structure.from_pars(l1, l2, name='myModel') - assert_equal(d.name, 'myModel') - assert_equal(d.interface, None) - assert_equal(d[0].name, 'thinBoron') - assert_equal(d[1].name, 'thickPotassium') - - def test_from_pars_error(self): - m1 = Material.from_pars(6.908, -0.278, 'Boron') - with self.assertRaises(ValueError): - _ = Structure.from_pars(m1, name='myModel') - - def test_repr(self): - p = Structure.default() - assert p.__repr__( - ) == 'EasyStructure:\n- EasyMultiLayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n- EasyMultiLayer:\n EasyLayers:\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n - EasyLayer:\n material:\n EasyMaterial:\n sld: 4.186e-6 1 / angstrom ** 2\n isld: 0.000e-6 1 / angstrom ** 2\n thickness: 10.000 angstrom\n roughness: 3.300 angstrom\n' - - def test_dict_round_trip(self): - p = Structure.default() - q = Structure.from_dict(p.as_dict()) - assert p.as_data_dict() == q.as_data_dict() \ No newline at end of file diff --git a/tests/test_fitting.py b/tests/test_fitting.py index 5c132fdd..739afd17 100644 --- a/tests/test_fitting.py +++ b/tests/test_fitting.py @@ -6,14 +6,15 @@ import os import unittest + import EasyReflectometry from EasyReflectometry.data import load from EasyReflectometry.experiment.model import Model from EasyReflectometry.fitting import Fitter -from EasyReflectometry.calculators import CalculatorFactory as Interface +from EasyReflectometry.calculators import CalculatorFactory from EasyReflectometry.sample import Layer -from EasyReflectometry.sample import Structure -from EasyReflectometry.sample.material import Material +from EasyReflectometry.sample import Sample +from EasyReflectometry.sample import Material class TestFitting(unittest.TestCase): @@ -31,12 +32,12 @@ def test_fitting(self): sio2_layer = Layer.from_pars(sio2, 30, 3, 'SiO2 layer') film_layer = Layer.from_pars(film, 250, 3, 'Film Layer') superphase = Layer.from_pars(d2o, 0, 3, 'D2O Subphase') - structure = Structure.from_pars(si_layer, + sample = Sample.from_pars(si_layer, sio2_layer, film_layer, superphase, name='Film Structure') - model = Model.from_pars(structure, 1, 1e-6, 0.02, 'Film Model') + model = Model.from_pars(sample, 1, 1e-6, 0.02, 'Film Model') # Thicknesses sio2_layer.thickness.bounds = (15, 50) film_layer.thickness.bounds = (200, 300) @@ -50,7 +51,7 @@ def test_fitting(self): model.background.bounds = (1e-7, 1e-5) # Scale model.scale.bounds = (0.5, 1.5) - interface = Interface() + interface = CalculatorFactory() model.interface = interface fitter = Fitter(model) analysed = fitter.fit(data)