Skip to content

gradient_layer

LayerCollection

Bases: BaseCollection

Source code in src/easyreflectometry/sample/collections/layer_collection.py
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
class LayerCollection(BaseCollection):
    def __init__(
        self,
        *layers: Optional[list[Layer]],
        name: str = 'EasyLayerCollection',
        interface=None,
        unique_name: Optional[str] = None,
        populate_if_none: bool = True,
        **kwargs,
    ):
        """Init function."""
        if not layers:
            layers = []

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

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

        Parameters
        ----------
        layer : Optional[Layer], optional
            Layer to add. By default, None.
        """
        if layer is None:
            layer = Layer(
                name='EasyLayer added',
                interface=self.interface,
            )
        self.append(layer)

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

        Parameters
        ----------
        index : int
        layer :
            Assembly to add.
        """
        to_be_duplicated = self[index]
        duplicate = Layer.from_dict(to_be_duplicated.as_dict(skip=['unique_name']))
        duplicate.name = duplicate.name + ' duplicate'
        self.append(duplicate)

__init__(*layers, name='EasyLayerCollection', interface=None, unique_name=None, populate_if_none=True, **kwargs)

Init function.

Source code in src/easyreflectometry/sample/collections/layer_collection.py
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
def __init__(
    self,
    *layers: Optional[list[Layer]],
    name: str = 'EasyLayerCollection',
    interface=None,
    unique_name: Optional[str] = None,
    populate_if_none: bool = True,
    **kwargs,
):
    """Init function."""
    if not layers:
        layers = []

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

add_layer(layer=None)

Add a layer to the collection.

Parameters:

Name Type Description Default
layer Optional[Layer]

Layer to add. By default, None.

None
Source code in src/easyreflectometry/sample/collections/layer_collection.py
34
35
36
37
38
39
40
41
42
43
44
45
46
47
def add_layer(self, layer: Optional[Layer] = None):
    """Add a layer to the collection.

    Parameters
    ----------
    layer : Optional[Layer], optional
        Layer to add. By default, None.
    """
    if layer is None:
        layer = Layer(
            name='EasyLayer added',
            interface=self.interface,
        )
    self.append(layer)

duplicate_layer(index)

Duplicate a layer in the collection.

Parameters:

Name Type Description Default
index int
required
layer

Assembly to add.

required
Source code in src/easyreflectometry/sample/collections/layer_collection.py
49
50
51
52
53
54
55
56
57
58
59
60
61
def duplicate_layer(self, index: int):
    """Duplicate a layer in the collection.

    Parameters
    ----------
    index : int
    layer :
        Assembly to add.
    """
    to_be_duplicated = self[index]
    duplicate = Layer.from_dict(to_be_duplicated.as_dict(skip=['unique_name']))
    duplicate.name = duplicate.name + ' duplicate'
    self.append(duplicate)

Layer

Bases: BaseCore

