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
19 changes: 19 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
# Unreleased

Restored the measured per-point resolution on data load (issue #368).

- Loading data through `Project` (`load_new_experiment`,
`load_experiment_for_model_at_index`,
`load_all_experiments_from_file`) again sets a `Pointwise` resolution
function when the file carries per-point q-resolution (an sQz column
in `.ort` files, or a 4th column in text files). Since PR #293 the
loaders discarded this data and always applied a flat
`PercentageFwhm(5.0)` — a temporary workaround that never got
reverted. Fits of such data were smeared with 5% FWHM regardless of
what the instrument delivered and should be re-run.
- Files without q-resolution data keep the 5% FWHM default. The pre-#293
fallback that built a `LinearSpline` from the _reflectivity_ error
(`sqrt(ye)`) was not restored: a reflectivity uncertainty is not a
q-width, and that branch produced effectively zero smearing.
- Known limitation (pre-existing): the resolution function lives on the
model, so when several experiments share one model the last-loaded
dataset's resolution wins.

Fixed inconsistent interpretation of vector resolution functions between
the refnx and refl1d engines (issue #367).

Expand Down
10 changes: 9 additions & 1 deletion src/easyreflectometry/project.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@
from easyreflectometry.model import Model
from easyreflectometry.model import ModelCollection
from easyreflectometry.model import PercentageFwhm
from easyreflectometry.model import Pointwise
from easyreflectometry.sample import Layer
from easyreflectometry.sample import Material
from easyreflectometry.sample import MaterialCollection
Expand Down Expand Up @@ -521,14 +522,21 @@
) -> None:
"""Set the resolution function on *model* based on variance data in *experiment*.

Uses the measured per-point q-resolution (``Pointwise``) when the
experiment carries q-variance data (``xe``, i.e. sQz²); otherwise
falls back to the default 5% FWHM percentage resolution.

Parameters
----------
experiment : DataSet1D
The experiment whose variance data drives the choice.
model : Model
The model whose resolution function is set.
"""
model.resolution_function = PercentageFwhm(5.0)
if experiment.xe is not None and np.any(experiment.xe):
model.resolution_function = Pointwise(q_data_points=[experiment.x, experiment.y, experiment.xe])

Check warning on line 537 in src/easyreflectometry/project.py

View check run for this annotation

Codecov / codecov/patch

src/easyreflectometry/project.py#L536-L537

Added lines #L536 - L537 were not covered by tests
else:
model.resolution_function = PercentageFwhm(5.0)

Check warning on line 539 in src/easyreflectometry/project.py

View check run for this annotation

Codecov / codecov/patch

src/easyreflectometry/project.py#L539

Added line #L539 was not covered by tests

@staticmethod
def _auto_set_background(experiment: DataSet1D) -> None:
Expand Down
2 changes: 1 addition & 1 deletion tests/summary/test_summary.py
Original file line number Diff line number Diff line change
Expand Up @@ -136,7 +136,7 @@ def test_experiments_section(self, project: Project) -> None:
assert 'No. of data points' in html
assert '408' in html
assert 'Resolution function' in html
assert 'PercentageFwhm' in html
assert 'Pointwise' in html

def test_experiments_section_percentage_fhwm(self, project: Project) -> None:
# When
Expand Down
55 changes: 46 additions & 9 deletions tests/test_project.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
from easyreflectometry.model import Model
from easyreflectometry.model import ModelCollection
from easyreflectometry.model import PercentageFwhm
from easyreflectometry.model import Pointwise
from easyreflectometry.project import Project
from easyreflectometry.sample import Layer
from easyreflectometry.sample import Material
Expand Down Expand Up @@ -663,7 +664,8 @@ def test_load_experiment(self):
assert isinstance(project.experiments[5], DataSet1D)
assert project.experiments[5].name == 'Example data file from refnx docs'
assert project.experiments[5].model == model_5
assert isinstance(project.models[5].resolution_function, PercentageFwhm)
# example.ort carries an sQz column, so the measured resolution is used
assert isinstance(project.models[5].resolution_function, Pointwise)
assert isinstance(project.models[4].resolution_function, PercentageFwhm)

def test_load_experiment_sets_resolution_function_pointwise_when_xe_present(self, tmp_path):
Expand All @@ -679,12 +681,13 @@ def test_load_experiment_sets_resolution_function_pointwise_when_xe_present(self
# Then
project.load_experiment_for_model_at_index(str(fpath))

# Resolution is always set to PercentageFwhm
from easyreflectometry.model.resolution_functions import PercentageFwhm
# Expect Pointwise because xe (q-resolution) is present
resolution_function = project.models[0].resolution_function
assert isinstance(resolution_function, Pointwise)
# The 4th column is sQz (sigma); smearing() must return it unchanged
assert_allclose(resolution_function.smearing([0.01, 0.02]), [1e-4, 1e-4])

assert isinstance(project.models[0].resolution_function, PercentageFwhm)

def test_load_experiment_sets_linearspline_when_only_ye_present(self, tmp_path):
def test_load_experiment_keeps_percentage_fwhm_when_no_xe(self, tmp_path):
# When
global_object.map._clear()
project = Project()
Expand All @@ -697,11 +700,45 @@ def test_load_experiment_sets_linearspline_when_only_ye_present(self, tmp_path):
# Then
project.load_experiment_for_model_at_index(str(fpath))

# Resolution is always set to PercentageFwhm
from easyreflectometry.model.resolution_functions import PercentageFwhm

# No q-resolution data available, so the 5% FWHM default is kept
assert isinstance(project.models[0].resolution_function, PercentageFwhm)

def test_apply_resolution_function_prefers_pointwise_falls_back_to_percentage(self):
# When
global_object.map._clear()
project = Project()
model = Model()
with_xe = DataSet1D(x=[0.01, 0.02], y=[1.0, 2.0], ye=[0.1, 0.1], xe=[1e-8, 4e-8])
zero_xe = DataSet1D(x=[0.01, 0.02], y=[1.0, 2.0], ye=[0.1, 0.1], xe=[0.0, 0.0])
no_xe = DataSet1D(x=[0.01, 0.02], y=[1.0, 2.0], ye=[0.1, 0.1])

# Then Expect
project._apply_resolution_function(with_xe, model)
assert isinstance(model.resolution_function, Pointwise)
# xe holds sQz variances; smearing() must return sigma = sqrt(xe)
assert_allclose(model.resolution_function.smearing([0.01, 0.02]), [1e-4, 2e-4])

project._apply_resolution_function(zero_xe, model)
assert isinstance(model.resolution_function, PercentageFwhm)

project._apply_resolution_function(no_xe, model)
assert isinstance(model.resolution_function, PercentageFwhm)

def test_load_all_experiments_from_file_sets_pointwise_when_sqz_present(self):
# When
global_object.map._clear()
project = Project()
project.models = ModelCollection(Model())
fpath = os.path.join(PATH_STATIC, 'test_example2.ort')

# Then
n_loaded = project.load_all_experiments_from_file(fpath)

# Expect
assert n_loaded == 2
assert list(project.experiments.keys()) == [0, 1]
assert isinstance(project.models[0].resolution_function, Pointwise)

def test_experimental_data_at_index(self):
# When
global_object.map._clear()
Expand Down
Loading