ratapi 0.0.0.dev16__cp315-cp315-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ratapi/__init__.py +42 -0
- ratapi/classlist.py +620 -0
- ratapi/controls.py +258 -0
- ratapi/eventManager.dll +0 -0
- ratapi/events.py +88 -0
- ratapi/examples/__init__.py +25 -0
- ratapi/examples/absorption/__init__.py +1 -0
- ratapi/examples/absorption/absorption.py +172 -0
- ratapi/examples/absorption/volume_thiol_bilayer.py +144 -0
- ratapi/examples/convert_rascal_project/Model_IIb.py +90 -0
- ratapi/examples/convert_rascal_project/__init__.py +1 -0
- ratapi/examples/convert_rascal_project/convert_rascal.py +49 -0
- ratapi/examples/data/D2O_spin_down.dat +100 -0
- ratapi/examples/data/D2O_spin_up.dat +101 -0
- ratapi/examples/data/DSPC_D2O.dat +82 -0
- ratapi/examples/data/DSPC_SMW.dat +82 -0
- ratapi/examples/data/H2O_spin_down.dat +102 -0
- ratapi/examples/data/H2O_spin_up.dat +102 -0
- ratapi/examples/data/__init__.py +1 -0
- ratapi/examples/data/c_PLP0016596.dat +146 -0
- ratapi/examples/data/c_PLP0016601.dat +97 -0
- ratapi/examples/data/c_PLP0016607.dat +104 -0
- ratapi/examples/data/d2o_background_data.dat +82 -0
- ratapi/examples/domains/__init__.py +1 -0
- ratapi/examples/domains/alloy_domains.py +34 -0
- ratapi/examples/domains/domains_XY_model.py +75 -0
- ratapi/examples/domains/domains_custom_XY.py +86 -0
- ratapi/examples/domains/domains_custom_layers.py +60 -0
- ratapi/examples/domains/domains_standard_layers.py +92 -0
- ratapi/examples/languages/__init__.py +1 -0
- ratapi/examples/languages/custom_bilayer.py +72 -0
- ratapi/examples/languages/run_custom_file_languages.py +41 -0
- ratapi/examples/languages/setup_problem.py +130 -0
- ratapi/examples/normal_reflectivity/DSPC_custom_XY.py +149 -0
- ratapi/examples/normal_reflectivity/DSPC_custom_layers.py +130 -0
- ratapi/examples/normal_reflectivity/DSPC_data_background.py +220 -0
- ratapi/examples/normal_reflectivity/DSPC_function_background.py +219 -0
- ratapi/examples/normal_reflectivity/DSPC_standard_layers.py +210 -0
- ratapi/examples/normal_reflectivity/__init__.py +1 -0
- ratapi/examples/normal_reflectivity/background_function.py +16 -0
- ratapi/examples/normal_reflectivity/custom_XY_DSPC.py +141 -0
- ratapi/examples/normal_reflectivity/custom_bilayer_DSPC.py +89 -0
- ratapi/inputs.py +603 -0
- ratapi/models.py +717 -0
- ratapi/outputs.py +821 -0
- ratapi/project.py +1091 -0
- ratapi/rat_core.cp315-win_amd64.pyd +0 -0
- ratapi/run.py +142 -0
- ratapi/utils/__init__.py +1 -0
- ratapi/utils/convert.py +597 -0
- ratapi/utils/custom_errors.py +40 -0
- ratapi/utils/enums.py +203 -0
- ratapi/utils/matlab.py +254 -0
- ratapi/utils/orso.py +247 -0
- ratapi/utils/plotting.py +1316 -0
- ratapi/wrappers.py +147 -0
- ratapi-0.0.0.dev16.dist-info/DELVEWHEEL +2 -0
- ratapi-0.0.0.dev16.dist-info/METADATA +61 -0
- ratapi-0.0.0.dev16.dist-info/RECORD +63 -0
- ratapi-0.0.0.dev16.dist-info/WHEEL +5 -0
- ratapi-0.0.0.dev16.dist-info/top_level.txt +1 -0
- ratapi.libs/msvcp140-a4c2229bdc2a2a630acdc095b4d86008.dll +0 -0
- ratapi.libs/vcomp140-f96f3a14d88d8846f31f3ab38a490304.dll +0 -0
ratapi/controls.py
ADDED
|
@@ -0,0 +1,258 @@
|
|
|
1
|
+
"""The Controls class for providing RAT algorithm settings."""
|
|
2
|
+
|
|
3
|
+
import contextlib
|
|
4
|
+
import os
|
|
5
|
+
import tempfile
|
|
6
|
+
import warnings
|
|
7
|
+
from pathlib import Path
|
|
8
|
+
|
|
9
|
+
import prettytable
|
|
10
|
+
from pydantic import (
|
|
11
|
+
BaseModel,
|
|
12
|
+
Field,
|
|
13
|
+
ValidationError,
|
|
14
|
+
ValidatorFunctionWrapHandler,
|
|
15
|
+
model_serializer,
|
|
16
|
+
model_validator,
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
from ratapi.utils.custom_errors import custom_pydantic_validation_error
|
|
20
|
+
from ratapi.utils.enums import BoundHandling, Display, Parallel, Procedures, Strategies
|
|
21
|
+
|
|
22
|
+
common_fields = [
|
|
23
|
+
"procedure",
|
|
24
|
+
"parallel",
|
|
25
|
+
"numSimulationPoints",
|
|
26
|
+
"resampleMinAngle",
|
|
27
|
+
"resampleNPoints",
|
|
28
|
+
"display",
|
|
29
|
+
]
|
|
30
|
+
update_fields = ["updateFreq", "updatePlotFreq"]
|
|
31
|
+
fields = {
|
|
32
|
+
"calculate": common_fields,
|
|
33
|
+
"simplex": [*common_fields, "xTolerance", "funcTolerance", "maxFuncEvals", "maxIterations", *update_fields],
|
|
34
|
+
"de": [
|
|
35
|
+
*common_fields,
|
|
36
|
+
"populationSize",
|
|
37
|
+
"fWeight",
|
|
38
|
+
"crossoverProbability",
|
|
39
|
+
"strategy",
|
|
40
|
+
"targetValue",
|
|
41
|
+
"numGenerations",
|
|
42
|
+
*update_fields,
|
|
43
|
+
],
|
|
44
|
+
"ns": [*common_fields, "nLive", "nMCMC", "propScale", "nsTolerance"],
|
|
45
|
+
"dream": [*common_fields, "nSamples", "nChains", "jumpProbability", "pUnitGamma", "boundHandling", "adaptPCR"],
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
class Controls(BaseModel, validate_assignment=True, extra="forbid", use_attribute_docstrings=True):
|
|
50
|
+
"""The full set of controls parameters for all five procedures that are required for the compiled RAT code."""
|
|
51
|
+
|
|
52
|
+
# All Procedures
|
|
53
|
+
procedure: Procedures = Procedures.Calculate
|
|
54
|
+
"""Which procedure RAT should execute. Can be 'calculate', 'simplex', 'de', 'ns', or 'dream'."""
|
|
55
|
+
|
|
56
|
+
parallel: Parallel = Parallel.Single
|
|
57
|
+
"""How the calculation should be parallelised. Can be 'single', 'contrasts' or 'points'."""
|
|
58
|
+
|
|
59
|
+
numSimulationPoints: int = Field(500, ge=2)
|
|
60
|
+
"""The number of points used for reflectivity simulations where no data is supplied."""
|
|
61
|
+
|
|
62
|
+
resampleMinAngle: float = Field(0.9, le=1, gt=0)
|
|
63
|
+
"""The upper threshold on the angle between three sampled points for resampling, in units of radians over pi."""
|
|
64
|
+
|
|
65
|
+
resampleNPoints: int = Field(50, gt=0)
|
|
66
|
+
"""The number of initial points to use for resampling."""
|
|
67
|
+
|
|
68
|
+
display: Display = Display.Iter
|
|
69
|
+
"""How much RAT should print to the terminal. Can be 'off', 'iter', or 'final'."""
|
|
70
|
+
|
|
71
|
+
# Simplex
|
|
72
|
+
xTolerance: float = Field(1.0e-6, gt=0.0)
|
|
73
|
+
"""[SIMPLEX] The termination tolerance for step size."""
|
|
74
|
+
|
|
75
|
+
funcTolerance: float = Field(1.0e-6, gt=0.0)
|
|
76
|
+
"""[SIMPLEX] The termination tolerance for change in chi-squared."""
|
|
77
|
+
|
|
78
|
+
maxFuncEvals: int = Field(10000, gt=0)
|
|
79
|
+
"""[SIMPLEX] The maximum number of function evaluations before the algorithm terminates."""
|
|
80
|
+
|
|
81
|
+
maxIterations: int = Field(1000, gt=0)
|
|
82
|
+
"""[SIMPLEX] The maximum number of iterations before the algorithm terminates."""
|
|
83
|
+
|
|
84
|
+
# Simplex and DE
|
|
85
|
+
updateFreq: int = 1
|
|
86
|
+
"""[SIMPLEX, DE] Number of iterations between printing progress updates to the terminal."""
|
|
87
|
+
|
|
88
|
+
updatePlotFreq: int = 20
|
|
89
|
+
"""[SIMPLEX, DE] Number of iterations between updates to live plots."""
|
|
90
|
+
|
|
91
|
+
# DE
|
|
92
|
+
populationSize: int = Field(20, ge=1)
|
|
93
|
+
"""[DE] The number of candidate solutions that exist at any time."""
|
|
94
|
+
|
|
95
|
+
fWeight: float = Field(0.5, gt=0.0)
|
|
96
|
+
"""[DE] The step size for how different mutations are to their parents."""
|
|
97
|
+
|
|
98
|
+
crossoverProbability: float = Field(0.8, gt=0.0, lt=1.0)
|
|
99
|
+
"""[DE] The probability of exchange of parameters between individuals at any iteration."""
|
|
100
|
+
|
|
101
|
+
strategy: Strategies = Strategies.RandomWithPerVectorDither
|
|
102
|
+
"""[DE] The algorithm used to generate new candidates."""
|
|
103
|
+
|
|
104
|
+
targetValue: float = Field(1.0, ge=1.0)
|
|
105
|
+
"""[DE] The value of chi-squared at which the algorithm will terminate."""
|
|
106
|
+
|
|
107
|
+
numGenerations: int = Field(500, ge=1)
|
|
108
|
+
"""[DE] The maximum number of iterations before the algorithm terminates."""
|
|
109
|
+
|
|
110
|
+
# NS
|
|
111
|
+
nLive: int = Field(150, ge=1)
|
|
112
|
+
"""[NS] The number of points to sample."""
|
|
113
|
+
|
|
114
|
+
nMCMC: int = Field(0, ge=0)
|
|
115
|
+
"""[NS] If non-zero, an MCMC process with ``nMCMC`` chains will be used instead of MultiNest."""
|
|
116
|
+
|
|
117
|
+
propScale: float = Field(0.1, gt=0.0, lt=1.0)
|
|
118
|
+
"""[NS] A scaling factor for the ellipsoid generated by MultiNest."""
|
|
119
|
+
|
|
120
|
+
nsTolerance: float = Field(0.1, ge=0.0)
|
|
121
|
+
"""[NS] The tolerance threshold for when the algorithm should terminate."""
|
|
122
|
+
|
|
123
|
+
# Dream
|
|
124
|
+
nSamples: int = Field(20000, ge=0)
|
|
125
|
+
"""[DREAM] The total number of function evaluations (number of algorithm generations times number of chains)."""
|
|
126
|
+
|
|
127
|
+
nChains: int = Field(10, gt=1)
|
|
128
|
+
"""[DREAM] The number of Markov chains to use in the algorithm."""
|
|
129
|
+
|
|
130
|
+
jumpProbability: float = Field(0.5, gt=0.0, lt=1.0)
|
|
131
|
+
"""[DREAM] The probability range for the size of jumps in sampling. Larger values mean more variable jumps."""
|
|
132
|
+
|
|
133
|
+
pUnitGamma: float = Field(0.2, gt=0.0, lt=1.0)
|
|
134
|
+
"""[DREAM] The probability that the scaling-down factor of jumps will be ignored and a larger jump will be taken."""
|
|
135
|
+
|
|
136
|
+
boundHandling: BoundHandling = BoundHandling.Reflect
|
|
137
|
+
"""[DREAM] How steps past the space boundaries should be handled. Can be 'off', 'reflect', 'bound', or 'fold'."""
|
|
138
|
+
|
|
139
|
+
adaptPCR: bool = True
|
|
140
|
+
"""[DREAM] Whether the crossover probability for differential evolution should be adapted during the run."""
|
|
141
|
+
|
|
142
|
+
# Private field for IPC file
|
|
143
|
+
_IPCFilePath: str = ""
|
|
144
|
+
|
|
145
|
+
@model_validator(mode="wrap")
|
|
146
|
+
def warn_setting_incorrect_properties(self, handler: ValidatorFunctionWrapHandler) -> "Controls":
|
|
147
|
+
"""Raise a warning if the user sets fields that apply to other procedures."""
|
|
148
|
+
model_input = self
|
|
149
|
+
try:
|
|
150
|
+
input_dict = model_input.__dict__
|
|
151
|
+
except AttributeError:
|
|
152
|
+
input_dict = model_input
|
|
153
|
+
|
|
154
|
+
validated_self = None
|
|
155
|
+
try:
|
|
156
|
+
validated_self = handler(self)
|
|
157
|
+
except ValidationError as exc:
|
|
158
|
+
procedure = input_dict.get("procedure", Procedures.Calculate)
|
|
159
|
+
custom_error_msgs = {
|
|
160
|
+
"extra_forbidden": f'Extra inputs are not permitted. The fields for the "{procedure}"'
|
|
161
|
+
f" controls procedure are:\n "
|
|
162
|
+
f"{', '.join(fields.get('procedure', []))}\n",
|
|
163
|
+
}
|
|
164
|
+
custom_error_list = custom_pydantic_validation_error(exc.errors(include_url=False), custom_error_msgs)
|
|
165
|
+
raise ValidationError.from_exception_data(exc.title, custom_error_list, hide_input=True) from None
|
|
166
|
+
|
|
167
|
+
if isinstance(model_input, validated_self.__class__):
|
|
168
|
+
# This is for changing fields in a defined model
|
|
169
|
+
changed_fields = [key for key in input_dict if input_dict[key] != validated_self.__dict__[key]]
|
|
170
|
+
elif isinstance(model_input, dict):
|
|
171
|
+
# This is for a newly-defined model
|
|
172
|
+
changed_fields = input_dict.keys()
|
|
173
|
+
else:
|
|
174
|
+
raise ValueError('The input to the "Controls" model is invalid.')
|
|
175
|
+
|
|
176
|
+
new_procedure = validated_self.procedure
|
|
177
|
+
allowed_fields = fields[new_procedure]
|
|
178
|
+
for field in changed_fields:
|
|
179
|
+
if field not in allowed_fields:
|
|
180
|
+
incorrect_procedures = [key for (key, value) in fields.items() if field in value]
|
|
181
|
+
warnings.warn(
|
|
182
|
+
f'\nThe current controls procedure is "{new_procedure}", but the property'
|
|
183
|
+
f' "{field}" applies instead to the {", ".join(incorrect_procedures)} procedure.\n\n'
|
|
184
|
+
f' The fields for the "{new_procedure}" controls procedure are:\n'
|
|
185
|
+
f" {', '.join(fields[new_procedure])}\n",
|
|
186
|
+
stacklevel=2,
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return validated_self
|
|
190
|
+
|
|
191
|
+
@model_serializer
|
|
192
|
+
def serialize(self):
|
|
193
|
+
"""Filter fields so only those applying to the chosen procedure are serialized."""
|
|
194
|
+
return {model_field: getattr(self, model_field) for model_field in fields[self.procedure]}
|
|
195
|
+
|
|
196
|
+
def __repr__(self) -> str:
|
|
197
|
+
fields_repr = ", ".join(repr(v) if a is None else f"{a}={v!r}" for a, v in self.model_dump().items())
|
|
198
|
+
return f"{self.__repr_name__()}({fields_repr})"
|
|
199
|
+
|
|
200
|
+
def __str__(self) -> str:
|
|
201
|
+
table = prettytable.PrettyTable()
|
|
202
|
+
table.field_names = ["Property", "Value"]
|
|
203
|
+
table.add_rows([[k, v] for k, v in self.model_dump().items()])
|
|
204
|
+
return table.get_string()
|
|
205
|
+
|
|
206
|
+
def initialise_IPC(self):
|
|
207
|
+
"""Set up the inter-process communication file."""
|
|
208
|
+
IPC_obj, self._IPCFilePath = tempfile.mkstemp()
|
|
209
|
+
os.write(IPC_obj, b"\x00")
|
|
210
|
+
os.close(IPC_obj)
|
|
211
|
+
return None
|
|
212
|
+
|
|
213
|
+
def sendStopEvent(self):
|
|
214
|
+
"""Send the stop event via the inter-process communication file.
|
|
215
|
+
|
|
216
|
+
Warnings
|
|
217
|
+
--------
|
|
218
|
+
UserWarning
|
|
219
|
+
Raised if we try to delete an IPC file that was not initialised.
|
|
220
|
+
|
|
221
|
+
"""
|
|
222
|
+
if os.path.isfile(self._IPCFilePath):
|
|
223
|
+
with open(self._IPCFilePath, "wb") as f:
|
|
224
|
+
f.write(b"\x01")
|
|
225
|
+
else:
|
|
226
|
+
warnings.warn("An IPC file was not initialised.", UserWarning, stacklevel=2)
|
|
227
|
+
return None
|
|
228
|
+
|
|
229
|
+
def delete_IPC(self):
|
|
230
|
+
"""Delete the inter-process communication file."""
|
|
231
|
+
with contextlib.suppress(FileNotFoundError):
|
|
232
|
+
os.remove(self._IPCFilePath)
|
|
233
|
+
self._IPCFilePath = ""
|
|
234
|
+
return None
|
|
235
|
+
|
|
236
|
+
def save(self, filepath: str | Path = "./controls.json"):
|
|
237
|
+
"""Save a controls object to a JSON file.
|
|
238
|
+
|
|
239
|
+
Parameters
|
|
240
|
+
----------
|
|
241
|
+
filepath : str or Path
|
|
242
|
+
The path to where the controls file will be written.
|
|
243
|
+
"""
|
|
244
|
+
filepath = Path(filepath).with_suffix(".json")
|
|
245
|
+
filepath.write_text(self.model_dump_json())
|
|
246
|
+
|
|
247
|
+
@classmethod
|
|
248
|
+
def load(cls, path: str | Path) -> "Controls":
|
|
249
|
+
"""Load a controls object from file.
|
|
250
|
+
|
|
251
|
+
Parameters
|
|
252
|
+
----------
|
|
253
|
+
path : str or Path
|
|
254
|
+
The path to the controls object file.
|
|
255
|
+
|
|
256
|
+
"""
|
|
257
|
+
file = Path(path)
|
|
258
|
+
return cls.model_validate_json(file.read_text())
|
ratapi/eventManager.dll
ADDED
|
Binary file
|
ratapi/events.py
ADDED
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Hooks for connecting to run callback events."""
|
|
2
|
+
|
|
3
|
+
import os
|
|
4
|
+
from collections.abc import Callable
|
|
5
|
+
|
|
6
|
+
from ratapi.rat_core import EventBridge, EventTypes, PlotEventData, ProgressEventData
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
def notify(event_type: EventTypes, data: str | PlotEventData | ProgressEventData) -> None:
|
|
10
|
+
"""Call registered callbacks with data when event type has been triggered.
|
|
11
|
+
|
|
12
|
+
Parameters
|
|
13
|
+
----------
|
|
14
|
+
event_type : EventTypes
|
|
15
|
+
The event type that was triggered.
|
|
16
|
+
data : str or PlotEventData or ProgressEventData
|
|
17
|
+
The data sent by the event. The message event data is a string.
|
|
18
|
+
|
|
19
|
+
"""
|
|
20
|
+
callbacks = __event_callbacks[event_type]
|
|
21
|
+
for callback in callbacks:
|
|
22
|
+
callback(data)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def get_event_callback(event_type: EventTypes) -> list[Callable[[str | PlotEventData | ProgressEventData], None]]:
|
|
26
|
+
"""Return all callbacks registered for the given event type.
|
|
27
|
+
|
|
28
|
+
Parameters
|
|
29
|
+
----------
|
|
30
|
+
event_type : EventTypes
|
|
31
|
+
The event type.
|
|
32
|
+
|
|
33
|
+
Returns
|
|
34
|
+
-------
|
|
35
|
+
callback : Callable[[Union[str, PlotEventData, ProgressEventData]], None]
|
|
36
|
+
The callback for the event type.
|
|
37
|
+
|
|
38
|
+
"""
|
|
39
|
+
return list(__event_callbacks[event_type])
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def register(event_type: EventTypes, callback: Callable[[str | PlotEventData | ProgressEventData], None]) -> None:
|
|
43
|
+
"""Register a new callback for the event type.
|
|
44
|
+
|
|
45
|
+
Parameters
|
|
46
|
+
----------
|
|
47
|
+
event_type : EventTypes
|
|
48
|
+
The event type to register.
|
|
49
|
+
callback : Callable[[Union[str, PlotEventData, ProgressEventData]], None]
|
|
50
|
+
The callback for when the event is triggered.
|
|
51
|
+
|
|
52
|
+
"""
|
|
53
|
+
if not isinstance(event_type, EventTypes):
|
|
54
|
+
raise ValueError("event_type must be a events.EventTypes enum")
|
|
55
|
+
|
|
56
|
+
if len(__event_callbacks[event_type]) == 0:
|
|
57
|
+
__event_impl.register(event_type)
|
|
58
|
+
__event_callbacks[event_type].add(callback)
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def clear(key=None, callback=None) -> None:
|
|
62
|
+
"""Clear all event callbacks or specific callback.
|
|
63
|
+
|
|
64
|
+
Parameters
|
|
65
|
+
----------
|
|
66
|
+
key : EventTypes, optional
|
|
67
|
+
The event type of the callback to clear if given.
|
|
68
|
+
callback : Callable[[Union[str, PlotEventData, ProgressEventData]], None], optional
|
|
69
|
+
A callback for an event which will be cleared if given.
|
|
70
|
+
|
|
71
|
+
"""
|
|
72
|
+
if key is None and callback is None:
|
|
73
|
+
for key in __event_callbacks:
|
|
74
|
+
__event_callbacks[key] = set()
|
|
75
|
+
elif key is not None and callback is not None:
|
|
76
|
+
__event_callbacks[key].remove(callback)
|
|
77
|
+
|
|
78
|
+
for value in __event_callbacks.values():
|
|
79
|
+
if value:
|
|
80
|
+
break
|
|
81
|
+
else:
|
|
82
|
+
__event_impl.clear()
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
dir_path = os.path.dirname(os.path.realpath(__file__))
|
|
86
|
+
os.environ["RAT_PATH"] = os.path.join(dir_path, "")
|
|
87
|
+
__event_impl = EventBridge(notify)
|
|
88
|
+
__event_callbacks = {EventTypes.Message: set(), EventTypes.Plot: set(), EventTypes.Progress: set()}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
"""Usage examples for the Python RAT API."""
|
|
2
|
+
|
|
3
|
+
from ratapi.examples.absorption.absorption import absorption
|
|
4
|
+
from ratapi.examples.convert_rascal_project.convert_rascal import convert_rascal
|
|
5
|
+
from ratapi.examples.domains.domains_custom_layers import domains_custom_layers
|
|
6
|
+
from ratapi.examples.domains.domains_custom_XY import domains_custom_XY
|
|
7
|
+
from ratapi.examples.domains.domains_standard_layers import domains_standard_layers
|
|
8
|
+
from ratapi.examples.normal_reflectivity.DSPC_custom_layers import DSPC_custom_layers
|
|
9
|
+
from ratapi.examples.normal_reflectivity.DSPC_custom_XY import DSPC_custom_XY
|
|
10
|
+
from ratapi.examples.normal_reflectivity.DSPC_data_background import DSPC_data_background
|
|
11
|
+
from ratapi.examples.normal_reflectivity.DSPC_function_background import DSPC_function_background
|
|
12
|
+
from ratapi.examples.normal_reflectivity.DSPC_standard_layers import DSPC_standard_layers
|
|
13
|
+
|
|
14
|
+
__all__ = [
|
|
15
|
+
"absorption",
|
|
16
|
+
"domains_custom_layers",
|
|
17
|
+
"domains_custom_XY",
|
|
18
|
+
"domains_standard_layers",
|
|
19
|
+
"DSPC_custom_layers",
|
|
20
|
+
"DSPC_custom_XY",
|
|
21
|
+
"DSPC_standard_layers",
|
|
22
|
+
"DSPC_data_background",
|
|
23
|
+
"DSPC_function_background",
|
|
24
|
+
"convert_rascal",
|
|
25
|
+
]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
"""An example of using absorption in a RAT project."""
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""An example for using absorption in RAT."""
|
|
2
|
+
|
|
3
|
+
import pathlib
|
|
4
|
+
|
|
5
|
+
import numpy as np
|
|
6
|
+
|
|
7
|
+
import ratapi as RAT
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def absorption():
|
|
11
|
+
"""Run a custom layers model including absorption.
|
|
12
|
+
|
|
13
|
+
RAT allows the use of an imaginary, as well as real part of the SLD.
|
|
14
|
+
The effect of this is usually seen below the critical edge, and must sometimes be accounted for.
|
|
15
|
+
|
|
16
|
+
This is an example of a Custom Layers project using absorption. used here is Custom Layers.
|
|
17
|
+
It analyses a bilayer sample on a permalloy / gold substrate,
|
|
18
|
+
measured using polarised neutrons, against D2O and H2O, leading to 4 contrasts in total.
|
|
19
|
+
Absorption (i.e. imaginary SLD) is defined for Gold and the Permalloy,
|
|
20
|
+
to account for non-flat data below the critical edge.
|
|
21
|
+
"""
|
|
22
|
+
problem = RAT.Project(
|
|
23
|
+
name="Absorption example",
|
|
24
|
+
calculation="normal",
|
|
25
|
+
model="custom layers",
|
|
26
|
+
geometry="substrate/liquid",
|
|
27
|
+
absorption=True,
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
# Add the required parameters (substrate roughness is already there by default)
|
|
31
|
+
problem.parameters.append(name="Alloy Thickness", min=100.0, value=135.6, max=200.0, fit=True)
|
|
32
|
+
problem.parameters.append(name="Alloy SLD up", min=6.0e-6, value=9.87e-6, max=1.2e-5, fit=True)
|
|
33
|
+
problem.parameters.append(name="Alloy SLD imaginary up", min=1.0e-9, value=4.87e-8, max=1.0e-7, fit=True)
|
|
34
|
+
problem.parameters.append(name="Alloy SLD down", min=6.0e-6, value=7.05e-6, max=1.3e-5, fit=True)
|
|
35
|
+
problem.parameters.append(name="Alloy SLD imaginary down", min=1.0e-9, value=4.87e-8, max=1.0e-7, fit=True)
|
|
36
|
+
problem.parameters.append(name="Alloy Roughness", min=2.0, value=5.71, max=10.0, fit=True)
|
|
37
|
+
problem.parameters.append(name="Gold Thickness", min=100.0, value=154.7, max=200.0, fit=True)
|
|
38
|
+
problem.parameters.append(name="Gold Roughness", min=0.1, value=5.42, max=10.0, fit=True)
|
|
39
|
+
problem.parameters.append(name="Gold SLD", min=4.0e-6, value=4.49e-6, max=5.0e-6, fit=True)
|
|
40
|
+
problem.parameters.append(name="Gold SLD imaginary", min=1.0e-9, value=4.20e-8, max=1.0e-7, fit=True)
|
|
41
|
+
|
|
42
|
+
problem.parameters.append(name="Thiol APM", min=40.0, value=56.27, max=100.0, fit=True)
|
|
43
|
+
problem.parameters.append(name="Thiol Head Hydration", min=20.0, value=30.0, max=50.0, fit=True)
|
|
44
|
+
problem.parameters.append(name="Thiol Coverage", min=0.5, value=0.9, max=1.0, fit=True)
|
|
45
|
+
|
|
46
|
+
problem.parameters.append(name="CW Thickness", min=1.0, value=12.87, max=25.0, fit=True)
|
|
47
|
+
problem.parameters.append(name="Bilayer APM", min=48.0, value=65.86, max=90.0, fit=True)
|
|
48
|
+
problem.parameters.append(name="Bilayer Head Hydration", min=20.0, value=30.0, max=50.0, fit=True)
|
|
49
|
+
problem.parameters.append(name="Bilayer Roughness", min=1.0, value=3.87, max=10.0, fit=True)
|
|
50
|
+
problem.parameters.append(name="Bilayer Coverage", min=0.5, value=0.94, max=1.0, fit=True)
|
|
51
|
+
|
|
52
|
+
# Change the existing Bulk In parameter to be Silicon
|
|
53
|
+
problem.bulk_in.set_fields(0, name="Silicon", min=2.0e-6, value=2.073e-6, max=2.1e-6)
|
|
54
|
+
|
|
55
|
+
# We need 2 bulk outs - D2O and H2O
|
|
56
|
+
problem.bulk_out.set_fields(0, name="D2O", min=5.8e-06, value=6.21e-06, max=6.35e-06, fit=True)
|
|
57
|
+
problem.bulk_out.append(name="H2O", min=-5.6e-07, value=-3.15e-07, max=0.0, fit=True)
|
|
58
|
+
|
|
59
|
+
# Use a different scalefactor for each dataset
|
|
60
|
+
del problem.scalefactors[0]
|
|
61
|
+
problem.scalefactors.append(name="Scalefactor 1", min=0.5, value=1, max=1.5, fit=True)
|
|
62
|
+
problem.scalefactors.append(name="Scalefactor 2", min=0.5, value=1, max=1.5, fit=True)
|
|
63
|
+
problem.scalefactors.append(name="Scalefactor 3", min=0.5, value=1, max=1.5, fit=True)
|
|
64
|
+
problem.scalefactors.append(name="Scalefactor 4", min=0.5, value=1, max=1.5, fit=True)
|
|
65
|
+
|
|
66
|
+
# Similarly, use an individual background for each dataset
|
|
67
|
+
del problem.backgrounds[0]
|
|
68
|
+
del problem.background_parameters[0]
|
|
69
|
+
|
|
70
|
+
problem.background_parameters.append(
|
|
71
|
+
name="Background parameter 1", min=5.0e-08, value=7.88e-06, max=9.0e-05, fit=True
|
|
72
|
+
)
|
|
73
|
+
problem.background_parameters.append(
|
|
74
|
+
name="Background parameter 2", min=1.0e-08, value=5.46e-06, max=9.0e-05, fit=True
|
|
75
|
+
)
|
|
76
|
+
problem.background_parameters.append(
|
|
77
|
+
name="Background parameter 3", min=1.0e-06, value=9.01e-06, max=9.0e-05, fit=True
|
|
78
|
+
)
|
|
79
|
+
problem.background_parameters.append(
|
|
80
|
+
name="Background parameter 4", min=1.0e-06, value=5.61e-06, max=9.0e-05, fit=True
|
|
81
|
+
)
|
|
82
|
+
|
|
83
|
+
problem.backgrounds.append(name="Background 1", type="constant", source="Background parameter 1")
|
|
84
|
+
problem.backgrounds.append(name="Background 2", type="constant", source="Background parameter 2")
|
|
85
|
+
problem.backgrounds.append(name="Background 3", type="constant", source="Background parameter 3")
|
|
86
|
+
problem.backgrounds.append(name="Background 4", type="constant", source="Background parameter 4")
|
|
87
|
+
|
|
88
|
+
# Make the resolution fittable
|
|
89
|
+
problem.resolution_parameters.set_fields(0, fit=True)
|
|
90
|
+
|
|
91
|
+
# Now add the data we need
|
|
92
|
+
data_path = pathlib.Path(__file__).parents[1] / "data"
|
|
93
|
+
|
|
94
|
+
data_1 = np.loadtxt(data_path / "D2O_spin_down.dat")
|
|
95
|
+
problem.data.append(name="D2O_dn", data=data_1)
|
|
96
|
+
|
|
97
|
+
data_2 = np.loadtxt(data_path / "D2O_spin_up.dat")
|
|
98
|
+
problem.data.append(name="D2O_up", data=data_2)
|
|
99
|
+
|
|
100
|
+
data_3 = np.loadtxt(data_path / "H2O_spin_down.dat")
|
|
101
|
+
problem.data.append(name="H2O_dn", data=data_3)
|
|
102
|
+
|
|
103
|
+
data_4 = np.loadtxt(data_path / "H2O_spin_up.dat")
|
|
104
|
+
problem.data.append(name="H2O_up", data=data_4)
|
|
105
|
+
|
|
106
|
+
# Add the custom file
|
|
107
|
+
problem.custom_files.append(
|
|
108
|
+
name="DPPC absorption",
|
|
109
|
+
filename="volume_thiol_bilayer.py",
|
|
110
|
+
language="python",
|
|
111
|
+
path=pathlib.Path(__file__).parent,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
# Finally add the contrasts
|
|
115
|
+
problem.contrasts.append(
|
|
116
|
+
name="D2O Down",
|
|
117
|
+
data="D2O_dn",
|
|
118
|
+
background="Background 1",
|
|
119
|
+
bulk_in="Silicon",
|
|
120
|
+
bulk_out="D2O",
|
|
121
|
+
scalefactor="Scalefactor 1",
|
|
122
|
+
resolution="Resolution 1",
|
|
123
|
+
resample=True,
|
|
124
|
+
model=["DPPC absorption"],
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
problem.contrasts.append(
|
|
128
|
+
name="D2O Up",
|
|
129
|
+
data="D2O_up",
|
|
130
|
+
background="Background 2",
|
|
131
|
+
bulk_in="Silicon",
|
|
132
|
+
bulk_out="D2O",
|
|
133
|
+
scalefactor="Scalefactor 2",
|
|
134
|
+
resolution="Resolution 1",
|
|
135
|
+
resample=True,
|
|
136
|
+
model=["DPPC absorption"],
|
|
137
|
+
)
|
|
138
|
+
|
|
139
|
+
problem.contrasts.append(
|
|
140
|
+
name="H2O Down",
|
|
141
|
+
data="H2O_dn",
|
|
142
|
+
background="Background 3",
|
|
143
|
+
bulk_in="Silicon",
|
|
144
|
+
bulk_out="H2O",
|
|
145
|
+
scalefactor="Scalefactor 3",
|
|
146
|
+
resolution="Resolution 1",
|
|
147
|
+
resample=True,
|
|
148
|
+
model=["DPPC absorption"],
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
problem.contrasts.append(
|
|
152
|
+
name="H2O Up",
|
|
153
|
+
data="H2O_up",
|
|
154
|
+
background="Background 4",
|
|
155
|
+
bulk_in="Silicon",
|
|
156
|
+
bulk_out="H2O",
|
|
157
|
+
scalefactor="Scalefactor 4",
|
|
158
|
+
resolution="Resolution 1",
|
|
159
|
+
resample=True,
|
|
160
|
+
model=["DPPC absorption"],
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
# Now make a controls block and run the code
|
|
164
|
+
controls = RAT.Controls(parallel="contrasts", resampleNPoints=150)
|
|
165
|
+
problem, results = RAT.run(problem, controls)
|
|
166
|
+
|
|
167
|
+
return problem, results
|
|
168
|
+
|
|
169
|
+
|
|
170
|
+
if __name__ == "__main__":
|
|
171
|
+
problem, results = absorption()
|
|
172
|
+
RAT.plotting.plot_ref_sld(problem, results, True)
|