Source code in src/easyreflectometry/sample/elements/layers/layer.py
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
class Layer(BaseCore):
    def __init__(
        self,
        material: Union[Material, None] = None,
        thickness: Union[Parameter, float, None] = None,
        roughness: Union[Parameter, float, None] = None,
        name: str = 'EasyLayer',
        unique_name: Optional[str] = None,
        interface=None,
    ):
        """Constructor.

        Parameters
        ----------
        unique_name : Optional[str], optional
            By default, None.
        material : Union[Material, None], optional
            The material for the layer. By default, None.
        thickness : Union[Parameter, float, None], optional
            Layer thickness in Angstrom. By default, None.
        roughness : Union[Parameter, float, None], optional
            Upper roughness on the layer in Angstrom. By default, None.
        name : str, optional
            Name of the layer. By default, 'EasyLayer'.
        interface :
            Interface object. By default, None.
        """
        if material is None:
            material = Material(interface=interface)

        if unique_name is None:
            unique_name = global_object.generate_unique_name(self.__class__.__name__)

        thickness_value = thickness
        thickness = get_as_parameter(
            name='thickness',
            value=thickness,
            default_dict=DEFAULTS,
            unique_name_prefix=f'{unique_name}_Thickness',
        )
        thickness.default_limits_pending = not isinstance(thickness_value, Parameter)

        roughness_value = roughness
        roughness = get_as_parameter(
            name='roughness',
            value=roughness,
            default_dict=DEFAULTS,
            unique_name_prefix=f'{unique_name}_Roughness',
        )
        roughness.default_limits_pending = not isinstance(roughness_value, Parameter)

        super().__init__(name=name, unique_name=unique_name)
        self._material = material
        self._thickness = thickness
        self._roughness = roughness

        if interface is not None:
            self.interface = interface

    @property
    def material(self) -> Material:
        return self._material

    @material.setter
    def material(self, value: Material) -> None:
        self._material = value

    @property
    def thickness(self) -> Parameter:
        return self._thickness

    @thickness.setter
    def thickness(self, value: float) -> None:
        self._thickness.value = value

    @property
    def roughness(self) -> Parameter:
        return self._roughness

    @roughness.setter
    def roughness(self, value: float) -> None:
        self._roughness.value = value

    def assign_material(self, material: Material) -> None:
        """Assign a material to the layer interface.

        Parameters
        ----------
        material : Material
            The material to assign to the layer.
        """
        self._material = material
        if self.interface is not None:
            self.interface().assign_material_to_layer(self.material.unique_name, self.unique_name)

    # Representation
    @property
    def _dict_repr(self) -> dict[str, str]:
        """A simplified dict representation."""
        return {
            self.name: {
                'material': self.material._dict_repr,
                'thickness': f'{self.thickness.value:.3f} {self.thickness.unit}',
                'roughness': f'{self.roughness.value:.3f} {self.roughness.unit}',
            }
        }

__init__(material=None, thickness=None, roughness=None, name='EasyLayer', unique_name=None, interface=None)

Constructor.

Parameters:

Name Type Description Default
unique_name Optional[str]

By default, None.

None
material Union[Material, None]

The material for the layer. By default, None.

None
thickness Union[Parameter, float, None]

Layer thickness in Angstrom. By default, None.

None
roughness Union[Parameter, float, None]

Upper roughness on the layer in Angstrom. By default, None.

None
name str

Name of the layer. By default, 'EasyLayer'.

'EasyLayer'
interface

Interface object. By default, None.

None
Source code in src/easyreflectometry/sample/elements/layers/layer.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
def __init__(
    self,
    material: Union[Material, None] = None,
    thickness: Union[Parameter, float, None] = None,
    roughness: Union[Parameter, float, None] = None,
    name: str = 'EasyLayer',
    unique_name: Optional[str] = None,
    interface=None,
):
    """Constructor.

    Parameters
    ----------
    unique_name : Optional[str], optional
        By default, None.
    material : Union[Material, None], optional
        The material for the layer. By default, None.
    thickness : Union[Parameter, float, None], optional
        Layer thickness in Angstrom. By default, None.
    roughness : Union[Parameter, float, None], optional
        Upper roughness on the layer in Angstrom. By default, None.
    name : str, optional
        Name of the layer. By default, 'EasyLayer'.
    interface :
        Interface object. By default, None.
    """
    if material is None:
        material = Material(interface=interface)

    if unique_name is None:
        unique_name = global_object.generate_unique_name(self.__class__.__name__)

    thickness_value = thickness
    thickness = get_as_parameter(
        name='thickness',
        value=thickness,
        default_dict=DEFAULTS,
        unique_name_prefix=f'{unique_name}_Thickness',
    )
    thickness.default_limits_pending = not isinstance(thickness_value, Parameter)

    roughness_value = roughness
    roughness = get_as_parameter(
        name='roughness',
        value=roughness,
        default_dict=DEFAULTS,
        unique_name_prefix=f'{unique_name}_Roughness',
    )
    roughness.default_limits_pending = not isinstance(roughness_value, Parameter)

    super().__init__(name=name, unique_name=unique_name)
    self._material = material
    self._thickness = thickness
    self._roughness = roughness

    if interface is not None:
        self.interface = interface

