-
Notifications
You must be signed in to change notification settings - Fork 0
Extend initial scipp-analysis integration test suite for DREAM #124
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from all commits
Commits
Show all changes
15 commits
Select commit
Hold shift + click to select a range
7309aaf
Extend DREAM scipp-analysis integration tests
AndrewSazonov f8e309d
Fetch CIF uncached and update DREAM data checks
AndrewSazonov d7a4f8f
Refine DREAM integration tests for robustness
AndrewSazonov d9df094
Enable DREAM reduced-data fitting in tests
AndrewSazonov 4ddedb5
Update DREAM integration tests for API changes
AndrewSazonov 16534f7
Refactor DREAM tests; drop legacy scipp-cif
AndrewSazonov fab6dda
Round 2theta range to 5dp in CIF output
AndrewSazonov feb970a
Update data index SHA256 hash
AndrewSazonov 257000e
Relax version check to support dev builds
AndrewSazonov 3bc98f2
Add essdiffraction dependency
AndrewSazonov e860874
Prevent zero SUs; default intensity SU to 1.0
AndrewSazonov 4e1f3fa
Update tests/integration/scipp-analysis/dream/conftest.py
AndrewSazonov 70179c5
Relax point ID check; allow conftest asserts
AndrewSazonov 930f36c
Inline sample model and consolidate test fixtures
AndrewSazonov 44ec50b
Replace assert_almost_equal with pytest.approx
AndrewSazonov File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,60 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2026 DMSC | ||
| """Shared fixtures for DREAM scipp-analysis integration tests. | ||
|
|
||
| This module provides pytest fixtures for downloading and parsing | ||
| reduced diffraction data from the DREAM instrument in CIF format. | ||
| """ | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| import gemmi | ||
| import pytest | ||
| from pooch import retrieve | ||
|
|
||
| # Remote CIF file URL (regenerated nightly by scipp reduction pipeline) | ||
| CIF_URL = 'https://pub-6c25ef91903d4301a3338bd53b370098.r2.dev/dream_reduced.cif' | ||
|
|
||
| # Expected datablock name in the CIF file | ||
| DATABLOCK_NAME = 'reduced_tof' | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def cif_path( | ||
| tmp_path_factory: pytest.TempPathFactory, | ||
| ) -> str: | ||
| """Download CIF file fresh each test session and return its path. | ||
|
|
||
| Uses tmp_path_factory to avoid pooch caching, ensuring the latest | ||
| version of the nightly-regenerated CIF file is always used. | ||
| """ | ||
| tmp_dir = tmp_path_factory.mktemp('dream_data') | ||
| return retrieve(url=CIF_URL, known_hash=None, path=tmp_dir) | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def cif_content( | ||
| cif_path: str, | ||
| ) -> str: | ||
| """Read the CIF file content as text.""" | ||
| return Path(cif_path).read_text() | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def cif_document( | ||
| cif_path: str, | ||
| ) -> gemmi.cif.Document: | ||
| """Read the CIF file with gemmi and return the document.""" | ||
| return gemmi.cif.read(cif_path) | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def cif_block( | ||
| cif_document: gemmi.cif.Document, | ||
| ) -> gemmi.cif.Block: | ||
| """Return the 'reduced_tof' data block from the CIF document.""" | ||
| block = cif_document.find_block(DATABLOCK_NAME) | ||
| assert block is not None, ( | ||
| f'Expected CIF datablock {DATABLOCK_NAME!r} was not found in the document.' | ||
| ) | ||
| return block | ||
213 changes: 213 additions & 0 deletions
213
tests/integration/scipp-analysis/dream/test_analyze_reduced_data.py
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,213 @@ | ||
| # SPDX-License-Identifier: BSD-3-Clause | ||
| # Copyright (c) 2026 DMSC | ||
| """Tests for analyzing reduced diffraction data using easydiffraction. | ||
|
|
||
| These tests verify the complete workflow: | ||
| 1. Define project | ||
| 2. Add sample model manually defined | ||
| 3. Modify experiment CIF file | ||
| 4. Add experiment from modified CIF file | ||
| 5. Modify default experiment configuration | ||
| 6. Select parameters to be fitted | ||
| 7. Do fitting | ||
| """ | ||
|
|
||
| from pathlib import Path | ||
|
|
||
| import pytest | ||
|
|
||
| import easydiffraction as ed | ||
AndrewSazonov marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| # CIF experiment type tags required by easydiffraction to identify | ||
| # the experiment configuration (powder TOF neutron diffraction) | ||
| EXPT_TYPE_TAGS = { | ||
| '_expt_type.sample_form': 'powder', | ||
| '_expt_type.beam_mode': 'time-of-flight', | ||
| '_expt_type.radiation_probe': 'neutron', | ||
| '_expt_type.scattering_type': 'bragg', | ||
| } | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def prepared_cif_path( | ||
| cif_path: str, | ||
| tmp_path_factory: pytest.TempPathFactory, | ||
| ) -> str: | ||
| """Prepare CIF file with experiment type tags for | ||
| easydiffraction. | ||
| """ | ||
| with Path(cif_path).open() as f: | ||
| content = f.read() | ||
|
|
||
| # Add experiment type tags if missing | ||
| for tag, value in EXPT_TYPE_TAGS.items(): | ||
| if tag not in content: | ||
| content += f'\n{tag} {value}' | ||
|
|
||
| # Write to temp file | ||
| tmp_dir = tmp_path_factory.mktemp('dream_data') | ||
| prepared_path = tmp_dir / 'dream_reduced_prepared.cif' | ||
| prepared_path.write_text(content) | ||
|
|
||
| return str(prepared_path) | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def project_with_data( | ||
| prepared_cif_path: str, | ||
| ) -> ed.Project: | ||
| """Create project with sample model, experiment data, and | ||
| configuration. | ||
|
|
||
| 1. Define project | ||
| 2. Add sample model manually defined | ||
| 3. Modify experiment CIF file | ||
| 4. Add experiment from modified CIF file | ||
| 5. Modify default experiment configuration | ||
| """ | ||
| # Step 1: Define Project | ||
| project = ed.Project() | ||
|
|
||
| # Step 2: Define Sample Model manually | ||
| project.sample_models.add(name='si') | ||
| sample_model = project.sample_models['si'] | ||
|
|
||
| sample_model.space_group.name_h_m = 'F d -3 m' | ||
| sample_model.space_group.it_coordinate_system_code = '1' | ||
|
|
||
| sample_model.cell.length_a = 5.43146 | ||
|
|
||
| sample_model.atom_sites.add( | ||
| label='Si', | ||
| type_symbol='Si', | ||
| fract_x=0.125, | ||
| fract_y=0.125, | ||
| fract_z=0.125, | ||
| wyckoff_letter='c', | ||
| b_iso=1.1, | ||
| ) | ||
|
|
||
| # Step 3: Add experiment from modified CIF file | ||
| project.experiments.add(cif_path=prepared_cif_path) | ||
| experiment = project.experiments['reduced_tof'] | ||
|
|
||
| # Step 4: Configure experiment | ||
| # Link phase | ||
| experiment.linked_phases.add(id='si', scale=0.8) | ||
|
|
||
| # Instrument setup | ||
| experiment.instrument.setup_twotheta_bank = 90.0 | ||
| experiment.instrument.calib_d_to_tof_linear = 18630.0 | ||
|
|
||
| # Peak profile parameters | ||
| experiment.peak.broad_gauss_sigma_0 = 48500.0 | ||
| experiment.peak.broad_gauss_sigma_1 = 3000.0 | ||
| experiment.peak.broad_gauss_sigma_2 = 0.0 | ||
| experiment.peak.broad_mix_beta_0 = 0.05 | ||
| experiment.peak.broad_mix_beta_1 = 0.0 | ||
| experiment.peak.asym_alpha_0 = 0.0 | ||
| experiment.peak.asym_alpha_1 = 0.26 | ||
|
|
||
| # Excluded regions | ||
| experiment.excluded_regions.add(id='1', start=0, end=10000) | ||
| experiment.excluded_regions.add(id='2', start=70000, end=200000) | ||
|
|
||
| # Background points | ||
| background_points = [ | ||
| ('2', 10000, 0.01), | ||
| ('3', 14000, 0.2), | ||
| ('4', 21000, 0.7), | ||
| ('5', 27500, 0.6), | ||
| ('6', 40000, 0.3), | ||
| ('7', 50000, 0.6), | ||
| ('8', 61000, 0.7), | ||
| ('9', 70000, 0.6), | ||
| ] | ||
| for id_, x, y in background_points: | ||
| experiment.background.add(id=id_, x=x, y=y) | ||
|
|
||
| return project | ||
|
|
||
|
|
||
| @pytest.fixture(scope='module') | ||
| def fitted_project( | ||
| project_with_data: ed.Project, | ||
| ) -> ed.Project: | ||
| """Perform fit and return project with results. | ||
|
|
||
| 6. Select parameters to be fitted | ||
| 7. Do fitting | ||
| """ | ||
| project = project_with_data | ||
| sample_model = project.sample_models['si'] | ||
| experiment = project.experiments['reduced_tof'] | ||
|
|
||
| # Step 5: Select parameters to be fitted | ||
| # Set free parameters for sample model | ||
| sample_model.atom_sites['Si'].b_iso.free = True | ||
|
|
||
| # Set free parameters for experiment | ||
| experiment.linked_phases['si'].scale.free = True | ||
| experiment.instrument.calib_d_to_tof_linear.free = True | ||
|
|
||
| experiment.peak.broad_gauss_sigma_0.free = True | ||
| experiment.peak.broad_gauss_sigma_1.free = True | ||
| experiment.peak.broad_mix_beta_0.free = True | ||
|
|
||
| # Set free parameters for background | ||
| for point in experiment.background: | ||
| point.y.free = True | ||
|
|
||
| # Step 6: Do fitting | ||
| project.analysis.fit() | ||
|
|
||
| return project | ||
|
|
||
|
|
||
| # Test: Data Loading | ||
|
|
||
|
|
||
| def test_analyze_reduced_data__load_cif( | ||
| project_with_data: ed.Project, | ||
| ) -> None: | ||
| """Verify CIF data loads into project correctly.""" | ||
| assert 'reduced_tof' in project_with_data.experiments.names | ||
|
|
||
|
|
||
| def test_analyze_reduced_data__data_size( | ||
| project_with_data: ed.Project, | ||
| ) -> None: | ||
| """Verify loaded data has expected size.""" | ||
| experiment = project_with_data.experiments['reduced_tof'] | ||
| # Data should have substantial number of points | ||
| assert experiment.data.x.size > 100 | ||
|
|
||
|
|
||
| # Test: Configuration | ||
|
|
||
|
|
||
| def test_analyze_reduced_data__phase_linked( | ||
| project_with_data: ed.Project, | ||
| ) -> None: | ||
| """Verify phase is correctly linked to experiment.""" | ||
| experiment = project_with_data.experiments['reduced_tof'] | ||
| assert 'si' in experiment.linked_phases.names | ||
|
|
||
|
|
||
| def test_analyze_reduced_data__background_set( | ||
| project_with_data: ed.Project, | ||
| ) -> None: | ||
| """Verify background points are configured.""" | ||
| experiment = project_with_data.experiments['reduced_tof'] | ||
| assert len(experiment.background.names) >= 5 | ||
|
|
||
|
|
||
| # Test: Fitting | ||
|
|
||
|
|
||
| def test_analyze_reduced_data__fit_quality( | ||
| fitted_project: ed.Project, | ||
| ) -> None: | ||
| """Verify fit quality is reasonable (chi-square value).""" | ||
| chi_square = fitted_project.analysis.fit_results.reduced_chi_square | ||
| assert chi_square == pytest.approx(16.0, abs=0.1) | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
cif_pathdownloads a nightly-regenerated CIF from a remote URL withknown_hash=Noneand explicitly avoids caching. This makes the integration test suite non-deterministic and prone to failures whenever the upstream file changes or the network is unavailable. Consider pinning to a versioned/test fixture (committed file or a URL+sha256 inknown_hash) and lettingpoochcache/validate the content for reproducible CI runs.