easyreflectometry 1.8.0__tar.gz → 2.0.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.8.0 → easyreflectometry-2.0.0}/.github/copilot-instructions.md +0 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/pypi-test.yml +8 -0
- easyreflectometry-2.0.0/.planning/codebase/ARCHITECTURE.md +311 -0
- easyreflectometry-2.0.0/.planning/codebase/CONCERNS.md +280 -0
- easyreflectometry-2.0.0/.planning/codebase/INTEGRATIONS.md +203 -0
- easyreflectometry-2.0.0/.planning/codebase/STACK.md +142 -0
- easyreflectometry-2.0.0/.planning/codebase/STRUCTURE.md +245 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/CHANGELOG.md +393 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/PKG-INFO +38 -9
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/README.md +33 -5
- easyreflectometry-2.0.0/docs/docs/api-reference/contrasts.md +75 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/index.md +9 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/inequality_constraints.md +2 -3
- easyreflectometry-2.0.0/docs/docs/api-reference/volume_fraction.md +1 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/installation-and-setup/index.md +2 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/advancedfitting/bayesian_bumps.ipynb +4 -4
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/advancedfitting/constraints.ipynb +4 -4
- easyreflectometry-2.0.0/docs/docs/tutorials/advancedfitting/contrasts.ipynb +579 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/advancedfitting/multi_contrast.ipynb +44 -27
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/advancedfitting/polarized_fitting.ipynb +91 -13
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/basic/model.md +6 -8
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/monolayer.ipynb +1 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/repeating.ipynb +1 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/index.md +5 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/bilayer.ipynb +78 -6
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/magnetism.ipynb +113 -24
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/user-guide/index.md +75 -2
- easyreflectometry-2.0.0/docs/docs/user-guide/magnetism.md +213 -0
- easyreflectometry-2.0.0/docs/docs/user-guide/resolution.md +154 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/mkdocs.yml +5 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/notebooks/ess_hercules_2026.ipynb +3 -3
- easyreflectometry-2.0.0/notebooks/polarized_demo_data/fe_on_si_dd.dat +161 -0
- easyreflectometry-2.0.0/notebooks/polarized_demo_data/fe_on_si_du.dat +161 -0
- easyreflectometry-2.0.0/notebooks/polarized_demo_data/fe_on_si_ud.dat +161 -0
- easyreflectometry-2.0.0/notebooks/polarized_demo_data/fe_on_si_uu.dat +161 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/notebooks/polarized_fitting.ipynb +2 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/notebooks/zero_variance_fitting.ipynb +7 -7
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/pixi.lock +7732 -8538
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/pyproject.toml +5 -3
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/__init__.py +2 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/_bumps_constraints.py +8 -10
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/analysis/bayesian.py +65 -18
- easyreflectometry-2.0.0/src/easyreflectometry/calculators/calculator_base.py +244 -0
- easyreflectometry-2.0.0/src/easyreflectometry/calculators/factory.py +168 -0
- easyreflectometry-2.0.0/src/easyreflectometry/calculators/refl1d/calculator.py +17 -0
- easyreflectometry-2.0.0/src/easyreflectometry/calculators/refl1d/wrapper.py +289 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/calculators/refnx/calculator.py +0 -19
- easyreflectometry-2.0.0/src/easyreflectometry/calculators/refnx/wrapper.py +77 -0
- easyreflectometry-2.0.0/src/easyreflectometry/calculators/stateless_wrapper_base.py +276 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/constraints.py +2 -2
- easyreflectometry-2.0.0/src/easyreflectometry/contrasts.py +450 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/data/__init__.py +4 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/data/data_store.py +18 -3
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/data/measurement.py +142 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/data/polarized.py +2 -0
- easyreflectometry-2.0.0/src/easyreflectometry/fit_settings.py +399 -0
- easyreflectometry-2.0.0/src/easyreflectometry/fitting.py +1434 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/inequality_constraints.py +5 -5
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/model/model.py +69 -22
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/model/model_collection.py +5 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/model/resolution_functions.py +14 -4
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/orso_utils.py +172 -43
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/plot.py +63 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/project.py +1207 -296
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/__init__.py +8 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/bilayer.py +22 -39
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/multilayer.py +1 -5
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/surfactant_layer.py +9 -26
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/base_core.py +40 -33
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/collections/base_collection.py +60 -36
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/collections/layer_collection.py +1 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/collections/material_collection.py +2 -3
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/collections/sample.py +36 -5
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/layers/layer.py +110 -19
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/layers/layer_area_per_molecule.py +1 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/materials/material_density.py +122 -47
- easyreflectometry-2.0.0/src/easyreflectometry/sample/elements/materials/material_magnetic.py +188 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/materials/material_mixture.py +2 -20
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/materials/material_solvated.py +1 -1
- easyreflectometry-2.0.0/src/easyreflectometry/sample/paths.py +75 -0
- easyreflectometry-2.0.0/src/easyreflectometry/sample/references.py +224 -0
- easyreflectometry-2.0.0/src/easyreflectometry/sample/volume_fraction.py +468 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/special/calculations.py +50 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/summary/html_templates.py +8 -4
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/summary/summary.py +273 -132
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/utils.py +3 -3
- easyreflectometry-2.0.0/tests/_static/PROVENANCE.md +43 -0
- easyreflectometry-2.0.0/tests/_static/e361r.txt +99 -0
- easyreflectometry-2.0.0/tests/_static/validation_test4.dat +104 -0
- easyreflectometry-2.0.0/tests/calculators/baselines/refl1d.npz +0 -0
- easyreflectometry-2.0.0/tests/calculators/baselines/refnx.npz +0 -0
- easyreflectometry-2.0.0/tests/calculators/refl1d/test_refl1d_calculator.py +125 -0
- easyreflectometry-2.0.0/tests/calculators/refl1d/test_refl1d_wrapper.py +456 -0
- easyreflectometry-2.0.0/tests/calculators/refnx/test_refnx_calculator.py +98 -0
- easyreflectometry-2.0.0/tests/calculators/refnx/test_refnx_wrapper.py +280 -0
- easyreflectometry-2.0.0/tests/calculators/test_calculator_baselines.py +302 -0
- easyreflectometry-2.0.0/tests/calculators/test_calculator_membership.py +169 -0
- easyreflectometry-2.0.0/tests/calculators/test_magnetism_reconciliation.py +223 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/calculators/test_polarization_interface.py +3 -4
- easyreflectometry-2.0.0/tests/calculators/test_recycled_names.py +109 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/calculators/test_resolution_conventions.py +16 -44
- easyreflectometry-2.0.0/tests/calculators/test_validation_suite.py +101 -0
- easyreflectometry-2.0.0/tests/calculators/test_volume_fraction_profile.py +88 -0
- easyreflectometry-2.0.0/tests/integration/convergence/reference.md +22 -0
- easyreflectometry-2.0.0/tests/integration/convergence/test_convergence.py +227 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/integration/test_cross_engine_resolution.py +20 -61
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/integration/test_ort_file_fitting.py +3 -4
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/model/test_model.py +45 -41
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/model/test_model_collection.py +7 -7
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/assemblies/test_base_assembly.py +2 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/assemblies/test_bilayer.py +32 -9
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/assemblies/test_gradient_layer.py +2 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/assemblies/test_multilayer.py +21 -15
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/assemblies/test_repeating_multilayer.py +21 -15
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/assemblies/test_surfactant_layer.py +33 -5
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/collections/test_base_collection.py +5 -22
- easyreflectometry-2.0.0/tests/sample/collections/test_collection_fields.py +70 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/collections/test_layer_collection.py +70 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/collections/test_material_collection.py +1 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/collections/test_sample.py +1 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/elements/layers/test_layer.py +6 -5
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/elements/layers/test_layer_area_per_molecule.py +2 -2
- easyreflectometry-2.0.0/tests/sample/elements/layers/test_layer_magnetism.py +476 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/elements/materials/test_material.py +1 -1
- easyreflectometry-2.0.0/tests/sample/elements/materials/test_material_density.py +334 -0
- easyreflectometry-2.0.0/tests/sample/elements/materials/test_material_magnetic.py +134 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/elements/materials/test_material_mixture.py +10 -13
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/elements/materials/test_material_solvated.py +2 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/sample/test_base_core.py +9 -29
- easyreflectometry-2.0.0/tests/sample/test_volume_fraction.py +331 -0
- easyreflectometry-2.0.0/tests/summary/test_summary.py +535 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_bayesian.py +224 -5
- easyreflectometry-2.0.0/tests/test_contrasts.py +580 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_data.py +1 -2
- easyreflectometry-2.0.0/tests/test_fit_settings.py +520 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_fitting.py +13 -12
- easyreflectometry-2.0.0/tests/test_guide_field.py +320 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_limits.py +19 -19
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_measurement_comprehensive.py +1 -2
- easyreflectometry-2.0.0/tests/test_model_removal.py +167 -0
- easyreflectometry-2.0.0/tests/test_orso_magnetism.py +272 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_orso_support.py +7 -5
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_orso_utils.py +1 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_ort_file.py +1 -2
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_parameter_utils.py +2 -2
- easyreflectometry-2.0.0/tests/test_per_dataset_resolution.py +1166 -0
- easyreflectometry-2.0.0/tests/test_plot.py +61 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_polarized_fitting.py +46 -24
- easyreflectometry-2.0.0/tests/test_prepared_fit.py +297 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_project.py +173 -94
- easyreflectometry-2.0.0/tests/test_public_api.py +13 -0
- easyreflectometry-2.0.0/tests/test_shared_persistence.py +298 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_utils.py +10 -1
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_bumps_constraints_shim.py +57 -15
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_constraints.py +86 -58
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_derived_parameters.py +6 -6
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_fitting_mcmc.py +113 -8
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_inequality_constraints.py +18 -18
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_material_and_calculations.py +6 -6
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_project_core.py +9 -12
- easyreflectometry-2.0.0/tests/unit/test_summary_goodness_of_fit.py +92 -0
- easyreflectometry-2.0.0/tools/benchmark_calculators.py +224 -0
- easyreflectometry-1.8.0/.coveragerc +0 -3
- easyreflectometry-1.8.0/CONTRIBUTING.rst +0 -114
- easyreflectometry-1.8.0/Makefile +0 -79
- easyreflectometry-1.8.0/notebooks/polarized_demo_data/fe_on_si_dd.dat +0 -161
- easyreflectometry-1.8.0/notebooks/polarized_demo_data/fe_on_si_du.dat +0 -161
- easyreflectometry-1.8.0/notebooks/polarized_demo_data/fe_on_si_ud.dat +0 -161
- easyreflectometry-1.8.0/notebooks/polarized_demo_data/fe_on_si_uu.dat +0 -161
- easyreflectometry-1.8.0/src/easyreflectometry/calculators/calculator_base.py +0 -332
- easyreflectometry-1.8.0/src/easyreflectometry/calculators/factory.py +0 -84
- easyreflectometry-1.8.0/src/easyreflectometry/calculators/refl1d/calculator.py +0 -38
- easyreflectometry-1.8.0/src/easyreflectometry/calculators/refl1d/wrapper.py +0 -609
- easyreflectometry-1.8.0/src/easyreflectometry/calculators/refnx/wrapper.py +0 -224
- easyreflectometry-1.8.0/src/easyreflectometry/calculators/wrapper_base.py +0 -442
- easyreflectometry-1.8.0/src/easyreflectometry/fitting.py +0 -948
- easyreflectometry-1.8.0/tests/calculators/refl1d/test_refl1d_calculator.py +0 -219
- easyreflectometry-1.8.0/tests/calculators/refl1d/test_refl1d_wrapper.py +0 -680
- easyreflectometry-1.8.0/tests/calculators/refnx/test_refnx_calculator.py +0 -133
- easyreflectometry-1.8.0/tests/calculators/refnx/test_refnx_wrapper.py +0 -577
- easyreflectometry-1.8.0/tests/sample/elements/layers/test_layer_magnetism.py +0 -227
- easyreflectometry-1.8.0/tests/sample/elements/materials/test_material_density.py +0 -181
- easyreflectometry-1.8.0/tests/summary/test_summary.py +0 -351
- easyreflectometry-1.8.0/tests/unit/test_summary_goodness_of_fit.py +0 -86
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.badgery.yaml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.codecov.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.copier-answers.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.gitattributes +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/actions/download-artifact/action.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/actions/github-script/action.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/actions/setup-easyscience-bot/action.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/actions/setup-pixi/action.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/actions/upload-artifact/action.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/actions/upload-codecov/action.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/configs/pages-deployment.json +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/configs/rulesets-develop.json +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/configs/rulesets-gh-pages.json +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/configs/rulesets-master.json +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/release-drafter.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/scripts/backmerge-conflict-issue.js +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/scripts/publish-dashboard.sh +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/backmerge.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/cleanup.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/codeql-analysis.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/coverage.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/dashboard.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/docs.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/issues-labels.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/lint-format.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/ossar-analysis.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/pr-labels.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/pypi-publish.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/python-ci.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/python-package.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/python-publish.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/release-drafter-verify-pr-labels.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/release-drafter.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/release-notes.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/release-pr.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/security.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/test-trigger.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/test.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/tutorial-tests-trigger.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.github/workflows/tutorial-tests.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.gitignore +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.pre-commit-config.yaml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/.prettierignore +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/AUTHORS.rst +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/CONTRIBUTING.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/LICENSE +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/codecov.yml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/assemblies/gradient_layer.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/assemblies/multilayer.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/assemblies/repeating_multilayer.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/assemblies/surfactant_layer.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/bayesian.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/calculators.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/constraints.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/data.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/layer.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/layer_area_per_molecule.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/layer_magnetism.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/material.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/material_density.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/material_mixture.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/elements/material_solvated.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/fitting.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/limits.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/model.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/orso.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/plot.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/project.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/sample.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/api-reference/summary.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/assets/images/favicon.png +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/assets/images/logo_dark.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/assets/images/logo_light.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/assets/javascripts/extra.js +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/assets/javascripts/mathjax.js +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/assets/stylesheets/extra.css +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/index.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/introduction/index.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/advancedfitting/example.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/advancedfitting/multiple.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/basic/assemblies_library.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/basic/layer_library.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/basic/material_library.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/d70d2o.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/dspc.png +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/eq_monolayer.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/eq_solvated.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/example.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/material_solvated.ipynb +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/monolayer.png +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/monolayer.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/polymer_film.png +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/polymer_film.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/repeating.png +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/repeating.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/repeating_layers.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/fitting/simple_fitting.ipynb +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/mod_pointwise_two_layer_sample_dq-0.0.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/mod_pointwise_two_layer_sample_dq-1.0.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/mod_pointwise_two_layer_sample_dq-10.0.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/resolution_functions.ipynb +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/two_layers.png +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/docs/tutorials/simulation/two_layers.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/includes/abbreviations.md +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/overrides/.icons/app.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/overrides/.icons/easyreflectometry.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/overrides/.icons/easyscience.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/overrides/.icons/google-colab.svg +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/overrides/main.html +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/docs/overrides/partials/logo.html +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/notebooks/reflectivity_geomgrid.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/package-lock.json +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/package.json +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/pixi.toml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/prettierrc.toml +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/analysis/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/calculators/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/calculators/polarization.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/limits.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/main.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/model/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/base_assembly.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/gradient_layer.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/assemblies/repeating_multilayer.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/collections/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/layers/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/layers/layer_magnetism.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/materials/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/sample/elements/materials/material.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/special/parsing.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/src/easyreflectometry/summary/__init__.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/Ni_example.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/amor_reduced_iofq.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/example.orb +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/example.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/fwhm_sqz.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/nan_sqz.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/nm_units.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/partial_nan_sqz.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/polarized_2ch.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/ref_concat_1.txt +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/ref_five_col.txt +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/ref_zero_var.txt +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/test_example1.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/test_example1.txt +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/test_example2.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/test_example3.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/_static/test_example4.ort +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/data/test_data_store.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/data/test_polarized.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/functional/test_dummy.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/integration/fitting/test_dummy.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/integration/scipp-analysis/test_dummy.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/model/test_resolution_functions.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/package_test.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/special/test_calculations.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/test_topmost_nesting.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tests/unit/test_dummy.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tools/license_headers.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tools/update_docs_assets.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/tools/update_github_labels.py +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/vscode-template/README.rst +0 -0
- {easyreflectometry-1.8.0 → easyreflectometry-2.0.0}/vscode-template/settings.json +0 -0
|
@@ -174,7 +174,6 @@ tox
|
|
|
174
174
|
```
|
|
175
175
|
src/easyreflectometry/ # Main package source code
|
|
176
176
|
├── calculators/ # Calculator implementations (refnx, refl1d)
|
|
177
|
-
│ └── bornagain/ # BornAgain calculator (not yet functional)
|
|
178
177
|
├── model/ # Reflectometry models
|
|
179
178
|
├── sample/ # Sample structures and materials
|
|
180
179
|
├── special/ # Special calculations and parsing
|
|
@@ -47,6 +47,14 @@ jobs:
|
|
|
47
47
|
- name: Init pixi project
|
|
48
48
|
run: pixi init easyreflectometry
|
|
49
49
|
|
|
50
|
+
# A fresh pixi project assumes macOS 13.0 on osx-arm64, which rejects
|
|
51
|
+
# scipp's macosx_14_0 wheels and forces a failing source build. Match
|
|
52
|
+
# the macOS minimum set in the repository's pixi.toml.
|
|
53
|
+
- name: Require macOS 14 (matches scipp wheels)
|
|
54
|
+
working-directory: easyreflectometry
|
|
55
|
+
shell: bash
|
|
56
|
+
run: printf '\n[system-requirements]\nmacos = "14.0"\n' >> pixi.toml
|
|
57
|
+
|
|
50
58
|
- name: Add Python 3.13 from Conda
|
|
51
59
|
working-directory: easyreflectometry
|
|
52
60
|
run: pixi add "python=3.13"
|
|
@@ -0,0 +1,311 @@
|
|
|
1
|
+
# Architecture
|
|
2
|
+
|
|
3
|
+
**Analysis Date:** 2026-10-15 **Branch analysed:** `develop` @
|
|
4
|
+
`66adaf54` **Revised:** 2026-11-08 for the easyscience 3 port
|
|
5
|
+
(`core-3.0`)
|
|
6
|
+
|
|
7
|
+
## Pattern Overview
|
|
8
|
+
|
|
9
|
+
**Overall:** Layered library with two facades. `Project` is the
|
|
10
|
+
stateful, GUI-oriented facade (experiments, models, persistence,
|
|
11
|
+
constraints, contrasts); `MultiFitter` is the fitting facade that turns
|
|
12
|
+
datasets into a `PreparedFit` and delegates execution to EasyScience's
|
|
13
|
+
`MultiFitter` / `Sampler`, which drive LMFit, BUMPS or DFO. Reflectivity
|
|
14
|
+
is computed by pluggable engine wrappers (refnx, refl1d) behind
|
|
15
|
+
`CalculatorFactory`.
|
|
16
|
+
|
|
17
|
+
**Key Characteristics:**
|
|
18
|
+
|
|
19
|
+
- Prepare / execute / record split: `Project.prepare_fit` →
|
|
20
|
+
`PreparedFit` (immutable snapshot of settings, arrays, bound fit
|
|
21
|
+
functions, configured core fitter) → `execute()` or a GUI worker →
|
|
22
|
+
`Project.record_fit` → `FitRun`
|
|
23
|
+
- Resolution follows the dataset, not the calculator: each
|
|
24
|
+
`DataSet1D.resolution_function` is bound into its fit function;
|
|
25
|
+
`CalculatorBase` keeps one resolution per model id
|
|
26
|
+
- Parameters are EasyScience `Parameter`s; all coupling (conformal
|
|
27
|
+
thickness, contrasts, user constraints, sum constraints) is expressed
|
|
28
|
+
as parameter dependencies and persisted by structural path
|
|
29
|
+
- Inequality constraints are enforced by patching the BUMPS driver in
|
|
30
|
+
the core's namespace, because the core offers no hook
|
|
31
|
+
- Shared objects (one material in two layers, one assembly in two
|
|
32
|
+
models) serialize once through `sample/references.py`
|
|
33
|
+
(`file_format` 4)
|
|
34
|
+
|
|
35
|
+
## Layers
|
|
36
|
+
|
|
37
|
+
**Project Facade:**
|
|
38
|
+
|
|
39
|
+
- Purpose: Hold the session state an application needs and expose
|
|
40
|
+
index-based accessors for plots and tables
|
|
41
|
+
- Location: `src/easyreflectometry/project.py` (`Project`,
|
|
42
|
+
`ModelRemoval`, spin-asymmetry helpers)
|
|
43
|
+
- Contains: experiments `Dict[int, DataSet1D | PolarizedDataSet]`,
|
|
44
|
+
`ModelCollection`, `MaterialCollection` palette, `FitSettings`,
|
|
45
|
+
inequality specs, link records, `last_fit`, load/save, ORSO import,
|
|
46
|
+
contrasts, polarized loading, `spin_asymmetry_for_experiment_at_index`
|
|
47
|
+
- Depends on: every other package in the library
|
|
48
|
+
- Used by: EasyReflectometry GUI (ERA app), tutorials, `Summary`
|
|
49
|
+
|
|
50
|
+
**Fitting Layer:**
|
|
51
|
+
|
|
52
|
+
- Purpose: Zero-variance handling, per-dataset resolution binding,
|
|
53
|
+
settings snapshot, constraint enforcement, goodness-of-fit metrics
|
|
54
|
+
- Location: `src/easyreflectometry/fitting.py`, `fit_settings.py`,
|
|
55
|
+
`_bumps_constraints.py`, `inequality_constraints.py`
|
|
56
|
+
- Contains: `MultiFitter` (`fit`, `fit_single_data_set_1d`,
|
|
57
|
+
`fit_polarized`, `for_experiments`, `prepare`, `mcmc_sample`,
|
|
58
|
+
`switch_minimizer`, chi-square properties), `PreparedFit`, `FitRun`,
|
|
59
|
+
`_ConstrainedEasyScienceMultiFitter`, `FitSettings`,
|
|
60
|
+
`MinimizerOption`, `InequalitySpec`, `build_constraints_factory`
|
|
61
|
+
- Depends on: `easyscience.fitting` (`MultiFitter`, `Sampler`,
|
|
62
|
+
`FitResults`), `easyscience.Minimizers`, `asteval`, `scipp`
|
|
63
|
+
|
|
64
|
+
**Bayesian Analysis Layer:**
|
|
65
|
+
|
|
66
|
+
- Purpose: Post-hoc posterior analysis and chain persistence
|
|
67
|
+
- Location: `src/easyreflectometry/analysis/bayesian.py`
|
|
68
|
+
- Contains: `PosteriorResults` (summary, corner, trace, distribution,
|
|
69
|
+
credible*interval, gelman_rubin, save), `posterior_predictive*\*`,
|
|
70
|
+
`save_posterior`/`load_posterior` with fingerprinted sidecar
|
|
71
|
+
- Depends on: plotly (required), arviz (optional), numpy
|
|
72
|
+
|
|
73
|
+
**Model / Sample Layer:**
|
|
74
|
+
|
|
75
|
+
- Purpose: Physical description of the sample and measurement
|
|
76
|
+
- Location: `src/easyreflectometry/model/`,
|
|
77
|
+
`src/easyreflectometry/sample/`
|
|
78
|
+
- Contains: `Model` (scale, background, resolution, `total_thickness`
|
|
79
|
+
derived parameter), `Sample` and assemblies/layers/materials,
|
|
80
|
+
`LayerMagnetism`, `MaterialDensity.sld_coupled`, `volume_fraction`,
|
|
81
|
+
`paths`, `references`
|
|
82
|
+
- Depends on: `easyscience.base_classes`, `easyscience.variable`,
|
|
83
|
+
periodictable (via `special/`)
|
|
84
|
+
|
|
85
|
+
**Constraints and Contrasts Layer:**
|
|
86
|
+
|
|
87
|
+
- Purpose: User-level coupling between parameters and between models
|
|
88
|
+
- Location: `src/easyreflectometry/constraints.py`, `contrasts.py`
|
|
89
|
+
- Contains: `constrain*`, `derived_parameter`, `clamp_sum_partners`;
|
|
90
|
+
`derive_contrast`, `ReplaceMaterial`, `ReplaceFormula`, `plan_link` /
|
|
91
|
+
`LinkPlan` / `LinkRecord`, `tie_corresponding`, `snapshot` / `restore`
|
|
92
|
+
- Depends on: EasyScience dependency mechanism; `USER_CONSTRAINT_FLAG`
|
|
93
|
+
marks constraints the project should persist
|
|
94
|
+
|
|
95
|
+
**Data Layer:**
|
|
96
|
+
|
|
97
|
+
- Purpose: Load, merge and describe measured data
|
|
98
|
+
- Location: `src/easyreflectometry/data/`, `orso_utils.py`
|
|
99
|
+
- Contains: `DataSet1D`, `PolarizedDataSet`, `load`, `load_as_dataset`,
|
|
100
|
+
`merge_datasets`, `resolution_from_dataset`, channel detection, ORSO
|
|
101
|
+
read/write and model-language conversion
|
|
102
|
+
|
|
103
|
+
**Calculator Layer:**
|
|
104
|
+
|
|
105
|
+
- Purpose: Pluggable reflectivity engines
|
|
106
|
+
- Location: `src/easyreflectometry/calculators/`
|
|
107
|
+
- Contains: `CalculatorBase` (thin layer over the wrapper: model
|
|
108
|
+
membership, resolution, magnetism flag and channel),
|
|
109
|
+
`StatelessWrapperBase` (one entry per served model, held weakly, with
|
|
110
|
+
its resolution and cache; magnetism reconciliation; abstract engine
|
|
111
|
+
API incl. `calculate_channel`, `calculate_polarized`,
|
|
112
|
+
`magnetic_sld_profile`), `CalculatorFactory` (`fit_func`,
|
|
113
|
+
`fit_func_for(channel, resolution_function)`), `PolarizationChannel`
|
|
114
|
+
- Stateless: each evaluation builds the engine structure from the model
|
|
115
|
+
and drops it again; no value is copied into a backend, so model edits
|
|
116
|
+
need no mirroring
|
|
117
|
+
- Implementations: `refnx/` (unpolarized), `refl1d/` (magnetism,
|
|
118
|
+
polarized probes with a per-model cache, oversampled resolution)
|
|
119
|
+
|
|
120
|
+
**EasyScience Core (external, `>=3.0.2,<4`):**
|
|
121
|
+
|
|
122
|
+
- `Fitter` / `MultiFitter` (reshaping of multi-dataset arrays, minimizer
|
|
123
|
+
dispatch), `Sampler` / `SamplingResults` (DREAM chain, `extend`,
|
|
124
|
+
`save`, `load_state`), minimizer wrappers, `Minimizers`
|
|
125
|
+
|
|
126
|
+
## Data Flow
|
|
127
|
+
|
|
128
|
+
**Classical Fit (project path):**
|
|
129
|
+
|
|
130
|
+
1. GUI/user calls
|
|
131
|
+
`Project.prepare_fit(experiments=None, objective=None, skip_invalid=False)`
|
|
132
|
+
2. `_fit_scope` selects experiments with `include_in_fit` and a model;
|
|
133
|
+
`_fitter_for` builds `MultiFitter.for_experiments(...)` with a deep
|
|
134
|
+
copy of `fit_settings` and a weak `constraints_factory_provider`
|
|
135
|
+
3. `MultiFitter.prepare` applies the objective (`hybrid` default:
|
|
136
|
+
Mighell for zero-variance points, WLS otherwise), binds each
|
|
137
|
+
dataset's fit function to its own resolution
|
|
138
|
+
(`_resolution_for_fitted_points` for `legacy_mask`), collects
|
|
139
|
+
`added_roots` (free parameters of left-out models that fitted
|
|
140
|
+
parameters follow), builds the core fitter via `_core_fitter_for` +
|
|
141
|
+
`FitSettings.configure`, checks preconditions (finite bounds for
|
|
142
|
+
engines that need them, warns on starts at bounds)
|
|
143
|
+
4. Caller runs `prepared.execute(**kwargs)` or `core_fitter.fit(...)` in
|
|
144
|
+
a thread; the `_ConstrainedEasyScienceMultiFitter.fit` property wraps
|
|
145
|
+
the call in `_bumps_constraints.applied(factory)`
|
|
146
|
+
5. `Project.record_fit(prepared, results, status)` computes classical
|
|
147
|
+
metrics from the measured arrays (`prepared.finalize`), stores a
|
|
148
|
+
`FitRun` on `last_fit` and pushes results to `project.fitter`
|
|
149
|
+
|
|
150
|
+
**Classical Fit (library path):**
|
|
151
|
+
|
|
152
|
+
1. `MultiFitter(model, ...)` and `fitter.fit(data: sc.DataGroup)`
|
|
153
|
+
2. `_datagroup_inputs` pairs curve `Qz_<n>` with model/dataset `n` in
|
|
154
|
+
numeric order
|
|
155
|
+
3. `prepare` → `execute` → results written back into the `DataGroup`
|
|
156
|
+
(model curves, SLD profiles, pooled chi-square)
|
|
157
|
+
|
|
158
|
+
**Polarized Fit:**
|
|
159
|
+
|
|
160
|
+
- `fit_polarized(polarized_dataset)` or `for_experiments([...])`: one
|
|
161
|
+
fit function per spin channel, all against one model, each bound to
|
|
162
|
+
its channel via `CalculatorFactory.fit_func_for(channel, resolution)`.
|
|
163
|
+
Structural parameters shared, magnetic parameters constrained by all
|
|
164
|
+
channels.
|
|
165
|
+
|
|
166
|
+
**Bayesian MCMC:**
|
|
167
|
+
|
|
168
|
+
1. `Project.prepare_sample()` → `for_experiments` fitter with settings
|
|
169
|
+
snapshot; or `fitter.mcmc_sample(data)` directly
|
|
170
|
+
2. `mcmc_sample` requires a BUMPS minimizer
|
|
171
|
+
(`minimizer.package == 'bumps'`), refuses all-zero variances unless
|
|
172
|
+
`objective='mighell'`, calls `prepare(action='sampling')`
|
|
173
|
+
3. Builds `Sampler(core_fitter, x, y, weights)` inside the constraints
|
|
174
|
+
context, patches `sampler.extend` to re-enter it, keeps the sampler
|
|
175
|
+
on `fitter.sampler`, runs
|
|
176
|
+
`sampler.sample(samples, burn, thin, population, sampler_kwargs, progress_callback, abort_test)`
|
|
177
|
+
4. Returns `{'draws', 'param_names', 'state', 'logp'}`; user wraps in
|
|
178
|
+
`PosteriorResults` or calls `save_posterior`
|
|
179
|
+
|
|
180
|
+
**Persistence:**
|
|
181
|
+
|
|
182
|
+
- `Project.as_dict` writes models, materials in order, experiments by
|
|
183
|
+
model index with per-dataset resolution, fit settings, inequality
|
|
184
|
+
specs, user-constraint records by path, contrast provenance, links.
|
|
185
|
+
`references.writing()` replaces repeated objects with `@ref`. Always
|
|
186
|
+
`file_format` 4.
|
|
187
|
+
- `from_dict` refuses anything but format 4 first (files from 1.x carry
|
|
188
|
+
2, 3 or none), then rebuilds shared objects (`references.reading`),
|
|
189
|
+
restores constraints by path, reports problems in `load_report`.
|
|
190
|
+
|
|
191
|
+
**State Management:**
|
|
192
|
+
|
|
193
|
+
- `Project._sync_parameter_states` disables physically meaningless
|
|
194
|
+
parameters (superphase thickness/roughness, subphase thickness) and
|
|
195
|
+
applies default limits to enabled parameters created from project
|
|
196
|
+
defaults
|
|
197
|
+
- `PreparedFit` owns copies of the measured arrays; model parameters and
|
|
198
|
+
constraints stay live because they are what the fit changes
|
|
199
|
+
- Parameter values saved/restored around posterior predictive evaluation
|
|
200
|
+
(`_save_parameter_state` / `_restore_parameter_state`)
|
|
201
|
+
|
|
202
|
+
## Key Abstractions
|
|
203
|
+
|
|
204
|
+
**`PreparedFit` (fitting.py):** Snapshot pattern. Everything a run
|
|
205
|
+
needs, fixed at prepare time; owns copies of measured arrays, the
|
|
206
|
+
configured core fitter and both full-resolution (`curve_funcs`) and
|
|
207
|
+
fitted-point (`fit_funcs`) evaluators.
|
|
208
|
+
|
|
209
|
+
**`FitRun` (fitting.py):** Frozen record of a run (status, minimizer,
|
|
210
|
+
objective, inputs by experiment key, pooled and per-dataset statistics).
|
|
211
|
+
Outlives model changes; `remapped()` follows experiment re-keying.
|
|
212
|
+
|
|
213
|
+
**`FitSettings` (fit_settings.py):** Serializable configuration
|
|
214
|
+
(minimizer, mode, objective, tolerance, budget, per-minimizer options
|
|
215
|
+
validated by `MinimizerOption` schemas). `configure(fitter)` and
|
|
216
|
+
`engine_kwargs()` are the two consumers.
|
|
217
|
+
|
|
218
|
+
**`_ConstrainedEasyScienceMultiFitter` / `_bumps_constraints`:**
|
|
219
|
+
Property override plus monkeypatch. The subclass overrides the core's
|
|
220
|
+
read-only `fit` property so the raw `easy_science_multi_fitter.fit(...)`
|
|
221
|
+
path resolves the owner's constraints provider in the calling thread and
|
|
222
|
+
consumes an explicit `constraints_factory` keyword (the core would
|
|
223
|
+
swallow it via `**kwargs`). `install()`, run at import of
|
|
224
|
+
`_bumps_constraints`, wraps `minimizer_bumps.FitDriver` so each
|
|
225
|
+
`FitProblem` gets `constraints = factory(pars)` before the driver runs.
|
|
226
|
+
Non-BUMPS engines raise `NON_BUMPS_ERROR`.
|
|
227
|
+
|
|
228
|
+
**`InequalitySpec` (inequality_constraints.py):** Declarative
|
|
229
|
+
`lhs op rhs` over parameter paths, evaluated with asteval in a
|
|
230
|
+
restricted symbol table; `build_constraints_factory` returns BUMPS
|
|
231
|
+
penalty callables.
|
|
232
|
+
|
|
233
|
+
**Contrasts (`contrasts.py`):** A contrast is an ordinary `Model`
|
|
234
|
+
sharing structure with a reference by identity or by `constrain_equal`.
|
|
235
|
+
`derive_contrast` copies touched assemblies, shares the rest and ties
|
|
236
|
+
parameters; `plan_link` / `apply_link` / `unlink` manage equalities
|
|
237
|
+
between two models of one layout with full state snapshots.
|
|
238
|
+
|
|
239
|
+
**`CalculatorBase` / `StatelessWrapperBase` / `CalculatorFactory`:**
|
|
240
|
+
Template + Adapter + Factory. Explicit model membership
|
|
241
|
+
(`register_model` / `discard_model` / `reset_storage`), per-model
|
|
242
|
+
resolution, a magnetism flag reconciled with the served models,
|
|
243
|
+
channel-bound fit functions.
|
|
244
|
+
|
|
245
|
+
**`references` (sample/references.py):** Context-managed shared-object
|
|
246
|
+
serialization (`writing()`, `reading(report)`, `suspended()`), with
|
|
247
|
+
`prune`, `rebuild` and `register` for load-time reconciliation.
|
|
248
|
+
|
|
249
|
+
**`paths.walk` (sample/paths.py):** Structural addressing of every
|
|
250
|
+
parameter; the stable identity used by constraints and persistence
|
|
251
|
+
instead of EasyScience unique names.
|
|
252
|
+
|
|
253
|
+
## Entry Points
|
|
254
|
+
|
|
255
|
+
- `Project.prepare_fit` / `prepare_sample` / `record_fit` - GUI fit and
|
|
256
|
+
sampling
|
|
257
|
+
- `Project.load_experiment_from_files`, `load_polarized_experiment*`,
|
|
258
|
+
`load_orso_file`, `save_as_json`, `load_from_json`
|
|
259
|
+
- `Project.add_contrast`, `plan_link`, `apply_link`, `detach`
|
|
260
|
+
- `MultiFitter.fit`, `fit_polarized`, `mcmc_sample`
|
|
261
|
+
- `Summary(project).save_html_summary` / `save_pdf_summary`
|
|
262
|
+
- `python -m easyreflectometry.main` prints available calculators (no
|
|
263
|
+
console script registered)
|
|
264
|
+
|
|
265
|
+
## Error Handling
|
|
266
|
+
|
|
267
|
+
**Strategy:** Fail loudly at preparation time; warn for recoverable data
|
|
268
|
+
issues; refuse silent degradation.
|
|
269
|
+
|
|
270
|
+
**Patterns:**
|
|
271
|
+
|
|
272
|
+
- `FitScopeError` (nothing to fit / experiment without model),
|
|
273
|
+
`FitPreconditionError` (unusable bounds for the engine), `ValueError`
|
|
274
|
+
for invalid settings or empty datasets after masking
|
|
275
|
+
- `RuntimeError` when sampling without a BUMPS minimizer
|
|
276
|
+
- `ValueError(NON_BUMPS_ERROR)` when inequality constraints meet a
|
|
277
|
+
non-BUMPS engine
|
|
278
|
+
- `UnitError` (subclass of `ValueError`) for inequality unit mismatches
|
|
279
|
+
- `FileExistsError` / `FileNotFoundError` / `OSError` / `ValueError`
|
|
280
|
+
from project persistence (no printing)
|
|
281
|
+
- `warnings.warn` for zero-variance handling, ORSO column problems,
|
|
282
|
+
negative volume fractions, unreadable dependencies on load; load
|
|
283
|
+
issues also collected in `Project.load_report`
|
|
284
|
+
- `try/finally` restores parameter state around posterior predictive;
|
|
285
|
+
`Project.save_as_json` writes atomically
|
|
286
|
+
|
|
287
|
+
## Cross-Cutting Concerns
|
|
288
|
+
|
|
289
|
+
**Logging:** `logging.getLogger(__name__)` in a handful of modules; no
|
|
290
|
+
handler configured. `warnings` is the main user-facing channel.
|
|
291
|
+
|
|
292
|
+
**Validation:** `FitSettings.validate`, `MinimizerOption.validate`,
|
|
293
|
+
`InequalitySpec.validate_syntax` + `check_units`, ORSO column
|
|
294
|
+
validation, `_check_fit_preconditions`.
|
|
295
|
+
|
|
296
|
+
**Threading:** The constraints factory lives in a `contextvars`
|
|
297
|
+
variable, resolved in the calling thread; `PreparedFit` owns array
|
|
298
|
+
copies so a worker thread is isolated from later edits. Resolution for
|
|
299
|
+
one-off curves is bound to the evaluator rather than swapped on the
|
|
300
|
+
model to avoid racing a running fit.
|
|
301
|
+
|
|
302
|
+
**Serialization:** EasyScience serializer plus library extensions:
|
|
303
|
+
`references` for shared objects, structural paths for constraints, per
|
|
304
|
+
dataset resolution entries, a `file_format` check. Posterior chains have
|
|
305
|
+
their own sidecar format.
|
|
306
|
+
|
|
307
|
+
**Authentication:** Not applicable.
|
|
308
|
+
|
|
309
|
+
---
|
|
310
|
+
|
|
311
|
+
_Architecture analysis: 2026-10-15 (develop @ 66adaf54)_
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
# Codebase Concerns
|
|
2
|
+
|
|
3
|
+
**Analysis Date:** 2026-10-15 **Branch analysed:** `develop` @
|
|
4
|
+
`66adaf54` **Revised:** 2026-11-08 for the easyscience 3 port
|
|
5
|
+
(`core-3.0`)
|
|
6
|
+
|
|
7
|
+
## Summary
|
|
8
|
+
|
|
9
|
+
`develop` is 51 commits past `v1.7.0` and carries the unreleased 1.9
|
|
10
|
+
work (contrasts, shared-object persistence, per-dataset resolution,
|
|
11
|
+
prepare/record fit split, volume fractions, inequality constraints on
|
|
12
|
+
BUMPS). The earlier concerns about the `bayesian_mp` branch
|
|
13
|
+
(multiprocessing, chains/seed, git-branch core pin) are gone: the core
|
|
14
|
+
dependency is a PyPI range, `n_workers`/`chains`/`seed` and
|
|
15
|
+
`CalculatorFactory.__reduce__` no longer exist in the library, and MCMC
|
|
16
|
+
goes through the core `Sampler`. The concerns below are what remains.
|
|
17
|
+
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
## 1. Coupling to EasyScience internals
|
|
21
|
+
|
|
22
|
+
### Monkeypatch of `minimizer_bumps.FitDriver`
|
|
23
|
+
|
|
24
|
+
**Issue:** Inequality constraints work only because
|
|
25
|
+
`_bumps_constraints.install()` rebinds the module-level `FitDriver` name
|
|
26
|
+
inside the core's `minimizer_bumps` namespace and inspects
|
|
27
|
+
`problem.models[0].pars`. Any core refactor (importing `FitDriver`
|
|
28
|
+
differently, building the problem elsewhere, renaming `.pars`) silently
|
|
29
|
+
disables every inequality constraint; the shim passes untouched problems
|
|
30
|
+
through without complaint.
|
|
31
|
+
|
|
32
|
+
**Files:** `src/easyreflectometry/_bumps_constraints.py`,
|
|
33
|
+
`src/easyreflectometry/fitting.py` (`_constraints`,
|
|
34
|
+
`_keep_constraints_on_extend`),
|
|
35
|
+
`tests/unit/test_bumps_constraints_shim.py`
|
|
36
|
+
|
|
37
|
+
**Impact:** High. Physics silently dropped on a core upgrade.
|
|
38
|
+
|
|
39
|
+
**Fix approach:** Keep the shim test that asserts penalties reach the
|
|
40
|
+
problem on both `fit` and `mcmc_sample`; add a start-up assertion that
|
|
41
|
+
the patched attribute exists; pursue an upstream hook
|
|
42
|
+
(`constraints=`/callback) in corelib and keep the `<4` pin until it
|
|
43
|
+
lands. Since core 3 the shim patches both `minimizer_bumps` and
|
|
44
|
+
`samplers.sampler_bumps`, which imports its own `FitDriver`.
|
|
45
|
+
|
|
46
|
+
### Core API surface (`easyscience>=3.0.2,<4`)
|
|
47
|
+
|
|
48
|
+
**Issue:** Besides the public `Parameter`, `DescriptorNumber`,
|
|
49
|
+
`global_object`, `Minimizers`, `ModelBase`/`EasyList`/`EasyBase`,
|
|
50
|
+
`SerializerBase`, `MultiFitter`, `Sampler` and `FitResults`, the library
|
|
51
|
+
depends on module paths a core refactor could move:
|
|
52
|
+
`fitting.engine_base.PARAMETER_PREFIX`, the module-level `FitDriver` in
|
|
53
|
+
`fitting.minimizers.minimizer_bumps` and
|
|
54
|
+
`fitting.samplers.sampler_bumps`, and
|
|
55
|
+
`variable.descriptor_base.DescriptorBase`.
|
|
56
|
+
|
|
57
|
+
**Files:** `analysis/bayesian.py`, `inequality_constraints.py`,
|
|
58
|
+
`_bumps_constraints.py`, `fitting.py`, `sample/base_core.py`
|
|
59
|
+
|
|
60
|
+
**Impact:** Medium. A core minor release can break them.
|
|
61
|
+
|
|
62
|
+
**Fix approach:** Keep the shim tests; check this list on every core
|
|
63
|
+
upgrade.
|
|
64
|
+
|
|
65
|
+
### Private core imports
|
|
66
|
+
|
|
67
|
+
**Issue:** `PARAMETER_PREFIX` from `fitting.engine_base` and
|
|
68
|
+
`DescriptorBase` from `variable.descriptor_base` are not exported at a
|
|
69
|
+
package level.
|
|
70
|
+
|
|
71
|
+
**Files:** `analysis/bayesian.py`, `inequality_constraints.py`,
|
|
72
|
+
`sample/base_core.py`
|
|
73
|
+
|
|
74
|
+
**Impact:** Medium.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## 2. Size and complexity hotspots
|
|
79
|
+
|
|
80
|
+
### `project.py` is a 3211-line god object
|
|
81
|
+
|
|
82
|
+
**Issue:** `Project` mixes session state, persistence (two file formats,
|
|
83
|
+
reference resolution, constraint records), fit orchestration, contrasts
|
|
84
|
+
and links, ORSO import, polarized loading, spin asymmetry, derived data
|
|
85
|
+
for plots, and GUI index bookkeeping. Most behaviours are tested through
|
|
86
|
+
it, so refactors are expensive.
|
|
87
|
+
|
|
88
|
+
**Files:** `src/easyreflectometry/project.py`
|
|
89
|
+
|
|
90
|
+
**Impact:** Medium-High for maintainability; the radon dashboard
|
|
91
|
+
publishes the complexity.
|
|
92
|
+
|
|
93
|
+
**Fix approach:** Extract cohesive modules that `Project` delegates to:
|
|
94
|
+
`project_io.py` (as_dict/from_dict helpers, `_needs_new_format`),
|
|
95
|
+
`project_experiments.py` (loading, polarized, merge),
|
|
96
|
+
`spin_asymmetry.py` (`_spin_asymmetry`, `_unwrapped_angle`, channel
|
|
97
|
+
helpers). Keep `Project` as the facade.
|
|
98
|
+
|
|
99
|
+
### `fitting.py` (1428 lines) and `analysis/bayesian.py` (1412 lines)
|
|
100
|
+
|
|
101
|
+
**Issue:** `bayesian.py` is mostly plotly styling constants and
|
|
102
|
+
hand-rolled contour/density code for corner plots; `fitting.py` combines
|
|
103
|
+
data preparation, settings, constraints wiring and five public fit
|
|
104
|
+
paths.
|
|
105
|
+
|
|
106
|
+
**Impact:** Low-Medium.
|
|
107
|
+
|
|
108
|
+
**Fix approach:** Split `bayesian.py` into `posterior.py` (results,
|
|
109
|
+
persistence, predictive) and `posterior_plots.py`; consider moving
|
|
110
|
+
`PreparedFit`/`FitRun` to `fit_run.py`.
|
|
111
|
+
|
|
112
|
+
### `Project.__init__` and `from_dict` ordering
|
|
113
|
+
|
|
114
|
+
**Issue:** Loading depends on a specific order (materials → models →
|
|
115
|
+
experiments → constraints → contrasts → links) and on `references`
|
|
116
|
+
contexts being active. Several `_from_dict_*` helpers append to a shared
|
|
117
|
+
`report` list. Easy to break when adding a persisted field.
|
|
118
|
+
|
|
119
|
+
**Files:** `project.py` lines ~2845-3000
|
|
120
|
+
|
|
121
|
+
**Impact:** Medium.
|
|
122
|
+
|
|
123
|
+
---
|
|
124
|
+
|
|
125
|
+
## 3. Persistence format risks
|
|
126
|
+
|
|
127
|
+
### Project files from 1.x no longer load
|
|
128
|
+
|
|
129
|
+
**Issue:** By design: 2.0 writes and reads `file_format` 4 only, and
|
|
130
|
+
refuses files from 1.x (format 2, 3 or none) with a message; there is no
|
|
131
|
+
converter. 1.x refuses format 4 in turn.
|
|
132
|
+
|
|
133
|
+
**Files:** `project.py` (`FILE_FORMAT`, `from_dict`)
|
|
134
|
+
|
|
135
|
+
**Impact:** Medium. Users re-create such projects or stay on 1.9; in the
|
|
136
|
+
CHANGELOG.
|
|
137
|
+
|
|
138
|
+
### Constraint records keyed by structural path
|
|
139
|
+
|
|
140
|
+
**Issue:** `_user_constraints` refuses (raises `ValueError`) when a
|
|
141
|
+
constrained parameter is not reachable from the models (e.g. a
|
|
142
|
+
constraint on a palette material not used by any model). Saving a
|
|
143
|
+
project then fails late, at save time.
|
|
144
|
+
|
|
145
|
+
**Files:** `project.py` (`_constraint_candidates`, `_constraint_record`)
|
|
146
|
+
|
|
147
|
+
**Impact:** Medium. Surfaced as an exception, which is the intended
|
|
148
|
+
behaviour, but the GUI must handle it.
|
|
149
|
+
|
|
150
|
+
### `derived_parameter` is session-only
|
|
151
|
+
|
|
152
|
+
**Issue:** Documented, but a user who builds a derived parameter and
|
|
153
|
+
saves loses it silently.
|
|
154
|
+
|
|
155
|
+
**Files:** `constraints.py`
|
|
156
|
+
|
|
157
|
+
**Impact:** Low.
|
|
158
|
+
|
|
159
|
+
---
|
|
160
|
+
|
|
161
|
+
## 4. Testing gaps
|
|
162
|
+
|
|
163
|
+
### Thin integration/functional layers
|
|
164
|
+
|
|
165
|
+
**Issue:** `tests/functional/test_dummy.py` asserts `2 + 2 == 4` and is
|
|
166
|
+
the whole functional suite; `tests/integration/fitting/test_dummy.py`
|
|
167
|
+
and `tests/integration/scipp-analysis/test_dummy.py` are `slow`-marked
|
|
168
|
+
placeholders. Real integration coverage is `integration/convergence/`,
|
|
169
|
+
`test_cross_engine_resolution.py`, `test_ort_file_fitting.py`.
|
|
170
|
+
|
|
171
|
+
**Impact:** Medium. CI jobs named "functional tests" pass vacuously.
|
|
172
|
+
|
|
173
|
+
**Fix approach:** Either delete the placeholders or move the end-to-end
|
|
174
|
+
project tests (`test_project.py` fit paths, `test_polarized_fitting.py`
|
|
175
|
+
slow cases) under `integration/`.
|
|
176
|
+
|
|
177
|
+
### Sampler continuation and persistence
|
|
178
|
+
|
|
179
|
+
**Issue:** `fitter.sampler.extend()` after constraints, and
|
|
180
|
+
`save_posterior`/`load_posterior` fingerprint mismatch handling, have
|
|
181
|
+
limited coverage (`TestMCMCSample*` in `test_fitting.py`,
|
|
182
|
+
`test_bayesian.py`); all real DREAM runs are `slow` and only run on
|
|
183
|
+
PRs/develop.
|
|
184
|
+
|
|
185
|
+
**Impact:** Medium.
|
|
186
|
+
|
|
187
|
+
### Default `pytest` skips `slow`
|
|
188
|
+
|
|
189
|
+
**Issue:** `addopts = -m "not slow"` means local runs and some CI pushes
|
|
190
|
+
never execute a real minimizer or sampler. Regressions in engine wiring
|
|
191
|
+
surface only on PR CI.
|
|
192
|
+
|
|
193
|
+
**Impact:** Medium; deliberate trade-off (see `pyproject.toml` comment).
|
|
194
|
+
34 of 1410 tests are `slow`.
|
|
195
|
+
|
|
196
|
+
### refnx-only assumptions
|
|
197
|
+
|
|
198
|
+
**Issue:** Several fits and convergence references are refnx-based;
|
|
199
|
+
refl1d is exercised mainly through polarized/magnetism tests and
|
|
200
|
+
`test_cross_engine_resolution.py`.
|
|
201
|
+
|
|
202
|
+
**Impact:** Low-Medium.
|
|
203
|
+
|
|
204
|
+
---
|
|
205
|
+
|
|
206
|
+
## 5. Repository hygiene
|
|
207
|
+
|
|
208
|
+
### Duplicate CI matrices
|
|
209
|
+
|
|
210
|
+
**Issue:** `python-ci.yml` and `python-package.yml` (pip-based, run on
|
|
211
|
+
every push/PR) duplicate `test.yml`; `python-publish.yml` duplicates
|
|
212
|
+
`pypi-publish.yml`.
|
|
213
|
+
|
|
214
|
+
**Impact:** Low-Medium (CI minutes, confusing failures).
|
|
215
|
+
|
|
216
|
+
### Untracked planning/notes directories
|
|
217
|
+
|
|
218
|
+
**Issue:** `.planning/`, `.claude/`, `MD/`, `examples/`, `integration/`
|
|
219
|
+
are untracked and not in `.gitignore`, so `git status` is always noisy
|
|
220
|
+
and an accidental `git add .` would commit them.
|
|
221
|
+
|
|
222
|
+
**Impact:** Low.
|
|
223
|
+
|
|
224
|
+
**Fix approach:** Add `.planning/`, `.claude/`, `MD/` to `.gitignore`
|
|
225
|
+
(or a global excludes file); decide whether `examples/` and
|
|
226
|
+
`integration/` belong in the repo.
|
|
227
|
+
|
|
228
|
+
### Docstring tooling disabled
|
|
229
|
+
|
|
230
|
+
**Issue:** `pydoclint` and `format-docstring` are configured with
|
|
231
|
+
`exclude = '\.'` (everything), `interrogate --fail-under=0`. The
|
|
232
|
+
`docstring-lint-check` CI step therefore cannot fail.
|
|
233
|
+
|
|
234
|
+
**Impact:** Low.
|
|
235
|
+
|
|
236
|
+
### `main.py` has no console entry
|
|
237
|
+
|
|
238
|
+
**Issue:** `main()` prints available calculators but `pyproject.toml`
|
|
239
|
+
declares no `[project.scripts]`.
|
|
240
|
+
|
|
241
|
+
**Impact:** Low; remove or register.
|
|
242
|
+
|
|
243
|
+
---
|
|
244
|
+
|
|
245
|
+
## 6. Numerical and physics conventions to keep in view
|
|
246
|
+
|
|
247
|
+
- **SLD unit convention:** values are stored in units of 1e-6 Å^-2 while
|
|
248
|
+
the declared unit is `1/angstrom^2`; EasyScience strips numeric unit
|
|
249
|
+
factors so this cannot be fixed without a breaking migration (issue
|
|
250
|
+
#377 left open on purpose).
|
|
251
|
+
- **Resolution convention:** `SIGMA_TO_FWHM` conversions in
|
|
252
|
+
`resolution_functions.py`, `orso_utils.py` and the refnx wrapper must
|
|
253
|
+
stay consistent; `tests/calculators/test_resolution_conventions.py`
|
|
254
|
+
and `tests/integration/test_cross_engine_resolution.py` pin them.
|
|
255
|
+
- **Weights convention:** core weights are 1/σ (issue #370); keep in
|
|
256
|
+
mind when touching `_prepare_fit_arrays`.
|
|
257
|
+
- **refl1d magnetism angle:** `DEFAULT_THETA_M = 270.0` and
|
|
258
|
+
`GUIDE_FIELD_ANGLE = 270.0`; thetaM=90 is parallel to the guide field
|
|
259
|
+
in refl1d. Past reviews got this backwards.
|
|
260
|
+
- **Mighell objective:** `hybrid` default substitutes Mighell for zero
|
|
261
|
+
variance points; sampling with `mighell` is a bias correction, not a
|
|
262
|
+
likelihood (warned in `mcmc_sample`).
|
|
263
|
+
- **Spin asymmetry heuristics:** `SPIN_ASYMMETRY_SIGNIFICANCE = 3.0`,
|
|
264
|
+
`SPIN_ASYMMETRY_CANCELLATION_FRACTION = 1e-3`,
|
|
265
|
+
`MAGNETIC_MOMENT_FLOOR_FRACTION = 0.01` in `project.py` are tuned
|
|
266
|
+
constants without a documented derivation.
|
|
267
|
+
|
|
268
|
+
---
|
|
269
|
+
|
|
270
|
+
## 7. Work in flight (not on develop)
|
|
271
|
+
|
|
272
|
+
- The easyscience 3 port is on `core-3.0`, in PRs into that branch; it
|
|
273
|
+
is merged into `develop` only once the app no longer installs the
|
|
274
|
+
library from `develop` (the app needs its own port).
|
|
275
|
+
- Uncommitted app-side work for per-dataset resolution (A1-A5) and the
|
|
276
|
+
magnetism/fitting gaps lives in the GUI repository, not here.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
_Concerns audit: 2026-10-15 (develop @ 66adaf54)_
|