assign_material(material)

Assign a material to the layer interface.

Parameters:

Name Type Description Default
material Material

The material to assign to the layer.

required
Source code in src/easyreflectometry/sample/elements/layers/layer.py
122
123
124
125
126
127
128
129
130
131
132
def assign_material(self, material: Material) -> None:
    """Assign a material to the layer interface.

    Parameters
    ----------
    material : Material
        The material to assign to the layer.
    """
    self._material = material
    if self.interface is not None:
        self.interface().assign_material_to_layer(self.material.unique_name, self.unique_name)

Material

Bases: BaseCore

Source code in src/easyreflectometry/sample/elements/materials/material.py
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
class Material(BaseCore):
    def __init__(
        self,
        sld: Union[Parameter, float, None] = None,
        isld: Union[Parameter, float, None] = None,
        name: str = 'EasyMaterial',
        unique_name: Optional[str] = None,
        interface=None,
    ):
        """Constructor.

        Parameters
        ----------
        unique_name : Optional[str], optional
            By default, None.
        sld : Union[Parameter, float, None], optional
            Real scattering length density. By default, None.
        isld : Union[Parameter, float, None], optional
            Imaginary scattering length density. By default, None.
        name : str, optional
            Name of the material. By default, 'EasyMaterial'.
        interface :
            Calculator interface. By default, None.
        """
        if unique_name is None:
            unique_name = global_object.generate_unique_name(self.__class__.__name__)

        sld = get_as_parameter(
            name='sld',
            value=sld,
            default_dict=DEFAULTS,
            unique_name_prefix=f'{unique_name}_Sld',
        )
        apply_default_limits(sld, 'sld')

        isld = get_as_parameter(
            name='isld',
            value=isld,
            default_dict=DEFAULTS,
            unique_name_prefix=f'{unique_name}_Isld',
        )
        apply_default_limits(isld, 'isld')

        super().__init__(name=name, unique_name=unique_name)
        self._sld = sld
        self._isld = isld

        if interface is not None:
            self.interface = interface

    @property
    def sld(self) -> Parameter:
        return self._sld

    @sld.setter
    def sld(self, value: float) -> None:
        self._sld.value = value

    @property
    def isld(self) -> Parameter:
        return self._isld

    @isld.setter
    def isld(self, value: float) -> None:
        self._isld.value = value

    # Representation
    @property
    def _dict_repr(self) -> dict[str, str]:
        """A simplified dict representation."""
        return {
            self.name: {
                'sld': f'{self._sld.value:.3f}e-6 {self._sld.unit}',
                'isld': f'{self._isld.value:.3f}e-6 {self._isld.unit}',
            }
        }

__init__(sld=None, isld=None, name='EasyMaterial', unique_name=None, interface=None)

Constructor.

Parameters:

Name Type Description Default
unique_name Optional[str]

By default, None.

None
sld Union[Parameter, float, None]

Real scattering length density. By default, None.

None
isld Union[Parameter, float, None]

Imaginary scattering length density. By default, None.

None
name str

Name of the material. By default, 'EasyMaterial'.

'EasyMaterial'
interface

Calculator interface. By default, None.

None
Source code in src/easyreflectometry/sample/elements/materials/material.py
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
def __init__(
    self,
    sld: Union[Parameter, float, None] = None,
    isld: Union[Parameter, float, None] = None,
    name: str = 'EasyMaterial',
    unique_name: Optional[str] = None,
    interface=None,
):
    """Constructor.

    Parameters
    ----------
    unique_name : Optional[str], optional
        By default, None.
    sld : Union[Parameter, float, None], optional
        Real scattering length density. By default, None.
    isld : Union[Parameter, float, None], optional
        Imaginary scattering length density. By default, None.
    name : str, optional
        Name of the material. By default, 'EasyMaterial'.
    interface :
        Calculator interface. By default, None.
    """
    if unique_name is None:
        unique_name = global_object.generate_unique_name(self.__class__.__name__)

    sld = get_as_parameter(
        name='sld',
        value=sld,
        default_dict=DEFAULTS,
        unique_name_prefix=f'{unique_name}_Sld',
    )
    apply_default_limits(sld, 'sld')

    isld = get_as_parameter(
        name='isld',
        value=isld,
        default_dict=DEFAULTS,
        unique_name_prefix=f'{unique_name}_Isld',
    )
    apply_default_limits(isld, 'isld')

    super().__init__(name=name, unique_name=unique_name)
    self._sld = sld
    self._isld = isld

    if interface is not None:
        self.interface = interface

