easyreflectometry 1.3.3__tar.gz → 1.4.0__tar.gz
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.
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/PKG-INFO +24 -25
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/pyproject.toml +24 -26
- easyreflectometry-1.4.0/src/easyreflectometry/__version__.py +1 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/bornagain/calculator.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/calculator_base.py +3 -3
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/factory.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/refl1d/wrapper.py +19 -6
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/data/__init__.py +6 -4
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/data/data_store.py +7 -6
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/data/measurement.py +49 -9
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/fitting.py +2 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/model/__init__.py +7 -5
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/model/model.py +7 -5
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/model/model_collection.py +5 -3
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/model/resolution_functions.py +60 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/project.py +42 -7
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/__init__.py +14 -14
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/assemblies/base_assembly.py +13 -42
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/assemblies/repeating_multilayer.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/assemblies/surfactant_layer.py +39 -58
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/base_core.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/collections/base_collection.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/elements/layers/layer.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/elements/layers/layer_area_per_molecule.py +17 -23
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/elements/materials/material.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/elements/materials/material_density.py +7 -10
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/elements/materials/material_mixture.py +7 -20
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/elements/materials/material_solvated.py +1 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/special/parsing.py +3 -6
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/summary/summary.py +2 -2
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/utils.py +2 -2
- easyreflectometry-1.3.3/src/easyreflectometry/__version__.py +0 -1
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/.gitignore +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/AUTHORS.rst +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/LICENSE +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/README.md +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/__init__.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/__init__.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/bornagain/wrapper.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/refl1d/calculator.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/refnx/calculator.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/refnx/wrapper.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/wrapper_base.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/main.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/plot.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/assemblies/gradient_layer.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/assemblies/multilayer.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/collections/layer_collection.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/collections/material_collection.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/sample/collections/sample.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/special/calculations.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/summary/__init__.py +0 -0
- {easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/summary/html_templates.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.1
|
|
2
2
|
Name: easyreflectometry
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.4.0
|
|
4
4
|
Summary: A reflectometry python package built on the EasyScience framework.
|
|
5
5
|
Project-URL: homepage, https://docs.easyreflectometry.org
|
|
6
6
|
Project-URL: documentation, https://docs.easyreflectometry.org
|
|
@@ -41,34 +41,33 @@ Classifier: License :: OSI Approved :: BSD License
|
|
|
41
41
|
Classifier: Operating System :: OS Independent
|
|
42
42
|
Classifier: Programming Language :: Python :: 3
|
|
43
43
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
44
|
-
Classifier: Programming Language :: Python :: 3.10
|
|
45
44
|
Classifier: Programming Language :: Python :: 3.11
|
|
46
45
|
Classifier: Programming Language :: Python :: 3.12
|
|
47
46
|
Classifier: Topic :: Scientific/Engineering
|
|
48
|
-
Requires-Python: <3.13,>=3.
|
|
49
|
-
Requires-Dist: bumps
|
|
50
|
-
Requires-Dist: easyscience
|
|
51
|
-
Requires-Dist: orsopy
|
|
52
|
-
Requires-Dist: refl1d
|
|
53
|
-
Requires-Dist: refnx
|
|
54
|
-
Requires-Dist: scipp
|
|
55
|
-
Requires-Dist: xhtml2pdf
|
|
47
|
+
Requires-Python: <3.13,>=3.11
|
|
48
|
+
Requires-Dist: bumps
|
|
49
|
+
Requires-Dist: easyscience
|
|
50
|
+
Requires-Dist: orsopy
|
|
51
|
+
Requires-Dist: refl1d>=1.0.0rc0
|
|
52
|
+
Requires-Dist: refnx
|
|
53
|
+
Requires-Dist: scipp
|
|
54
|
+
Requires-Dist: xhtml2pdf
|
|
56
55
|
Provides-Extra: dev
|
|
57
|
-
Requires-Dist: build
|
|
58
|
-
Requires-Dist: codecov
|
|
59
|
-
Requires-Dist: coverage
|
|
60
|
-
Requires-Dist: coveralls
|
|
61
|
-
Requires-Dist: flake8
|
|
62
|
-
Requires-Dist: ipykernel
|
|
63
|
-
Requires-Dist: jupyter
|
|
64
|
-
Requires-Dist: jupyterlab
|
|
65
|
-
Requires-Dist: plopp
|
|
66
|
-
Requires-Dist: pooch
|
|
67
|
-
Requires-Dist: pytest
|
|
68
|
-
Requires-Dist: pytest
|
|
69
|
-
Requires-Dist: ruff
|
|
70
|
-
Requires-Dist: toml
|
|
71
|
-
Requires-Dist: yapf
|
|
56
|
+
Requires-Dist: build; extra == 'dev'
|
|
57
|
+
Requires-Dist: codecov; extra == 'dev'
|
|
58
|
+
Requires-Dist: coverage; extra == 'dev'
|
|
59
|
+
Requires-Dist: coveralls; extra == 'dev'
|
|
60
|
+
Requires-Dist: flake8; extra == 'dev'
|
|
61
|
+
Requires-Dist: ipykernel; extra == 'dev'
|
|
62
|
+
Requires-Dist: jupyter; extra == 'dev'
|
|
63
|
+
Requires-Dist: jupyterlab; extra == 'dev'
|
|
64
|
+
Requires-Dist: plopp; extra == 'dev'
|
|
65
|
+
Requires-Dist: pooch; extra == 'dev'
|
|
66
|
+
Requires-Dist: pytest; extra == 'dev'
|
|
67
|
+
Requires-Dist: pytest-cov; extra == 'dev'
|
|
68
|
+
Requires-Dist: ruff; extra == 'dev'
|
|
69
|
+
Requires-Dist: toml; extra == 'dev'
|
|
70
|
+
Requires-Dist: yapf; extra == 'dev'
|
|
72
71
|
Provides-Extra: docs
|
|
73
72
|
Requires-Dist: myst-parser; extra == 'docs'
|
|
74
73
|
Requires-Dist: nbsphinx; extra == 'docs'
|
|
@@ -21,41 +21,40 @@ classifiers = [
|
|
|
21
21
|
"Topic :: Scientific/Engineering",
|
|
22
22
|
"Programming Language :: Python :: 3",
|
|
23
23
|
"Programming Language :: Python :: 3 :: Only",
|
|
24
|
-
"Programming Language :: Python :: 3.10",
|
|
25
24
|
"Programming Language :: Python :: 3.11",
|
|
26
25
|
"Programming Language :: Python :: 3.12",
|
|
27
26
|
"Development Status :: 3 - Alpha"
|
|
28
27
|
]
|
|
29
28
|
|
|
30
|
-
requires-python = ">=3.
|
|
29
|
+
requires-python = ">=3.11,<3.13"
|
|
31
30
|
|
|
32
31
|
dependencies = [
|
|
33
|
-
"easyscience
|
|
34
|
-
"scipp
|
|
35
|
-
"refnx
|
|
36
|
-
"refl1d
|
|
37
|
-
"orsopy
|
|
38
|
-
"xhtml2pdf
|
|
39
|
-
"bumps
|
|
32
|
+
"easyscience",
|
|
33
|
+
"scipp",
|
|
34
|
+
"refnx",
|
|
35
|
+
"refl1d>=1.0.0rc0",
|
|
36
|
+
"orsopy",
|
|
37
|
+
"xhtml2pdf",
|
|
38
|
+
"bumps",
|
|
40
39
|
]
|
|
41
40
|
|
|
42
41
|
[project.optional-dependencies]
|
|
43
42
|
dev = [
|
|
44
|
-
"build
|
|
45
|
-
"codecov
|
|
46
|
-
"coverage
|
|
47
|
-
"coveralls
|
|
48
|
-
"flake8
|
|
49
|
-
"ipykernel
|
|
50
|
-
"jupyter
|
|
51
|
-
"jupyterlab
|
|
52
|
-
"plopp
|
|
53
|
-
"pooch
|
|
54
|
-
"pytest
|
|
55
|
-
"pytest-cov
|
|
56
|
-
"ruff
|
|
57
|
-
"toml
|
|
58
|
-
"yapf
|
|
43
|
+
"build",
|
|
44
|
+
"codecov",
|
|
45
|
+
"coverage",
|
|
46
|
+
"coveralls",
|
|
47
|
+
"flake8",
|
|
48
|
+
"ipykernel",
|
|
49
|
+
"jupyter",
|
|
50
|
+
"jupyterlab",
|
|
51
|
+
"plopp",
|
|
52
|
+
"pooch",
|
|
53
|
+
"pytest",
|
|
54
|
+
"pytest-cov",
|
|
55
|
+
"ruff",
|
|
56
|
+
"toml",
|
|
57
|
+
"yapf",
|
|
59
58
|
]
|
|
60
59
|
|
|
61
60
|
docs = [
|
|
@@ -134,10 +133,9 @@ force-single-line = true
|
|
|
134
133
|
legacy_tox_ini = """
|
|
135
134
|
[tox]
|
|
136
135
|
isolated_build = True
|
|
137
|
-
envlist = py{3.
|
|
136
|
+
envlist = py{3.11,3.12}
|
|
138
137
|
[gh-actions]
|
|
139
138
|
python =
|
|
140
|
-
3.10: py310
|
|
141
139
|
3.11: py311
|
|
142
140
|
3.12: py312
|
|
143
141
|
[gh-actions:env]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
__version__ = '1.4.0'
|
|
@@ -4,8 +4,8 @@ from abc import ABCMeta
|
|
|
4
4
|
from typing import Callable
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
|
-
from easyscience.
|
|
8
|
-
from easyscience.
|
|
7
|
+
from easyscience.fitting.calculators.interface_factory import ItemContainer
|
|
8
|
+
from easyscience.io import SerializerComponent
|
|
9
9
|
|
|
10
10
|
from easyreflectometry.model import Model
|
|
11
11
|
from easyreflectometry.sample import BaseAssembly
|
|
@@ -17,7 +17,7 @@ from easyreflectometry.sample import Multilayer
|
|
|
17
17
|
from .wrapper_base import WrapperBase
|
|
18
18
|
|
|
19
19
|
|
|
20
|
-
class CalculatorBase(
|
|
20
|
+
class CalculatorBase(SerializerComponent, metaclass=ABCMeta):
|
|
21
21
|
"""
|
|
22
22
|
This class is a template and defines all properties that a calculator should have.
|
|
23
23
|
"""
|
{easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/calculators/factory.py
RENAMED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
__author__ = 'github.com/wardsimon'
|
|
2
2
|
from typing import Callable
|
|
3
3
|
|
|
4
|
-
from easyscience.
|
|
4
|
+
from easyscience.fitting.calculators.interface_factory import InterfaceFactoryTemplate
|
|
5
5
|
|
|
6
6
|
from easyreflectometry.calculators import CalculatorBase
|
|
7
7
|
|
|
@@ -42,9 +42,7 @@ class Refl1dWrapper(WrapperBase):
|
|
|
42
42
|
|
|
43
43
|
:param name: The name of the item
|
|
44
44
|
"""
|
|
45
|
-
self.storage['item'][name] = Repeat(
|
|
46
|
-
names.Stack(names.Slab(names.SLD(), thickness=0, interface=0)), name=str(name)
|
|
47
|
-
)
|
|
45
|
+
self.storage['item'][name] = Repeat(names.Stack(names.Slab(names.SLD(), thickness=0, interface=0)), name=str(name))
|
|
48
46
|
del self.storage['item'][name].stack[0]
|
|
49
47
|
|
|
50
48
|
def update_layer(self, name: str, **kwargs):
|
|
@@ -66,7 +64,9 @@ class Refl1dWrapper(WrapperBase):
|
|
|
66
64
|
:param key: The given value keys
|
|
67
65
|
"""
|
|
68
66
|
if key in ['magnetism_rhoM', 'magnetism_thetaM']:
|
|
69
|
-
return getattr(
|
|
67
|
+
return getattr(
|
|
68
|
+
self.storage['layer'][name].magnetism, key.split('_')[-1]
|
|
69
|
+
).value # TODO: check if we want to return the raw value or the full Parameter # noqa: E501
|
|
70
70
|
return super().get_layer_value(name, key)
|
|
71
71
|
|
|
72
72
|
def create_model(self, name: str):
|
|
@@ -231,6 +231,7 @@ def _get_probe(
|
|
|
231
231
|
model_name: str,
|
|
232
232
|
storage: dict,
|
|
233
233
|
oversampling_factor: int = 1,
|
|
234
|
+
magnetism: bool = False,
|
|
234
235
|
) -> names.QProbe:
|
|
235
236
|
probe = names.QProbe(
|
|
236
237
|
Q=q_array,
|
|
@@ -238,6 +239,12 @@ def _get_probe(
|
|
|
238
239
|
intensity=storage['model'][model_name]['scale'],
|
|
239
240
|
background=storage['model'][model_name]['bkg'],
|
|
240
241
|
)
|
|
242
|
+
|
|
243
|
+
# Add theta_offset attribute if magnetism is enabled
|
|
244
|
+
# This is required for PolarizedQProbe to work correctly
|
|
245
|
+
if magnetism:
|
|
246
|
+
probe.theta_offset = names.Parameter.default(0, name='theta_offset')
|
|
247
|
+
|
|
241
248
|
if oversampling_factor > 1:
|
|
242
249
|
probe.calc_Qo = _get_oversampling_q(q_array, dq_array, oversampling_factor)
|
|
243
250
|
return probe
|
|
@@ -250,7 +257,7 @@ def _get_polarized_probe(
|
|
|
250
257
|
storage: dict,
|
|
251
258
|
oversampling_factor: int = 1,
|
|
252
259
|
all_polarizations: bool = False,
|
|
253
|
-
) -> names.
|
|
260
|
+
) -> names.PolarizedNeutronQProbe:
|
|
254
261
|
four_probes = []
|
|
255
262
|
for i in range(4):
|
|
256
263
|
if i == 0 or all_polarizations:
|
|
@@ -260,11 +267,17 @@ def _get_polarized_probe(
|
|
|
260
267
|
model_name=model_name,
|
|
261
268
|
storage=storage,
|
|
262
269
|
oversampling_factor=oversampling_factor,
|
|
270
|
+
magnetism=True, # Enable magnetism for polarized probes
|
|
263
271
|
)
|
|
264
272
|
else:
|
|
265
273
|
probe = None
|
|
266
274
|
four_probes.append(probe)
|
|
267
|
-
|
|
275
|
+
|
|
276
|
+
# Create polarized probe and work around initialization bug
|
|
277
|
+
polarized_probe = names.PolarizedNeutronQProbe.__new__(names.PolarizedNeutronQProbe)
|
|
278
|
+
polarized_probe._union_cache_key = None # Initialize missing attribute
|
|
279
|
+
polarized_probe.__init__(xs=four_probes, name='polarized')
|
|
280
|
+
return polarized_probe
|
|
268
281
|
|
|
269
282
|
|
|
270
283
|
def _build_sample(storage: dict, model_name: str) -> names.Stack:
|
|
@@ -2,10 +2,12 @@ from .data_store import DataSet1D
|
|
|
2
2
|
from .data_store import ProjectData
|
|
3
3
|
from .measurement import load
|
|
4
4
|
from .measurement import load_as_dataset
|
|
5
|
+
from .measurement import merge_datagroups
|
|
5
6
|
|
|
6
7
|
__all__ = [
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
'load',
|
|
9
|
+
'load_as_dataset',
|
|
10
|
+
'merge_datagroups',
|
|
11
|
+
'ProjectData',
|
|
12
|
+
'DataSet1D',
|
|
11
13
|
]
|
{easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/data/data_store.py
RENAMED
|
@@ -6,15 +6,16 @@ from typing import TypeVar
|
|
|
6
6
|
from typing import Union
|
|
7
7
|
|
|
8
8
|
import numpy as np
|
|
9
|
-
from easyscience.
|
|
10
|
-
from easyscience.
|
|
9
|
+
from easyscience.io import SerializerComponent
|
|
10
|
+
from easyscience.io import SerializerDict
|
|
11
11
|
|
|
12
|
+
# from easyscience.utils.io.dict import DictSerializer
|
|
12
13
|
from easyreflectometry.model import Model
|
|
13
14
|
|
|
14
15
|
T = TypeVar('T')
|
|
15
16
|
|
|
16
17
|
|
|
17
|
-
class ProjectData(
|
|
18
|
+
class ProjectData(SerializerComponent):
|
|
18
19
|
def __init__(self, name='DataStore', exp_data=None, sim_data=None):
|
|
19
20
|
self.name = name
|
|
20
21
|
if exp_data is None:
|
|
@@ -25,7 +26,7 @@ class ProjectData(ComponentSerializer):
|
|
|
25
26
|
self.sim_data = sim_data
|
|
26
27
|
|
|
27
28
|
|
|
28
|
-
class DataStore(Sequence,
|
|
29
|
+
class DataStore(Sequence, SerializerComponent):
|
|
29
30
|
def __init__(self, *args, name='DataStore'):
|
|
30
31
|
self.name = name
|
|
31
32
|
self.items = list(args)
|
|
@@ -55,7 +56,7 @@ class DataStore(Sequence, ComponentSerializer):
|
|
|
55
56
|
items = d['items']
|
|
56
57
|
del d['items']
|
|
57
58
|
obj = cls.from_dict(d)
|
|
58
|
-
decoder =
|
|
59
|
+
decoder = SerializerDict()
|
|
59
60
|
obj.items = [decoder.decode(item) for item in items]
|
|
60
61
|
return obj
|
|
61
62
|
|
|
@@ -68,7 +69,7 @@ class DataStore(Sequence, ComponentSerializer):
|
|
|
68
69
|
return [self[idx] for idx in range(len(self)) if self[idx].is_simulation]
|
|
69
70
|
|
|
70
71
|
|
|
71
|
-
class DataSet1D(
|
|
72
|
+
class DataSet1D(SerializerComponent):
|
|
72
73
|
def __init__(
|
|
73
74
|
self,
|
|
74
75
|
name: str = 'Series',
|
{easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/data/measurement.py
RENAMED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
__author__ = 'github.com/arm61'
|
|
2
2
|
|
|
3
|
+
import os
|
|
3
4
|
from typing import TextIO
|
|
4
5
|
from typing import Union
|
|
5
6
|
|
|
@@ -25,11 +26,16 @@ def load(fname: Union[TextIO, str]) -> sc.DataGroup:
|
|
|
25
26
|
def load_as_dataset(fname: Union[TextIO, str]) -> DataSet1D:
|
|
26
27
|
"""Load data from an ORSO .ort file as a DataSet1D."""
|
|
27
28
|
data_group = load(fname)
|
|
29
|
+
basename = os.path.splitext(os.path.basename(fname))[0]
|
|
30
|
+
data_name = 'R_' + basename
|
|
31
|
+
coords_name = 'Qz_' + basename
|
|
32
|
+
coords_name = list(data_group['coords'].keys())[0] if coords_name not in data_group['coords'] else coords_name
|
|
33
|
+
data_name = list(data_group['data'].keys())[0] if data_name not in data_group['data'] else data_name
|
|
28
34
|
return DataSet1D(
|
|
29
|
-
x=data_group['coords'][
|
|
30
|
-
y=data_group['data'][
|
|
31
|
-
ye=data_group['data'][
|
|
32
|
-
xe=data_group['coords'][
|
|
35
|
+
x=data_group['coords'][coords_name].values,
|
|
36
|
+
y=data_group['data'][data_name].values,
|
|
37
|
+
ye=data_group['data'][data_name].variances,
|
|
38
|
+
xe=data_group['coords'][coords_name].variances,
|
|
33
39
|
)
|
|
34
40
|
|
|
35
41
|
|
|
@@ -86,6 +92,8 @@ def _load_txt(fname: Union[TextIO, str]) -> sc.DataGroup:
|
|
|
86
92
|
if ',' in first_line:
|
|
87
93
|
delimiter = ','
|
|
88
94
|
|
|
95
|
+
basename = os.path.splitext(os.path.basename(fname))[0]
|
|
96
|
+
|
|
89
97
|
try:
|
|
90
98
|
# First load only the data to check column count
|
|
91
99
|
data = np.loadtxt(fname, delimiter=delimiter, comments='#')
|
|
@@ -97,7 +105,7 @@ def _load_txt(fname: Union[TextIO, str]) -> sc.DataGroup:
|
|
|
97
105
|
|
|
98
106
|
# Verify minimum column requirement
|
|
99
107
|
if num_columns < 3:
|
|
100
|
-
raise ValueError(f
|
|
108
|
+
raise ValueError(f'File must contain at least 3 columns (found {num_columns})')
|
|
101
109
|
|
|
102
110
|
# Now unpack the data based on column count
|
|
103
111
|
if num_columns >= 4:
|
|
@@ -108,15 +116,47 @@ def _load_txt(fname: Union[TextIO, str]) -> sc.DataGroup:
|
|
|
108
116
|
|
|
109
117
|
except (ValueError, IOError) as error:
|
|
110
118
|
# Re-raise with more descriptive message
|
|
111
|
-
raise ValueError(f
|
|
119
|
+
raise ValueError(f'Failed to load data from {fname}: {str(error)}') from error
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
data_name = 'R_' + basename
|
|
122
|
+
coords_name = 'Qz_' + basename
|
|
123
|
+
data = {data_name: sc.array(dims=[coords_name], values=y, variances=np.square(e))}
|
|
114
124
|
coords = {
|
|
115
|
-
data[
|
|
116
|
-
dims=[
|
|
125
|
+
data[data_name].dims[0]: sc.array(
|
|
126
|
+
dims=[coords_name],
|
|
117
127
|
values=x,
|
|
118
128
|
variances=np.square(xe),
|
|
119
129
|
unit=sc.Unit('1/angstrom'),
|
|
120
130
|
)
|
|
121
131
|
}
|
|
122
132
|
return sc.DataGroup(data=data, coords=coords)
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def merge_datagroups(*data_groups: sc.DataGroup) -> sc.DataGroup:
|
|
136
|
+
"""Merge multiple DataGroups into a single DataGroup."""
|
|
137
|
+
merged_data = {}
|
|
138
|
+
merged_coords = {}
|
|
139
|
+
merged_attrs = {}
|
|
140
|
+
|
|
141
|
+
for group in data_groups:
|
|
142
|
+
for key, value in group['data'].items():
|
|
143
|
+
if key not in merged_data:
|
|
144
|
+
merged_data[key] = value
|
|
145
|
+
else:
|
|
146
|
+
merged_data[key] = sc.concatenate([merged_data[key], value])
|
|
147
|
+
|
|
148
|
+
for key, value in group['coords'].items():
|
|
149
|
+
if key not in merged_coords:
|
|
150
|
+
merged_coords[key] = value
|
|
151
|
+
else:
|
|
152
|
+
merged_coords[key] = sc.concatenate([merged_coords[key], value])
|
|
153
|
+
|
|
154
|
+
if 'attrs' not in group:
|
|
155
|
+
continue
|
|
156
|
+
for key, value in group['attrs'].items():
|
|
157
|
+
if key not in merged_attrs:
|
|
158
|
+
merged_attrs[key] = value
|
|
159
|
+
else:
|
|
160
|
+
merged_attrs[key] = {**merged_attrs[key], **value}
|
|
161
|
+
|
|
162
|
+
return sc.DataGroup(data=merged_data, coords=merged_coords, attrs=merged_attrs)
|
|
@@ -50,7 +50,8 @@ class MultiFitter:
|
|
|
50
50
|
)
|
|
51
51
|
sld_profile = self.easy_science_multi_fitter._fit_objects[i].interface.sld_profile(self._models[i].unique_name)
|
|
52
52
|
new_data[f'SLD_{id}'] = sc.array(dims=[f'z_{id}'], values=sld_profile[1] * 1e-6, unit=sc.Unit('1/angstrom') ** 2)
|
|
53
|
-
|
|
53
|
+
if 'attrs' in new_data:
|
|
54
|
+
new_data['attrs'][f'R_{id}_model'] = {'model': sc.scalar(self._models[i].as_dict())}
|
|
54
55
|
new_data['coords'][f'z_{id}'] = sc.array(
|
|
55
56
|
dims=[f'z_{id}'], values=sld_profile[0], unit=(1 / new_data['coords'][f'Qz_{id}'].unit).unit
|
|
56
57
|
)
|
|
@@ -2,12 +2,14 @@ from .model import Model
|
|
|
2
2
|
from .model_collection import ModelCollection
|
|
3
3
|
from .resolution_functions import LinearSpline
|
|
4
4
|
from .resolution_functions import PercentageFwhm
|
|
5
|
+
from .resolution_functions import Pointwise
|
|
5
6
|
from .resolution_functions import ResolutionFunction
|
|
6
7
|
|
|
7
8
|
__all__ = (
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
9
|
+
'LinearSpline',
|
|
10
|
+
'PercentageFwhm',
|
|
11
|
+
'Pointwise',
|
|
12
|
+
'ResolutionFunction',
|
|
13
|
+
'Model',
|
|
14
|
+
'ModelCollection',
|
|
13
15
|
)
|
|
@@ -8,9 +8,9 @@ from typing import Optional
|
|
|
8
8
|
from typing import Union
|
|
9
9
|
|
|
10
10
|
import numpy as np
|
|
11
|
+
from easyscience import ObjBase as BaseObj
|
|
11
12
|
from easyscience import global_object
|
|
12
|
-
from easyscience.
|
|
13
|
-
from easyscience.Objects.variable import Parameter
|
|
13
|
+
from easyscience.variable import Parameter
|
|
14
14
|
|
|
15
15
|
from easyreflectometry.sample import BaseAssembly
|
|
16
16
|
from easyreflectometry.sample import Sample
|
|
@@ -42,6 +42,8 @@ DEFAULTS = {
|
|
|
42
42
|
},
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
+
COLORS = ['#0173B2', '#DE8F05', '#029E73', '#D55E00', '#CC78BC', '#CA9161', '#FBAFE4', '#949494', '#ECE133', '#56B4E9']
|
|
46
|
+
|
|
45
47
|
|
|
46
48
|
class Model(BaseObj):
|
|
47
49
|
"""Model is the class that represents the experiment.
|
|
@@ -60,8 +62,8 @@ class Model(BaseObj):
|
|
|
60
62
|
scale: Union[Parameter, Number, None] = None,
|
|
61
63
|
background: Union[Parameter, Number, None] = None,
|
|
62
64
|
resolution_function: Union[ResolutionFunction, None] = None,
|
|
63
|
-
name: str = '
|
|
64
|
-
color: str =
|
|
65
|
+
name: str = 'Model',
|
|
66
|
+
color: str = COLORS[0],
|
|
65
67
|
unique_name: Optional[str] = None,
|
|
66
68
|
interface=None,
|
|
67
69
|
):
|
|
@@ -70,7 +72,7 @@ class Model(BaseObj):
|
|
|
70
72
|
:param sample: The sample being modelled.
|
|
71
73
|
:param scale: Scaling factor of profile.
|
|
72
74
|
:param background: Linear background magnitude.
|
|
73
|
-
:param name: Name of the model, defaults to '
|
|
75
|
+
:param name: Name of the model, defaults to 'Model'.
|
|
74
76
|
:param resolution_function: Resolution function, defaults to PercentageFwhm.
|
|
75
77
|
:param interface: Calculator interface, defaults to `None`.
|
|
76
78
|
|
{easyreflectometry-1.3.3 → easyreflectometry-1.4.0}/src/easyreflectometry/model/model_collection.py
RENAMED
|
@@ -4,6 +4,7 @@ from typing import List
|
|
|
4
4
|
from typing import Optional
|
|
5
5
|
from typing import Tuple
|
|
6
6
|
|
|
7
|
+
from easyreflectometry.model.model import COLORS
|
|
7
8
|
from easyreflectometry.sample.collections.base_collection import BaseCollection
|
|
8
9
|
|
|
9
10
|
from .model import Model
|
|
@@ -18,7 +19,7 @@ class ModelCollection(BaseCollection):
|
|
|
18
19
|
def __init__(
|
|
19
20
|
self,
|
|
20
21
|
*models: Tuple[Model],
|
|
21
|
-
name: str = '
|
|
22
|
+
name: str = 'Models',
|
|
22
23
|
interface=None,
|
|
23
24
|
unique_name: Optional[str] = None,
|
|
24
25
|
populate_if_none: bool = True,
|
|
@@ -41,7 +42,8 @@ class ModelCollection(BaseCollection):
|
|
|
41
42
|
:param model: Model to add.
|
|
42
43
|
"""
|
|
43
44
|
if model is None:
|
|
44
|
-
|
|
45
|
+
color = COLORS[len(self) % len(COLORS)]
|
|
46
|
+
model = Model(name='Model', interface=self.interface, color=color)
|
|
45
47
|
self.append(model)
|
|
46
48
|
|
|
47
49
|
def duplicate_model(self, index: int):
|
|
@@ -77,6 +79,6 @@ class ModelCollection(BaseCollection):
|
|
|
77
79
|
collection.add_model(Model.from_dict(model_data))
|
|
78
80
|
|
|
79
81
|
if len(collection) != len(this_dict['data']):
|
|
80
|
-
raise ValueError(f
|
|
82
|
+
raise ValueError(f'Expected {len(collection)} models, got {len(this_dict["data"])}')
|
|
81
83
|
|
|
82
84
|
return collection
|
|
@@ -30,6 +30,8 @@ class ResolutionFunction:
|
|
|
30
30
|
return PercentageFwhm(data['constant'])
|
|
31
31
|
if data['smearing'] == 'LinearSpline':
|
|
32
32
|
return LinearSpline(data['q_data_points'], data['fwhm_values'])
|
|
33
|
+
if data['smearing'] == 'Pointwise':
|
|
34
|
+
return Pointwise([data['q_data_points'], data['R_data_points'], data['sQz_data_points']])
|
|
33
35
|
raise ValueError('Unknown resolution function type')
|
|
34
36
|
|
|
35
37
|
|
|
@@ -60,3 +62,61 @@ class LinearSpline(ResolutionFunction):
|
|
|
60
62
|
self, skip: Optional[List[str]] = None
|
|
61
63
|
) -> dict[str, str]: # skip is kept for consistency of the as_dict signature
|
|
62
64
|
return {'smearing': 'LinearSpline', 'q_data_points': list(self.q_data_points), 'fwhm_values': list(self.fwhm_values)}
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
# add pointwise smearing funtion
|
|
68
|
+
class Pointwise(ResolutionFunction):
|
|
69
|
+
def __init__(self, q_data_points: list[np.ndarray]):
|
|
70
|
+
self.q_data_points = q_data_points
|
|
71
|
+
self.q = None
|
|
72
|
+
|
|
73
|
+
def smearing(self, q: Union[np.ndarray, float] = None) -> np.ndarray:
|
|
74
|
+
Qz = self.q_data_points[0]
|
|
75
|
+
R = self.q_data_points[1]
|
|
76
|
+
sQz = self.q_data_points[2]
|
|
77
|
+
if q is None:
|
|
78
|
+
q = self.q_data_points[0]
|
|
79
|
+
self.q = q
|
|
80
|
+
sQzs = np.sqrt(sQz)
|
|
81
|
+
if isinstance(Qz, float):
|
|
82
|
+
Qz = np.array(Qz)
|
|
83
|
+
|
|
84
|
+
smeared = self.apply_smooth_smearing(Qz, R, sQzs)
|
|
85
|
+
return smeared
|
|
86
|
+
|
|
87
|
+
def as_dict(
|
|
88
|
+
self, skip: Optional[List[str]] = None
|
|
89
|
+
) -> dict[str, str]: # skip is kept for consistency of the as_dict signature
|
|
90
|
+
return {
|
|
91
|
+
'smearing': 'Pointwise',
|
|
92
|
+
'q_data_points': list(self.q_data_points[0]),
|
|
93
|
+
'R_data_points': list(self.q_data_points[1]),
|
|
94
|
+
'sQz_data_points': list(self.q_data_points[2]),
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
def gaussian_smearing(self, qt, Qz, R, sQz):
|
|
98
|
+
weights = np.exp(-0.5 * ((qt - Qz) / sQz) ** 2)
|
|
99
|
+
if np.sum(weights) == 0 or not np.isfinite(np.sum(weights)):
|
|
100
|
+
return np.sum(R)
|
|
101
|
+
weights /= sQz * np.sqrt(2 * np.pi)
|
|
102
|
+
return np.sum(R * weights) / np.sum(weights)
|
|
103
|
+
|
|
104
|
+
def apply_smooth_smearing(self, Qz, R, sQzs):
|
|
105
|
+
"""
|
|
106
|
+
Apply smooth resolution smearing using convolution with Gaussian kernel.
|
|
107
|
+
"""
|
|
108
|
+
if self.q is None:
|
|
109
|
+
R_smeared = np.zeros_like(Qz)
|
|
110
|
+
else:
|
|
111
|
+
R_smeared = np.zeros_like(self.q)
|
|
112
|
+
|
|
113
|
+
if not isinstance(Qz, np.ndarray):
|
|
114
|
+
Qz = np.array(Qz)
|
|
115
|
+
if not isinstance(R, np.ndarray):
|
|
116
|
+
R = np.array(R)
|
|
117
|
+
R_smeared = np.zeros_like(self.q)
|
|
118
|
+
|
|
119
|
+
for i, qt in enumerate(self.q):
|
|
120
|
+
R_smeared[i] = self.gaussian_smearing(qt, Qz, R, sQzs)
|
|
121
|
+
|
|
122
|
+
return R_smeared
|