Skip to content

Commit

Permalink
197 add experiment data to project in order for this to be exposed to…
Browse files Browse the repository at this point in the history
… the app (#198)

* experiments and q values

* pass all four elements in datapoint

* adjust tests and set resolution function when loading measured data

* from steps to resolution
  • Loading branch information
andped10 authored Oct 25, 2024
1 parent d6a661a commit e95df0f
Show file tree
Hide file tree
Showing 4 changed files with 135 additions and 19 deletions.
8 changes: 6 additions & 2 deletions src/easyreflectometry/data/data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -101,6 +101,10 @@ def __init__(
x = np.array(x)
if not isinstance(y, np.ndarray):
y = np.array(y)
if not isinstance(ye, np.ndarray):
ye = np.array(ye)
if not isinstance(xe, np.ndarray):
xe = np.array(xe)

self.x = x
self.y = y
Expand Down Expand Up @@ -129,8 +133,8 @@ def is_experiment(self) -> bool:
def is_simulation(self) -> bool:
return self._model is None

def data_points(self) -> int:
return zip(self.x, self.y)
def data_points(self) -> tuple[float, float, float, float]:
return zip(self.x, self.y, self.ye, self.xe)

def __repr__(self) -> str:
return "1D DataStore of '{:s}' Vs '{:s}' with {} data points".format(self.x_label, self.y_label, len(self.x))
76 changes: 62 additions & 14 deletions src/easyreflectometry/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,20 @@
import json
import os
from pathlib import Path
from typing import Dict
from typing import List
from typing import Optional
from typing import Union

import numpy as np
from easyscience import global_object
from easyscience.fitting import AvailableMinimizers
from scipp import DataGroup

from easyreflectometry.calculators import CalculatorFactory
from easyreflectometry.data import DataSet1D
from easyreflectometry.data import load
from easyreflectometry.model import LinearSpline
from easyreflectometry.model import Model
from easyreflectometry.model import ModelCollection
from easyreflectometry.model import PercentageFhwm
Expand All @@ -23,19 +27,10 @@

Q_MIN = 0.001
Q_MAX = 0.3
Q_ELEMENTS = 500
Q_RESOLUTION = 500

DEFAULT_MINIZER = AvailableMinimizers.LMFit_leastsq

EXPERIMENTAL_DATA = [
DataSet1D(
name='Example Data 0',
x=np.linspace(Q_MIN, Q_MAX, Q_ELEMENTS),
y=3 * np.linspace(Q_MIN, Q_MAX, Q_ELEMENTS),
ye=0.1 * np.linspace(Q_MIN, Q_MAX, Q_ELEMENTS),
)
]


class Project:
def __init__(self):
Expand All @@ -45,9 +40,12 @@ def __init__(self):
self._materials = MaterialCollection(populate_if_none=False, unique_name='project_materials')
self._calculator = CalculatorFactory()
self._minimizer = DEFAULT_MINIZER
self._experiments: List[DataSet1D] = None
self._experiments: Dict[DataGroup] = {}
self._colors = None
self._report = None
self._q_min = None
self._q_max = None
self._q_resolution = None

# Project flags
self._created = False
Expand All @@ -65,14 +63,44 @@ def reset(self):
self._path_project_parent = Path(os.path.expanduser('~'))
self._calculator = CalculatorFactory()
self._minimizer = DEFAULT_MINIZER
self._experiments = None
self._experiments = {}
self._colors = None
self._report = None

# Project flags
self._created = False
self._with_experiments = False

@property
def q_min(self):
if self._q_min is None:
return Q_MIN
return self._q_min

@q_min.setter
def q_min(self, value: float) -> None:
self._q_min = value

@property
def q_max(self):
if self._q_max is None:
return Q_MAX
return self._q_max

@q_max.setter
def q_max(self, value: float) -> None:
self._q_max = value

@property
def q_resolution(self):
if self._q_resolution is None:
return Q_RESOLUTION
return self._q_resolution

@q_resolution.setter
def q_resolution(self, value: int) -> None:
self._q_resolution = value

@property
def created(self) -> bool:
return self._created
Expand Down Expand Up @@ -113,6 +141,16 @@ def experiments(self, experiments: List[DataSet1D]) -> None:
def path_json(self):
return self.path / 'project.json'

def load_experiment_for_model_at_index(self, path: Union[Path, str], index: Optional[int] = 0) -> None:
self._experiments[index] = load(str(path))
# Set the resolution function if variance data is present
if sum(self._experiments[index]['coords']['Qz_0'].variances) != 0:
resolution_function = LinearSpline(
q_data_points=self._experiments[index]['coords']['Qz_0'].values,
fwhm_values=np.sqrt(self._experiments[index]['coords']['Qz_0'].variances),
)
self._models[index].resolution_function = resolution_function

def sld_data_for_model_at_index(self, index: int = 0) -> DataSet1D:
self.models[index].interface = self._calculator
sld = self.models[index].interface().sld_profile(self._models[index].unique_name)
Expand All @@ -132,7 +170,7 @@ def sample_data_for_model_at_index(self, index: int = 0, q_range: Optional[np.ar

def model_data_for_model_at_index(self, index: int = 0, q_range: Optional[np.array] = None) -> DataSet1D:
if q_range is None:
q_range = np.linspace(Q_MIN, Q_MAX, Q_ELEMENTS)
q_range = np.linspace(self.q_min, self.q_max, self.q_resolution)
self.models[index].interface = self._calculator
reflectivity = self.models[index].interface().reflectity_profile(q_range, self._models[index].unique_name)
return DataSet1D(
Expand All @@ -142,7 +180,17 @@ def model_data_for_model_at_index(self, index: int = 0, q_range: Optional[np.arr
)

def experimental_data_for_model_at_index(self, index: int = 0) -> DataSet1D:
return EXPERIMENTAL_DATA[index]
if index in self._experiments.keys():
return DataSet1D(
name=f'Experiment for Model {index}',
x=self._experiments[index]['coords']['Qz_0'].values,
y=self._experiments[index]['data']['R_0'].values,
ye=self._experiments[index]['data']['R_0'].variances,
xe=self._experiments[index]['coords']['Qz_0'].variances,
model=self.models[index],
)
else:
raise IndexError(f'No experiment data for model at index {index}')

def default_model(self):
self._replace_collection(MaterialCollection(), self._materials)
Expand Down
2 changes: 1 addition & 1 deletion tests/data/test_data_store.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,4 +41,4 @@ def test_data_points(self):
points = data.data_points()

# Expect
assert list(points) == [(1, 4), (2, 5), (3, 6)]
assert list(points) == [(1, 4, 7, 10), (2, 5, 8, 11), (3, 6, 9, 12)]
68 changes: 66 additions & 2 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,20 @@
from easyscience import global_object
from easyscience.fitting import AvailableMinimizers
from numpy.testing import assert_allclose
from scipp import DataGroup

import easyreflectometry
from easyreflectometry.data import DataSet1D
from easyreflectometry.model import LinearSpline
from easyreflectometry.model import Model
from easyreflectometry.model import ModelCollection
from easyreflectometry.model import PercentageFhwm
from easyreflectometry.project import Project
from easyreflectometry.sample import Material
from easyreflectometry.sample import MaterialCollection

PATH_STATIC = os.path.join(os.path.dirname(easyreflectometry.__file__), '..', '..', 'tests', '_static')


class TestProject:
def test_constructor(self):
Expand All @@ -32,7 +39,7 @@ def test_constructor(self):
assert len(project._models) == 0
assert project._calculator.current_interface_name == 'refnx'
assert project._minimizer == AvailableMinimizers.LMFit_leastsq
assert project._experiments is None
assert project._experiments == {}
assert project._report is None
assert project._created is False
assert project._with_experiments is False
Expand Down Expand Up @@ -70,7 +77,7 @@ def test_reset(self):
assert project._path_project_parent == Path(os.path.expanduser('~'))
assert project._calculator.current_interface_name == 'refnx'
assert project._minimizer == AvailableMinimizers.LMFit_leastsq
assert project._experiments is None
assert project._experiments == {}
assert project._report is None
assert project._created is False
assert project._with_experiments is False
Expand Down Expand Up @@ -465,3 +472,60 @@ def test_create(self, tmp_path):
'experiments': 'None',
'modified': datetime.datetime.now().strftime('%d.%m.%Y %H:%M'),
}

def test_load_experiment(self):
# When
project = Project()
project.models = ModelCollection(Model(), Model(), Model(), Model(), Model(), Model())
fpath = os.path.join(PATH_STATIC, 'example.ort')

# Then
project.load_experiment_for_model_at_index(fpath, 5)

# Expect
assert list(project.experiments.keys()) == [5]
assert isinstance(project.experiments[5], DataGroup)
assert isinstance(project.models[5].resolution_function, LinearSpline)
assert isinstance(project.models[4].resolution_function, PercentageFhwm)

def test_experimental_data_at_index(self):
# When
project = Project()
project.models = ModelCollection(Model())
fpath = os.path.join(PATH_STATIC, 'example.ort')
project.load_experiment_for_model_at_index(fpath)

# Then
data = project.experimental_data_for_model_at_index()

# Expect
assert data.name == 'Experiment for Model 0'
assert data.is_experiment
assert isinstance(data, DataSet1D)
assert len(data.x) == 408
assert len(data.xe) == 408
assert len(data.y) == 408
assert len(data.ye) == 408

def test_q(self):
# When
project = Project()

# Then
q = project.q_min, project.q_max, project.q_resolution

# Expect
assert q == (0.001, 0.3, 500)

def test_set_q(self):
# When
project = Project()

# Then
project.q_min = 1
project.q_max = 2
project.q_resolution = 3

# Expect
q = project.q_min, project.q_max, project.q_resolution
assert q == (1, 2, 3)

0 comments on commit e95df0f

Please sign in to comment.