BaseAssembly

Bases: BaseCore

Assembly of layers.

The front layer (front_layer) is the layer the neutron beam starts in, it has an index of 0. The back layer (back_layer) is the final layer from which the unreflected neutron beam is transmitted, its index number depends on the number of finite layers in the system, but it might be accessed at index -1.

Source code in src/easyreflectometry/sample/assemblies/base_assembly.py
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
class BaseAssembly(BaseCore):
    """Assembly of layers.

    The front layer (front_layer) is the layer the neutron beam starts in, it has an index of 0.
    The back layer (back_layer) is the final layer from which the unreflected neutron beam is transmitted,
    its index number depends on the number of finite layers in the system, but it might be accessed at index -1.
    """

    def __init__(
        self,
        name: str,
        type: str,
        interface,
        layers: LayerCollection,
        unique_name: Optional[str] = None,
    ):
        super().__init__(name=name, unique_name=unique_name)
        self._layers = layers

        # Type is needed when fitting in easyscience
        self._type = type
        self._roughness_constraints_setup = False
        self._thickness_constraints_setup = False

        if interface is not None:
            self.interface = interface

    @property
    def layers(self) -> LayerCollection:
        return self._layers

    @layers.setter
    def layers(self, value: LayerCollection) -> None:
        self._layers = value

    @property
    def type(self) -> str:
        """Get type of the assembly.

        Needed by the GUI.
        """
        return self._type

    @property
    def front_layer(self) -> Optional[Layer]:
        """Get the front layer in the assembly."""
        if len(self.layers) == 0:
            return None
        return self.layers[0]

    @front_layer.setter
    def front_layer(self, layer: Layer) -> None:
        """Set the front layer in the assembly.

        Parameters
        ----------
        layer : Layer
            Layer to set as the front layer.
        """
        if len(self.layers) == 0:
            self.layers.append(layer)
        else:
            self.layers[0] = layer

    @property
    def back_layer(self) -> Optional[Layer]:
        """Get the back layer in the assembly."""

        if len(self.layers) < 2:
            return None
        return self.layers[-1]

    @back_layer.setter
    def back_layer(self, layer: Layer) -> None:
        """Set the back layer in the assembly.

        Parameters
        ----------
        layer : Layer
            Layer to set as the back layer.
        """

        if len(self.layers) == 0:
            raise Exception('There is no front layer to add the back layer to. Please add a front layer first.')
        if len(self.layers) == 1:
            self.layers.append(layer)
        else:
            self.layers[-1] = layer

    def _setup_thickness_constraints(self) -> None:
        """Setup thickness constraint, front layer is the deciding layer."""
        independent_param = self.front_layer.thickness
        for i in range(1, len(self.layers)):
            self.layers[i].thickness.make_dependent_on(dependency_expression='a', dependency_map={'a': independent_param})
        self._thickness_constraints_setup = True

    def _enable_thickness_constraints(self):
        """Enable the thickness constraint."""
        if self._thickness_constraints_setup:
            # Make sure that the thickness constraint is enabled
            self._setup_thickness_constraints()
            # Make sure that the thickness parameter is enabled
        else:
            raise Exception('Thickness constraints not setup')

    def _disable_thickness_constraints(self):
        """Disable the thickness constraint."""
        if self._thickness_constraints_setup:
            for i in range(1, len(self.layers)):
                self.layers[i].thickness.make_independent()
        else:
            raise Exception('Thickness constraints not setup')

    def _setup_roughness_constraints(self) -> None:
        """Setup roughness constraint, front layer is the deciding layer."""
        independent_parameter = self.front_layer.roughness
        for i in range(1, len(self.layers)):
            self.layers[i].roughness.make_dependent_on(dependency_expression='a', dependency_map={'a': independent_parameter})
        self._roughness_constraints_setup = True

    def _enable_roughness_constraints(self):
        """Enable the roughness constraint."""
        independent_parameter = self.front_layer.roughness
        for i in range(1, len(self.layers)):
            self.layers[i].roughness.make_dependent_on(dependency_expression='a', dependency_map={'a': independent_parameter})

    def _disable_roughness_constraints(self):
        """Disable the roughness constraint."""
        for i in range(1, len(self.layers)):
            self.layers[i].roughness.make_independent()

