Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .github/workflows/run_tests.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,5 +53,5 @@ jobs:
- name: Install and Test with pytest
run: |
export PATH="$pythonLocation:$PATH"
python -m pip install -e .[Dev]
python -m pip install -e .[Dev,Orso]
pytest tests/ --cov=RATapi --cov-report=term
4 changes: 2 additions & 2 deletions RATapi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,6 @@
from RATapi.controls import Controls
from RATapi.project import Project
from RATapi.run import run
from RATapi.utils import convert, plotting
from RATapi.utils import convert, orso, plotting

__all__ = ["examples", "models", "events", "ClassList", "Controls", "Project", "run", "plotting", "convert"]
__all__ = ["examples", "models", "events", "ClassList", "Controls", "Project", "run", "plotting", "convert", "orso"]
13 changes: 13 additions & 0 deletions RATapi/classlist.py
Original file line number Diff line number Diff line change
Expand Up @@ -296,6 +296,19 @@ def extend(self, other: Sequence[T]) -> None:
self._check_unique_name_fields(other)
self.data.extend(other)

def union(self, other: Sequence[T]) -> None:
"""Extend the ClassList by a sequence, ignoring input items with names that already exist."""
if other and not (isinstance(other, Sequence) and not isinstance(other, str)):
other = [other]

self.extend(
[
item
for item in other
if hasattr(item, self.name_field) and getattr(item, self.name_field) not in self.get_names()
]
)

def set_fields(self, index: Union[int, slice, str, T], **kwargs) -> None:
"""Assign the values of an existing object's attributes using keyword arguments."""
self._validate_name_field(kwargs)
Expand Down
441 changes: 441 additions & 0 deletions RATapi/examples/data/c_PLP0011859_q.ort

Large diffs are not rendered by default.

188 changes: 188 additions & 0 deletions RATapi/examples/orso_integration/orso_integration.ipynb
Original file line number Diff line number Diff line change
@@ -0,0 +1,188 @@
{
"cells": [
{
"cell_type": "markdown",
"metadata": {},
"source": [
"# ``orsopy`` Integration"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"``python-RAT`` contains some integration with ``orsopy``, allowing for convenient interaction with the ``.ort`` file format. This integration is available through the `RATapi.utils.orso` submodule."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import RATapi.utils.orso"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Creating models from the ORSO model description language\n",
"\n",
"The [ORSO model description format](https://www.reflectometry.org/advanced_and_expert_level/file_format/simple_model) allows the description of a standard slab model as a one-line string, provided that all the layer materials are defined [in the ORSO SLD database](https://slddb.esss.dk/slddb/).\n",
"\n",
"The function `RATapi.utils.orso.orso_model_to_rat` function can read a model and return an `ORSOSample` dataclass, which gives bulk in and bulk out parameters for the model, a list of all layers defined in the model, and all the parameters needed to define those layers as RAT models. \n",
"\n",
"**Note:** the ORSO format gives the thicknesses of materials in *nanometres*. When we convert them to RAT parameters, the units will be converted to Angstroms.\n",
"\n",
"For example, the string `air | Ni 100 | SiO2 0.5 | Si` describes a 1000 angstrom nickel film backed by a 5 angstrom silicon oxide layer. The bulk-in and bulk-out are air and silicon respectively. The roughnesses and SLDs will be calculated or taken from the ORSO SLD database."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"# create the RAT parameters and layers from this model\n",
"sample = RATapi.utils.orso.orso_model_to_rat(\"air | Ni 100 | SiO2 0.5 | Si\")\n",
"print(sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"You can also set `absorption=True` and the model will account for absorption. For example if we change the nickel film for a boron carbide film and want to account for its relatively high absorption, we can add it to the output:"
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample = RATapi.utils.orso.orso_model_to_rat(\"vacuum | B4C 100 | SiO2 0.5 | Si\", absorption=True)\n",
"print(sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"Finally, ORSO supports defining repeated layers using parentheses. For example, if we had a polarising multilayer of 5 repetitions of 70 angstrom silicon and 70 angstrom iron, we could represent it as `air | 5 ( Si 7 | Fe 7 ) | Si`.\n",
"\n",
"RAT will only create the number of layers and parameters necessary, but the `ORSOSample` object's `model` attribute will give a list of layer names with the structure of the model preserved, which can be given as the layer model for a Contrast."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"sample = RATapi.utils.orso.orso_model_to_rat(\"air | 5 ( Si 7 | Fe 7 ) | Si\")\n",
"print(sample)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"## Reading in data and models from .ort files"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"RAT can also load both data and model information from an .ort file. This is done through the `ORSOProject` object, which takes a file path and can also optionally account for absorption.\n",
"\n",
"The example data file we use here is example data for an unknown film on deposited on silicon."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"import pathlib\n",
"data_path = pathlib.Path(\"../data\")\n",
"\n",
"orso_data = RATapi.utils.orso.ORSOProject(data_path / \"c_PLP0011859_q.ort\")\n",
"print(orso_data)"
]
},
{
"cell_type": "markdown",
"metadata": {},
"source": [
"The `ORSOProject` object contains two lists: `ORSOProject.data` and `ORSOProject.samples`. The former is a list of Data objects with each dataset defined in the file, and the latter is a list of `ORSOSample` objects (like above) with model information. Note that if the .ort file does not define a model for a dataset, that index of `ORSOProject.samples` will be None.\n",
"\n",
"It's then easy to access this data to create a RAT `Project` that represents our data."
]
},
{
"cell_type": "code",
"execution_count": null,
"metadata": {},
"outputs": [],
"source": [
"from RATapi.models import Background, Contrast, Parameter, Resolution\n",
"\n",
"dataset = orso_data.data[0]\n",
"sample = orso_data.samples[0]\n",
"\n",
"project = RATapi.Project(\n",
" name = \"Example Project\",\n",
" geometry = \"substrate/liquid\",\n",
" parameters = sample.parameters,\n",
" bulk_in = [sample.bulk_in],\n",
" bulk_out = [sample.bulk_out],\n",
" scalefactors = [Parameter(name=\"Scalefactor\", min=0, value=0.34, max=1.5)],\n",
" background_parameters = [Parameter(name=\"Background Parameter\", min=0, value=2e-6, max=1)],\n",
" backgrounds = [Background(name=\"Background\", type=\"constant\", source=\"Background Parameter\")],\n",
" resolutions = [Resolution(name=\"Data Resolution\", type=\"data\")],\n",
" data = [dataset],\n",
" layers = sample.layers,\n",
" contrasts = [Contrast(\n",
" name = \"prist4\",\n",
" data = dataset.name,\n",
" background = \"Background\",\n",
" bulk_in = sample.bulk_in.name,\n",
" bulk_out = sample.bulk_out.name,\n",
" scalefactor = \"Scalefactor\",\n",
" resolution = \"Data Resolution\",\n",
" model = sample.model,\n",
" )]\n",
")\n",
"\n",
"controls = RATapi.Controls()\n",
"project, results = RATapi.run(project, controls)\n",
"RATapi.plotting.plot_ref_sld(project, results)"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
Loading