type property

Get type of the assembly.

Needed by the GUI.

front_layer property writable

Get the front layer in the assembly.

back_layer property writable

Get the back layer in the assembly.

GradientLayer

Bases: BaseAssembly

A set of discrete gradient layers changing from the front to the back material.

The front layer is where the neutron beam starts in, it has an index of 0.

Source code in src/easyreflectometry/sample/assemblies/gradient_layer.py
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
class GradientLayer(BaseAssembly):
    """A set of discrete gradient layers changing from the front to the back material.

    The front layer is where the neutron beam starts in, it has an index of 0.
    """

    def __init__(
        self,
        front_material: Optional[Material] = None,
        back_material: Optional[Material] = None,
        thickness: Optional[float] = 2.0,
        roughness: Optional[float] = 0.2,
        discretisation_elements: int = 10,
        name: str = 'EasyGradienLayer',
        unique_name: Optional[str] = None,
        interface=None,
    ):
        """Constructor.

        Parameters
        ----------
        unique_name : Optional[str], optional
            By default, None.
        front_material : Optional[Material], optional
            Material of front of the layer. By default, None.
        back_material : Optional[Material], optional
            Material of back of the layer. By default, None.
        thickness : Optional[float], optional
            Thicknkess of the layer. By default, 2.0.
        roughness : Optional[float], optional
            Roughness of the layer. By default, 0.2.
        discretisation_elements : int, optional
            Number of discrete layers. By default, 10.
        name : str, optional
            Name for gradient layer. By default, 'EasyGradienLayer'.
        interface :
            Calculator interface. By default, None.
        """

        if front_material is None:
            front_material = Material(0.0, 0.0, 'Air')

        if back_material is None:
            back_material = Material(6.36, 0.0, 'D2O')

        if discretisation_elements < 2:
            raise ValueError('Discretisation elements must be greater than 2.')

        gradient_layers = _prepare_gradient_layers(
            front_material=front_material,
            back_material=back_material,
            discretisation_elements=discretisation_elements,
            interface=interface,
        )

        super().__init__(
            layers=gradient_layers,
            name=name,
            unique_name=unique_name,
            interface=None,
            type='Gradient-layer',
        )
        self._front_material = front_material
        self._back_material = back_material
        self._discretisation_elements = discretisation_elements

        self._setup_thickness_constraints()
        self._enable_thickness_constraints()
        self._setup_roughness_constraints()
        self._enable_roughness_constraints()

        # Set the thickness and roughness properties
        self.thickness = thickness
        self.roughness = roughness

        if interface is not None:
            self.interface = interface

    @property
    def front_material(self) -> Material:
        return self._front_material

    @property
    def back_material(self) -> Material:
        return self._back_material

    @property
    def discretisation_elements(self) -> int:
        return self._discretisation_elements

    @property
    def thickness(self) -> float:
        """Get the thickness of the gradient layer in Angstrom."""
        return self.front_layer.thickness.value * self._discretisation_elements

    @thickness.setter
    def thickness(self, thickness: float) -> None:
        """Set the thickness of the gradient layer.

        Parameters
        ----------
        thickness : float
            Thickness of the gradient layer in Angstroms.
        """
        self.front_layer.thickness.value = thickness / self._discretisation_elements

    @property
    def roughness(self) -> float:
        """Get the Roughness of the gradient layer in Angstrom."""
        return self.front_layer.roughness.value

    @roughness.setter
    def roughness(self, roughness: float) -> None:
        """Set the roughness of the gradient layer.

        Parameters
        ----------
        roughness : float
            Roughness of the gradient layer in Angstroms.
        """
        self.front_layer.roughness.value = roughness

    @property
    def _dict_repr(self) -> dict[str, str]:
        """A simplified dict representation."""
        return {
            'thickness': float(self.thickness),  # Conversion to float is necessary to prevent property reference in dict
            'discretisation_elements': int(self._discretisation_elements),  # Same as above
            'back_layer': self.back_layer._dict_repr,
            'front_layer': self.front_layer._dict_repr,
        }

    def to_dict(self, skip: Optional[list[str]] = None) -> dict:
        """Produces a cleaned dict using a custom to_dict method to skip necessary things.

        The resulting dict matches the parameters in __init__: layers are derived
        in ``__init__`` from ``front_material``/``back_material``/``discretisation_elements``
        so they are excluded from the serialized representation.

        Parameters
        ----------
        skip : Optional[list[str]], optional
            List of keys to skip. By default, None.
        """
        this_dict = super().to_dict(skip=skip)
        # Determined in __init__
        this_dict.pop('layers', None)
        # `thickness` / `roughness` are read-only float views; the serialized
        # constructor args are the floats themselves.
        this_dict['thickness'] = float(self.thickness)
        this_dict['roughness'] = float(self.roughness)
        return this_dict

    def as_dict(self, skip: Optional[list[str]] = None) -> dict:
        """Compatibility alias for :meth:`to_dict`."""
        return self.to_dict(skip=skip)

thickness property writable

Get the thickness of the gradient layer in Angstrom.

roughness property writable

Get the Roughness of the gradient layer in Angstrom.

__init__(front_material=None, back_material=None, thickness=2.0, roughness=0.2, discretisation_elements=10, name='EasyGradienLayer', unique_name=None, interface=None)

Constructor.

Parameters:

Name Type Description Default
unique_name Optional[str]

By default, None.

None
front_material Optional[Material]

Material of front of the layer. By default, None.

None
back_material Optional[Material]

Material of back of the layer. By default, None.

None
thickness Optional[float]

Thicknkess of the layer. By default, 2.0.

2.0
roughness Optional[float]

Roughness of the layer. By default, 0.2.

0.2
discretisation_elements int

Number of discrete layers. By default, 10.

10
name str

Name for gradient layer. By default, 'EasyGradienLayer'.

'EasyGradienLayer'
interface

Calculator interface. By default, None.

None
Source code in src/easyreflectometry/sample/assemblies/gradient_layer.py
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
def __init__(
    self,
    front_material: Optional[Material] = None,
    back_material: Optional[Material] = None,
    thickness: Optional[float] = 2.0,
    roughness: Optional[float] = 0.2,
    discretisation_elements: int = 10,
    name: str = 'EasyGradienLayer',
    unique_name: Optional[str] = None,
    interface=None,
):
    """Constructor.

    Parameters
    ----------
    unique_name : Optional[str], optional
        By default, None.
    front_material : Optional[Material], optional
        Material of front of the layer. By default, None.
    back_material : Optional[Material], optional
        Material of back of the layer. By default, None.
    thickness : Optional[float], optional
        Thicknkess of the layer. By default, 2.0.
    roughness : Optional[float], optional
        Roughness of the layer. By default, 0.2.
    discretisation_elements : int, optional
        Number of discrete layers. By default, 10.
    name : str, optional
        Name for gradient layer. By default, 'EasyGradienLayer'.
    interface :
        Calculator interface. By default, None.
    """

    if front_material is None:
        front_material = Material(0.0, 0.0, 'Air')

    if back_material is None:
        back_material = Material(6.36, 0.0, 'D2O')

    if discretisation_elements < 2:
        raise ValueError('Discretisation elements must be greater than 2.')

    gradient_layers = _prepare_gradient_layers(
        front_material=front_material,
        back_material=back_material,
        discretisation_elements=discretisation_elements,
        interface=interface,
    )

    super().__init__(
        layers=gradient_layers,
        name=name,
        unique_name=unique_name,
        interface=None,
        type='Gradient-layer',
    )
    self._front_material = front_material
    self._back_material = back_material
    self._discretisation_elements = discretisation_elements

    self._setup_thickness_constraints()
    self._enable_thickness_constraints()
    self._setup_roughness_constraints()
    self._enable_roughness_constraints()

    # Set the thickness and roughness properties
    self.thickness = thickness
    self.roughness = roughness

    if interface is not None:
        self.interface = interface

to_dict(skip=None)

Produces a cleaned dict using a custom to_dict method to skip necessary things.

The resulting dict matches the parameters in init: layers are derived in __init__ from front_material/back_material/discretisation_elements so they are excluded from the serialized representation.

Parameters:

Name Type Description Default
skip Optional[list[str]]

List of keys to skip. By default, None.

None
Source code in src/easyreflectometry/sample/assemblies/gradient_layer.py
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
def to_dict(self, skip: Optional[list[str]] = None) -> dict:
    """Produces a cleaned dict using a custom to_dict method to skip necessary things.

    The resulting dict matches the parameters in __init__: layers are derived
    in ``__init__`` from ``front_material``/``back_material``/``discretisation_elements``
    so they are excluded from the serialized representation.

    Parameters
    ----------
    skip : Optional[list[str]], optional
        List of keys to skip. By default, None.
    """
    this_dict = super().to_dict(skip=skip)
    # Determined in __init__
    this_dict.pop('layers', None)
    # `thickness` / `roughness` are read-only float views; the serialized
    # constructor args are the floats themselves.
    this_dict['thickness'] = float(self.thickness)
    this_dict['roughness'] = float(self.roughness)
    return this_dict

as_dict(skip=None)

Compatibility alias for :meth:to_dict.

Source code in src/easyreflectometry/sample/assemblies/gradient_layer.py
168
169
170
def as_dict(self, skip: Optional[list[str]] = None) -> dict:
    """Compatibility alias for :meth:`to_dict`."""
    return self.to_dict(skip=skip)

_linear_gradient(front_value, back_value, discretisation_elements)

Linear gradient.

Source code in src/easyreflectometry/sample/assemblies/gradient_layer.py
173
174
175
176
177
178
179
180
181
182
183
184
185
def _linear_gradient(
    front_value: float,
    back_value: float,
    discretisation_elements: int,
) -> list[float]:
    """Linear gradient."""
    discrete_step = (back_value - front_value) / discretisation_elements
    if discrete_step != 0:
        # Both front and back values are included
        gradient = arange(front_value, back_value + discrete_step, discrete_step)
    else:
        gradient = [front_value] * discretisation_elements
    return gradient

_prepare_gradient_layers(front_material, back_material, discretisation_elements, interface=None)

Prepare gradient layers.

Source code in src/easyreflectometry/sample/assemblies/gradient_layer.py
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
def _prepare_gradient_layers(
    front_material: Material,
    back_material: Material,
    discretisation_elements: int,
    interface=None,
) -> LayerCollection:
    """Prepare gradient layers."""
    gradient_sld = _linear_gradient(
        front_value=front_material.sld.value,
        back_value=back_material.sld.value,
        discretisation_elements=discretisation_elements,
    )
    gradient_isld = _linear_gradient(
        front_value=front_material.isld.value,
        back_value=back_material.isld.value,
        discretisation_elements=discretisation_elements,
    )
    gradient_layers = []
    for i in range(discretisation_elements):
        material_i = Material(gradient_sld[i], gradient_isld[i], interface=interface)
        layer = Layer(
            material=material_i,
            thickness=0.0,
            roughness=0.0,
            name=str(i),
            interface=interface,
            unique_name=global_object.generate_unique_name('GradientLayer'),
        )
        gradient_layers.append(layer)
    return LayerCollection(gradient_layers)